10 extensiones Swift que usamos en Livefront

Hola, Habr. La traducción se preparó como parte del curso en línea "Desarrollador iOS. Básico".



Invitamos a todos a un intensivo gratuito de dos días "Creación de una aplicación simple sin una sola línea de código". El primer día averiguaremos:





1. ¿Qué es Xcode?

2. Cómo "se dibujan las pantallas"

3. Agregue botones y campos de entrada a las pantallas. Creemos una pantalla de autorización.

4. Creemos una segunda pantalla de nuestra aplicación y agreguemos una transición desde la ventana de autorización.

Puedes registrarte aquí.






Agregue su propio sabor a Swift





Seamos honestos. Los frameworks Swift y Apple no tienen toda la funcionalidad necesaria para crear el mejor software para dispositivos Apple. Afortunadamente, Swift admite extensiones para que podamos agregar las piezas faltantes que necesitamos para que funcione mejor.





Si es nuevo en Swift, consulte la documentación para obtener más información sobre las extensiones antes de continuar.





, . , .





- , , . 





, , -.





Xcode Playground, , GitHub.





10 , Livefront.





1. UIView —

UIView

import PlaygroundSupport
import UIKit

// Extension #1 - A helper method to add a view to another with top, left, bottom, and right constraints.
extension UIView {

    /// Add a subview, constrained to the specified top, left, bottom and right margins.
    ///
    /// - Parameters:
    ///   - view: The subview to add.
    ///   - top: Optional top margin constant.
    ///   - left: Optional left (leading) margin constant.
    ///   - bottom: Optional bottom margin constant.
    ///   - right: Optional right (trailing) margin constant.
    ///
    func addConstrained(subview: UIView,
                        top: CGFloat? = 0,
                        left: CGFloat? = 0,
                        bottom: CGFloat? = 0,
                        right: CGFloat? = 0) {
        subview.translatesAutoresizingMaskIntoConstraints = false
        addSubview(subview)

        if let top = top {
            subview.topAnchor.constraint(equalTo: topAnchor, constant: top).isActive = true
        }
        if let left = left {
            subview.leadingAnchor.constraint(equalTo: leadingAnchor, constant: left).isActive = true
        }
        if let bottom = bottom {
            subview.bottomAnchor.constraint(equalTo: bottomAnchor, constant: bottom).isActive = true
        }
        if let right = right {
            subview.trailingAnchor.constraint(equalTo: trailingAnchor, constant: right).isActive = true
        }
    }
}

// Implementation
class ViewController: UIViewController {

    let newView = UIView()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemBlue

        newView.backgroundColor = .systemTeal
        view.addConstrained(subview: newView, top: 50, left: 100, right: -100)
    }
    
}

let viewController = ViewController()
PlaygroundPage.current.liveView = viewController
      
      



, translatesAutoresizingMaskIntoConstraints



false



, , - . , , . , , . , .





2. - (UTC Date)

Date UTC

import Foundation

// Extension #2 - Create a date object from a date string with the UTC timezone.
//Inspired by: https://developer.apple.com/library/archive/qa/qa1480/_index.html
extension Date {
    /// Returns a date from the provided string.
    ///
    /// - Parameter utcString: The string used to create the date.
    ///
    /// - Returns: A date from the provided string.
    ///
    static func utcDate(from utcString: String) -> Date? {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = TimeZone(abbreviation: "UTC")!
        return formatter.date(from: utcString)
    }
}

// Implementation
let utcDateString = "2021-04-03T14:00:00.000Z"
let utcDate = Date.utcDate(from: utcDateString) //Playgrounds will show this in the machine's timezone.
print(utcDate!)
      
      



API REST UTC. Date



. , , dateFormat



, .





3. String () — URL-

URL-

import Foundation

// Extension #3 - Retrieves valid URLs from a given string.
//Credit - Thanks to Paul Hudson for the core functionality on this extension.
//Source - https://www.hackingwithswift.com/example-code/strings/how-to-detect-a-url-in-a-string-using-nsdatadetector
extension String {
    /// Searches through a string to find valid URLs.
    /// - Returns: An array of found URLs.
    func getURLs() -> [URL] {
        var foundUrls = [URL]()
        guard let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) else {
            return foundUrls
        }

        let matches = detector.matches(
            in: self,
            options: [],
            range: NSRange(location: 0, length: self.utf16.count)
        )

        for match in matches {
            guard let range = Range(match.range, in: self),
                  let retrievedURL = URL(string: String(self[range])) else { continue }
            foundUrls.append(retrievedURL)
        }

        return foundUrls
    }
}

// Implementation
let unfilteredString = "To get the best search results, go to https://www.google.com, www.duckduckgo.com, or www.bing.com"
let urls = unfilteredString.getURLs()
      
      



- , URL . -, , URL- JSON-.





4. UIStackView —

subviews UIStackView

import UIKit

// Extension #4 - Removes all views from a UIStackView.
extension UIStackView {
    /// Removes all arranged subviews and their constraints from the view.
    func removeAllArrangedSubviews() {
        arrangedSubviews.forEach {
            self.removeArrangedSubview($0)
            NSLayoutConstraint.deactivate($0.constraints)
            $0.removeFromSuperview()
        }
    }
}

// Implementation
let view1 = UIView()
let view2 = UIView()
let view3 = UIView()

let stackView = UIStackView()

//Add subviews to stackView
stackView.addArrangedSubview(view1)
stackView.addArrangedSubview(view2)
stackView.addArrangedSubview(view3)

//Confirm stackView contains 3 views
stackView.arrangedSubviews.count    //3
//Remove views from stackView
stackView.removeAllArrangedSubviews()

//Confirm stackView doesn't contain any subviews now
stackView.arrangedSubviews.count    //0
      
      



, UIStackView



, , , . - .





5. Bundle —

import Foundation

// Extension #5 - retrieve the app version # and build #.
//Inspired by https://stackoverflow.com/questions/25965239/how-do-i-get-the-app-version-and-build-number-using-swift
extension Bundle {
    /// Retrieve the app version # from Bundle
    var releaseVersionNumber: String? {
        return infoDictionary?["CFBundleShortVersionString"] as? String
    }

    /// Retrieve the build version # from Bundle
    var buildVersionNumber: String? {
        return infoDictionary?["CFBundleVersion"] as? String
    }
}

// Implementation
let releaseVersionNumber = Bundle.main.releaseVersionNumber
let buildVersionNumber = Bundle.main.buildVersionNumber
      
      



, Bundle



. , , . .





6. —

Integer

import Foundation

// Extension #6 - Get the prior year as an integer
extension Calendar {
    /// Returns the prior year as an integer.
    ///
    /// - Returns: Returns last year's year as an integer.
    func priorYear() -> Int {
        guard let priorYear = date(byAdding: .year, value: -1, to: Date()) else {
            return component(.year, from: Date()) - 1
        }
        return component(.year, from: priorYear)
    }
}

//Implementation
let priorYearAsNumber = Calendar.current.priorYear()
      
      



. Integer.





7. UIStackView — Init

Init ()

import PlaygroundSupport
import UIKit

// Extension #7 - Make UIStackView creation a lot easier.
extension UIStackView {

    /// `UIStackView` convenience initializer for creating a stack view with arranged subviews, an
    /// axis and spacing.
    ///
    /// - Parameters:
    ///   - alignment: The alignment of the arranged subviews perpendicular to the stack view’s
    ///                axis.
    ///   - arrangedSubviews: The subviews to arrange in the `UIStackView`.
    ///   - axis: The axis that the subviews should be arranged around.
    ///   - distribution: The distribution of the arranged views along the stack view’s axis.
    ///   - spacing: The spacing to place between each arranged subview. Defaults to 0.
    ///
    convenience init(alignment: UIStackView.Alignment = .fill,
                     arrangedSubviews: [UIView],
                     axis: NSLayoutConstraint.Axis,
                     distribution: UIStackView.Distribution = .fill,
                     spacing: CGFloat = 0) {
        arrangedSubviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false }
        self.init(arrangedSubviews: arrangedSubviews)
        self.alignment = alignment
        self.axis = axis
        self.distribution = distribution
        self.spacing = spacing
    }
}

// Implementation
let view1 = UIView()
view1.backgroundColor = .systemPink
let view2 = UIView()
view2.backgroundColor = .systemOrange
let view3 = UIView()
view3.backgroundColor = .systemTeal

let stackView = UIStackView(alignment: .leading,
                            arrangedSubviews: [view1, view2, view3],
                            axis: .vertical,
                            distribution: .fill,
                            spacing: 20)



let view = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
view.backgroundColor = .systemBlue
view.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false

NSLayoutConstraint.activate([
    view1.heightAnchor.constraint(equalToConstant: 50),
    view1.widthAnchor.constraint(equalToConstant: 150),
    view2.heightAnchor.constraint(equalToConstant: 50),
    view2.widthAnchor.constraint(equalToConstant: 150),
    view3.heightAnchor.constraint(equalToConstant: 50),
    view3.widthAnchor.constraint(equalToConstant: 150),
    stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
    stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])

PlaygroundPage.current.liveView = view
      
      



, UIStackView



, . . translatesAutoresizingMaskIntoConstraints



false



.





8. UIColor — Hex

Hex () UIColor

import UIKit

// Extension #8 - generates a string with the hex color value.
//Inspired by: https://stackoverflow.com/a/26341062
extension UIColor {
    // MARK: - Helper Functions
    /// Returns the hex string for this `UIColor`. For example: `#FFFFFF` or `#222222AB` if the alpha value is included.
    ///
    /// - Parameter includeAlpha: A boolean indicating if the alpha value should be included in the returned hex string.
    ///
    /// - Returns: The hex string for this `UIColor`. For example: `#FFFFFF` or
    ///            `#222222AB` if the alpha value is included.
    ///
    func hexString(includeAlpha: Bool = false) -> String {
        let components = cgColor.components
        let red: CGFloat = components?[0] ?? 0.0
        let green: CGFloat = components?[1] ?? 0.0
        let blue: CGFloat = components?[2] ?? 0.0
        let alpha: CGFloat = components?[3] ?? 0.0
        let hexString = String.init(
            format: "#%02lX%02lX%02lX%02lX",
            lroundf(Float(red * 255)),
            lroundf(Float(green * 255)),
            lroundf(Float(blue * 255)),
            lroundf(Float(alpha * 255))
        )
        return includeAlpha ? hexString : String(hexString.dropLast(2))
    }
}

// Implementation
let whiteColor = UIColor(displayP3Red: 1, green: 1, blue: 1, alpha: 1)

let whiteHexString = whiteColor.hexString() //#FFFFFF
let blackColor = UIColor(displayP3Red: 0, green: 0, blue: 0, alpha: 1)

let blackHexString = blackColor.hexString() //#000000
      
      



UIColor



String



. , . , RGB.





9. UIViewController —

,

import UIKit

// Extension #9
extension UIViewController {
    /// Gets a flag indicating whether or not the UI is in dark mode.
    public var isDarkMode: Bool {
        if #available(iOS 12.0, *) {
            return traitCollection.userInterfaceStyle == .dark
        }
        return false
    }
}
      
      



UIColors



, .label



, .systemBlue



.., , , , . , , .





10. UICollectionView — IndexPath

indexPath collectionView

import PlaygroundSupport
import UIKit

// Extension #10 - get the last valid indexPath in a UICollectionView.
extension UICollectionView {

    /// Validates whether an `IndexPath` is a valid index path for an item in a collection view.
    ///
    /// - Parameter indexPath: The index path to validate.
    /// - Returns: `true` if the index path represents an item in the collection view or false
    ///     otherwise.
    ///
    func isValid(_ indexPath: IndexPath) -> Bool {
        guard indexPath.section < numberOfSections,
              indexPath.item < numberOfItems(inSection: indexPath.section)
        else {
            return false
        }

        return true
    }

    /// Provides the last valid `indexPath` in the collection view.
    /// - Parameter section: The section used to provide the last `indexPath`.
    /// - Returns: the last valid `indexPath` in the collection view or nil if not a valid `indexPath`.
    func lastIndexPath(in section: Int) -> IndexPath? {
        let lastIndexPath = IndexPath(row: numberOfItems(inSection: section) - 1, section: section)
        guard isValid(lastIndexPath) else { return nil }
        return lastIndexPath
    }
}

// Implementation
class CollectionViewController: UICollectionViewController {

    let items = Array(1...100)

    override func viewDidLoad() {
        super.viewDidLoad()

        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
    }

    override func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return items.count
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
        cell.backgroundColor = .systemBlue
        return cell
    }
}

let collectionViewController = CollectionViewController(collectionViewLayout: UICollectionViewFlowLayout())
let lastIndexPath = collectionViewController.collectionView.lastIndexPath(in: 0)
lastIndexPath?.section  //0
lastIndexPath?.row      //99
PlaygroundPage.current.liveView = collectionViewController
      
      



, UICollectionView



, indexPath



. , , , UIKit. collectionView



; .





, . Swift . " Swift" , -.





() .





:









Swift










"iOS Developer. Basic"





« »








All Articles