Take notes on your iOS learning process

IOS Development – Fragmentary notes

Create a project directory structure

  1. Appdelegate.swift: Definition of the life cycle and variables
  2. ViewController. Swift: MVC C
  3. Assets. Xcasset: Store resource files, such as pictures
  4. Info. plist: configuration file
  5. XxxTest: unit test
  6. Products: generated file
  7. Main. Storyboard: view

Add comments quickly

option + command + /
Copy the code

Close the soft keyboard

Close the code

textField.resignFirstResponder()
Copy the code

Closing method 1: Override the touchesEnded() method in Controller, and then close the software disk inside it, meaning close by clicking on the blank

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    name.resignFirstResponder()
}
Copy the code

Close method 2: Close the soft keyboard when you click Next. Controller implements UITextFieldDelegate protocol; Implement textFieldShouldReturn method in UITextFieldDelegate protocol;

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    textField.resignFirstResponder()
    return true
}
Copy the code

UIDatePicker calculates the age after selecting the time

func calAge(by datePicker: UIDatePicker) -> Int? {
    let gregorian = NSCalendar(calendarIdentifier: .gregorian)
    let now = Date(a)letcomponents = gregorian? .components(NSCalendar.Unit.year, from: datePicker.date, to: now, options: NSCalendar.Options.init(rawValue: 0))
    returncomponents? .year }Copy the code

Page jump, data transfer

There are two controllers: ViewController and GalleryViewController. Jump from ViewController to GalleryViewController. The ViewController overwrite method prepare, which is called when the page jumps and we need to determine which page to jump to.

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // You need to name the Segue
        if segue.identifier == "GoToGallery" {
            let index = beautyPicker.selectedRow(inComponent: 0)
            var imageName: String?
            switch index {
            case 0:
                imageName = "fangbingbing"
            case 1:
                imageName = "libingbing"
            case 2:
                imageName = "wangfei"
            case 3:
                imageName = "yangmi"
            case 4:
                imageName = "zhouxu"
            default:
                imageName = nil
            }

            // Get the Controller for the next page
            let vc = segue.destination as! GalleryViewController
            vc.imageName = imageName
        }
    }
Copy the code

Set the image by the image file name

 beautyImage.image = UIImage(named: imageName)
Copy the code

Unwind segue closes the page

After the page is closed, the Controller can get the value from the previous page that was written on the previous page

@IBAction func closedPrePage(segue: UIStoryboardSegue) {
    print("closed")}Copy the code

Move the TableView down by one status bar height

  1. Methods a
if #available(iOS 11.0, *) {
    tableView.contentInsetAdjustmentBehavior = .never
}
Copy the code
  1. In method two, the content is partially offset by the height of the status bar
collectionView? .contentInset.top = -UIApplication.shared.statusBarFrame.height
Copy the code

TableView add refresh

let refreshControl = UIRefreshControl(a)// Initialize the refresh
refreshControl.backgroundColor = UIColor.blue // Set the background color for the refresh
refreshControl.attributedTitle = NSAttributedString(string: "Refresh:\(Data())", attributes: [NSAttributedStringKey.foregroundColor: UIColor.white]) // Set the font color
refreshControl.tintColor = UIColor.green // Load the chrysanthemum color
refreshControl.tintAdjustmentMode = .dimmed // Color adjustment mode
refreshControl.addTarget(self, action: #selector(addcount), for: .valueChanged) // Add a method target

// Add the refresh
tableView.refreshControl = refreshControl
Copy the code

The refresh method

@objc func addcount(a) {
    dataArrary.append(contentsOf: dataArrary)
    tableView.reloadData()
    refreshControl.endRefreshing()
}
Copy the code

Add a font to the project

Developer.apple.com/documentati…

Because the font name used in iOS is not the file name, but the name of the font itself. The following code searches for all fonts, and then we find the extra names in the console.

for family: String in UIFont.familyNames
{
    print("\(family)")
    for names: String in UIFont.fontNames(forFamilyName: family)
    {
        print(= = ""\(names)")}}Copy the code

Set tabbar font and font size

override func viewDidLoad(a) {
    super.viewDidLoad()
    let appearance = UITabBarItem.appearance()
    appearance.setTitleTextAttributes([NSAttributedStringKey.font: UIFont(name: "Ubuntu-Light", size: 9)! ] .for: .normal)
}
Copy the code

UIButton related

UIButton.isEnabled = falseThe background image of the rear image button has been changed

UIButton.adjustsImageWhenDisabled = false
Copy the code

The extension adds rounded corners, borders, and border colors

@IBDesignable extension UIButton {

    @IBInspectable var borderWidth: CGFloat {
        set {
            layer.borderWidth = newValue
        }
        get {
            return layer.borderWidth
        }
    }

    @IBInspectable var cornerRadius: CGFloat {
        set {
            layer.cornerRadius = newValue
        }
        get {
            return layer.cornerRadius
        }
    }

    @IBInspectable var borderColor: UIColor? {
        set {
            guard let uiColor = newValue else { return }
            layer.borderColor = uiColor.cgColor
        }
        get {
            guard let color = layer.borderColor else { return nil }
            return UIColor(cgColor: color)
        }
    }
}
Copy the code

Expand picture on top, text on bottom

extension UIButton {
    func alignVertical(spacing: CGFloat = 6.0, imageBottom: CGFloat = 0.0) {
        guard let imageSize = self.imageView? .image? .size,let text = self.titleLabel? .text,let font = self.titleLabel? .fontelse { return }
        self.titleEdgeInsets = UIEdgeInsets(top: 0.0.left: -imageSize.width, bottom: -(imageSize.height + spacing), right: 0.0)
        let labelString = NSString(string: text)
        let titleSize = labelString.size(withAttributes: [NSAttributedStringKey.font: font])
        self.imageEdgeInsets = UIEdgeInsets(top: -(titleSize.height + spacing), left: 0.0, bottom: imageBottom, right: -titleSize.width)
        let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0;
        self.contentEdgeInsets = UIEdgeInsets(top: edgeOffset, left: 0.0, bottom: edgeOffset, right: 0.0)}}Copy the code

UITableView or UICollectionView is covered by TabBar

UITableView callreloadDataCauses moving to the top of the list to fail

UIView.animate(withDuration: 0, animations: {
    self.tableView.contentOffset = CGPoint.zero
}, completion: { _ in
    self.tableView.reloadData()
})
Copy the code

NavigationBar causes the CollectionViewCell or TableViewCell to be offset

self.collectionView? .contentInsetAdjustmentBehavior = .automaticCopy the code

Get the APP version

/// get the version name
let appVersion = Bundle.main.infoDictionary! ["CFBundleShortVersionString"] as? String

/// get the version number
let versionNumber = Bundle.main.infoDictionary! ["CFBundleVersion"] as? String
Copy the code

Clear the cache

 func clearCache(a) {

     // Retrieve the cache folder where the cache files are stored
     let cachePath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.cachesDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first

     // Fetch the array of all files under the folder
     let fileArr = FileManager.default.subpaths(atPath: cachePath!)

     // Iterate over the delete
     for file in fileArr! {

         letpath = cachePath? .appendingFormat("/\(file)")
         if FileManager.default.fileExists(atPath: path!) {

             do {
                 try FileManager.default.removeItem(atPath: path!)
             } catch{}}}}Copy the code

Open the Appstore of this software

// App Store URL.
let appStoreLink = "https://itunes.apple.com/cn/app/id1144351773?mt=8"

/* First create a URL, then check whether there is an installed app that can open it on the device. */
if let url = URL(string: appStoreLink), UIApplication.shared.canOpenURL(url) {
    // Attempt to open the URL.
    UIApplication.shared.open(url, options: [:], completionHandler: {(success: Bool) in
        if success {
            print("Launching \(url) was successful")}}}Copy the code

Set the circular display image

  1. Set up theUIImageViewWidth and height, if set to 60 x 60
  2. Set the runtime properties and set the arc to 30 (half the square edge length)
  3. Check theClip to Bounds.

UIScrollView fills the top (remove the status bar to the spacing)

  1. Content InsetschooseNever
  2. unselectedSafe Area Relative Margins

UIImage Gaussian blur extension

extension UIImage {
    func blurred(radius: CGFloat) -> UIImage {
        let ciContext = CIContext(options: nil)
        guard let cgImage = cgImage else { return self }
        let inputImage = CIImage(cgImage: cgImage)
        guard let ciFilter = CIFilter(name: "CIGaussianBlur") else { return self }
        ciFilter.setValue(inputImage, forKey: kCIInputImageKey)
        ciFilter.setValue(radius, forKey: "inputRadius")
        guard let resultImage = ciFilter.value(forKey: kCIOutputImageKey) as? CIImage else { return self }
        guard let cgImage2 = ciContext.createCGImage(resultImage, from: inputImage.extent) else { return self }
        return UIImage(cgImage: cgImage2)
    }
}
Copy the code

Two UIImage merge extensions

extension UIImage {

  func overlayWith(image: UIImage, posX: CGFloat, posY: CGFloat) -> UIImage {
    let newWidth = size.width < posX + image.size.width ? posX + image.size.width : size.width
    let newHeight = size.height < posY + image.size.height ? posY + image.size.height : size.height
    let newSize = CGSize(width: newWidth, height: newHeight)

    UIGraphicsBeginImageContextWithOptions(newSize, false.0.0)
    draw(in: CGRect(origin: CGPoint.zero, size: size))
    image.draw(in: CGRect(origin: CGPoint(x: posX, y: posY), size: image.size))
    let newImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext(a)return newImage
  }

}
Copy the code

SDWebImageView downloads images

  1. Methods a
img.sd_setImage(with: URL(string: "http://url"),
  placeholderImage: #imageLiteral(resourceName: "default_square")) { image, error, cacheType, url in

}
Copy the code
  1. Way 2
SDWebImageDownloader
  .shared()
  .downloadImage(with: URL(string: "http://url"),
    options: SDWebImageDownloaderOptions.init(rawValue: 0),
    progress: nil,
    completed: { image, data, error, finished in
    if finished {

    }
})
Copy the code

AVPlayerViewController video playback

import AVKit
func playVideoByUrl(string: String) {
    let videoURL = URL(string: string)
    let player = AVPlayer(url: videoURL!)
    let playerViewController = AVPlayerViewController()
    playerViewController.player = player
    self.present(playerViewController, animated: true) { playerViewController.player! .play() } }Copy the code

The click gesture added to UIImageView is invalid

  1. Check onUser Interaction Enabled
  2. Set in codeuiimageview.userInteractionEnabled = true

PHAsset Gets the file path

extension PHAsset {

    func getURL(completionHandler : @escaping ((_ responseURL : URL?) -> Void)) {if self.mediaType == .image {
            let options: PHContentEditingInputRequestOptions = PHContentEditingInputRequestOptions()
            options.canHandleAdjustmentData = {(adjustmeta: PHAdjustmentData) - >Bool in
                return true
            }
            self.requestContentEditingInput(with: options, completionHandler: {(contentEditingInput: PHContentEditingInput? , info: [AnyHashable : Any]) -> Void incompletionHandler(contentEditingInput! .fullSizeImageURLas URL?)
            })
        } else if self.mediaType == .video {
            let options: PHVideoRequestOptions = PHVideoRequestOptions()
            options.version = .original
            PHImageManager.default().requestAVAsset(forVideo: self, options: options, resultHandler: {(asset: AVAsset? , audioMix:AVAudioMix? , info: [AnyHashable : Any]? ->Void in
                if let urlAsset = asset as? AVURLAsset {
                    let localVideoUrl: URL = urlAsset.url as URL
                    completionHandler(localVideoUrl)
                } else {
                    completionHandler(nil)}})}}}Copy the code

UIView associated

Get the parent UIViewController from UIView

extension UIView {
    var parentViewController: UIViewController? {
        var parentResponder: UIResponder? = self
        whileparentResponder ! =nil{ parentResponder = parentResponder! .nextif let viewController = parentResponder as? UIViewController {
                return viewController
            }
        }
        return nil}}Copy the code

String related

HTML string, the code into the corresponding effect

extension String {
    var htmlToAttributedString: NSAttributedString? {
        guard let data = data(using: .utf8) else { return NSAttributedString()}do {
            return try NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding:String.Encoding.utf8.rawValue], documentAttributes: nil)}catch {
            return NSAttributedString()}}var htmlToString: String {
        returnhtmlToAttributedString? .string ??""}}Copy the code

Regular expression matching

/// The regular expression matches
extension String {
    func matchingStrings(regex: String)- > [String] {
        do {
            let regex = try NSRegularExpression(pattern: regex)
            let results = regex.matches(in: self,
                                        range: NSRange(self.startIndex... .in: self))
            return results.map {
                String(self[Range($0.range, in: self)!])
            }
        } catch let error {
            print("invalid regex: \(error.localizedDescription)")
            return[]}}}Copy the code

Data Splicing Data

extension Data {
    mutating func append(_ string: String, using encoding: String.Encoding = .utf8) {
        if let data = string.data(using: encoding) {
            append(data)
        }
    }
}
Copy the code

Scrambling an array

extension Array{
    mutating func randamArray(a) {
        var list = self
        for index in 0..<list.count {
            let newIndex = Int(arc4random_uniform(UInt32(list.count-index))) + index
            ifindex ! = newIndex { list.swapAt(index, newIndex) } }self = list
    }
}
Copy the code

UIImage related

Gaussian blur picture

extension UIImage {
    func blurred(radius: CGFloat) -> UIImage {
        let ciContext = CIContext(options: nil)
        guard let cgImage = cgImage else { return self }
        let inputImage = CIImage(cgImage: cgImage)
        guard let ciFilter = CIFilter(name: "CIGaussianBlur") else { return self }
        ciFilter.setValue(inputImage, forKey: kCIInputImageKey)
        ciFilter.setValue(radius, forKey: "inputRadius")
        guard let resultImage = ciFilter.value(forKey: kCIOutputImageKey) as? CIImage else { return self }
        guard let cgImage2 = ciContext.createCGImage(resultImage, from: inputImage.extent) else { return self }
        return UIImage(cgImage: cgImage2)
    }
}
Copy the code

Two pictures are superimposed to make one picture

extension UIImage {

    func overlayWith(image: UIImage, posX: CGFloat, posY: CGFloat) -> UIImage {
        let newWidth = size.width < posX + image.size.width ? posX + image.size.width : size.width
        let newHeight = size.height < posY + image.size.height ? posY + image.size.height : size.height
        let newSize = CGSize(width: newWidth, height: newHeight)

        UIGraphicsBeginImageContextWithOptions(newSize, false.0.0)
        draw(in: CGRect(origin: CGPoint.zero, size: size))
        image.draw(in: CGRect(origin: CGPoint(x: posX, y: posY), size: image.size))
        let newImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext(a)return newImage
    }

}
Copy the code

Zoom pictures

extension UIImage {

    func scaled(withSize size: CGSize) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(size, false.0.0)
        defer { UIGraphicsEndImageContext() }
        draw(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
        return UIGraphicsGetImageFromCurrentImageContext()! }}Copy the code

Json related

Json encoding

extension JSONEncoder {

    /// Convert the entity class to Json data
    func toJson<T: Encodable>(_ entity: T) -> String? {
        guard let encodedData = try? encode(entity) else {
            return nil
        }
        return String(data: encodedData, encoding: .utf8)
    }
}
Copy the code

Json decode

extension JSONDecoder {
    func from<T: Decodable>(_ type: T.Type, json: String) -> T? {
        do {
            return try decode(type, from: json.data(using: .utf8)!)
        }
        catch {
            return nil}}}Copy the code

The request field is encoded as a string, for example, key= Value&key = Value&key =value

extension Dictionary {
    func percentEscaped(a) -> String {
        return map { (key, value) in
            let escapedKey = "\(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
            let escapedValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
            return escapedKey + "=" + escapedValue
            }
            .joined(separator: "&")}}extension CharacterSet {
    static let urlQueryValueAllowed: CharacterSet = {
        let generalDelimitersToEncode = : # @ "[]" // does not include "?" or "/" due to RFC 3986 - Section 3.4
        let subDelimitersToEncode = ! "" * + $& '(),; ="

        var allowed = CharacterSet.urlQueryAllowed
        allowed.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
        return allowed
    }()
}
Copy the code

UIViewController related

Add a sub UIViewController

extension UIViewController {

    // add child ViewController
    func addSubController(child: UIViewController, to: UIView? = nil) {
        addChildViewController(child)
        if let to = to {
            child.view.frame = to.frame
            to.addSubview(child.view)
        }
        else {
            child.view.frame = view.frame
            view.addSubview(child.view)
        }
        child.didMove(toParentViewController: self)}}Copy the code

Remove the sub UIViewController

extension UIViewController {
    // remove the child ViewController
    func removeSubController(child: UIViewController) {
        child.willMove(toParentViewController: nil)
        child.removeFromParentViewController()
        child.view.removeFromSuperview()
    }
}
Copy the code

Close the page

Close the current page

extension UIViewController {
    // close the current page
    func closePage(a) {
        self.dismiss(animated: true, completion: nil)}}Copy the code

Close all pages except the one at the lowest level

extension UIViewController {
    func closeAllPage(a) {
        // Get root VC
        var rootVC = self.presentingViewController
        while letparent = rootVC? .presentingViewController { rootVC = parent }// Release all subordinate viewsrootVC? .dismiss(animated:true, completion: nil)}}Copy the code

Show and close chrysanthemums waiting to load

extension UIViewController {
    class func displaySpinner(onView : UIView) - >UIView {
        let spinnerView = UIView.init(frame: onView.bounds)
        spinnerView.backgroundColor = UIColor.init(red: 0.5, green: 0.5, blue: 0.5, alpha: 0.5)
        let ai = UIActivityIndicatorView.init(activityIndicatorStyle: .whiteLarge)
        ai.startAnimating()
        ai.center = spinnerView.center

        DispatchQueue.main.async {
            spinnerView.addSubview(ai)
            onView.addSubview(spinnerView)
        }

        return spinnerView
    }

    class func removeSpinner(spinner :UIView) {
        DispatchQueue.main.async {
            spinner.removeFromSuperview()
        }
    }
}
Copy the code

According to

let sp = UIViewController.displaySpinner(onView: self.view)
Copy the code

Shut down

UIViewController.removeSpinner(spinner: sp)
Copy the code

Within the IAP purchase

use

  1. The internal purchase preparation process except the code has been OK
  2. Obtaining product data: PassIAPHelper.shared.fetchAvailableProductsGets the product information from the Apple server for all product ids passed in as an array of product ID strings
  3. Payment:IAPHelper.shared.purchase(id: id)Id is the product ID
IAPHelper.shared.purchase(id: selectItem! .product_id) {alert, product, transactionin
  if alert == .purchased { // Successful purchase
      if let receiptUrl = Bundle.main.appStoreReceiptURL, let receiptData = NSData(contentsOf: receiptUrl) {
          let receiptString = receiptData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
          // Validate the receiptString encrypted string}}else if alert == .restored {

  }
  else if alert == .purchasing {

  }
  else{}}Copy the code

IAPHelper code

import StoreKit

enum IAPHelperAlertType{
    case disabled
    case restored
    case purchased
    case purchasing
    case setProductIds

    func message(a) -> String{
        switch self {
        case .setProductIds: return "No product ID set, please call fetchAvailableProducts()"
        case .disabled: return "Purchase cancelled"
        case .restored: return "You have successfully resumed purchase."
        case .purchased: return "You have successfully purchased this product."
        case .purchasing: return "Buying..."}}}class IAPHelper: NSObject {
    static let shared = IAPHelper(a)private override init() { }

    fileprivate var productID = ""
    fileprivate var productsRequest = SKProductsRequest()
    fileprivate var productDict = [String:SKProduct]()
    fileprivate var fetchProductCompletion: (([SKProduct] - >Void)?

    fileprivate var productToPurchase: SKProduct?
    var purchaseProductCompletion: ((IAPHelperAlertType.SKProduct? .SKPaymentTransaction?). ->Void)?

    // MARK: - Buy the product
    func canMakePurchases(a) -> Bool {  return SKPaymentQueue.canMakePayments()  }

    func purchase(id: String, completion: @escaping ((IAPHelperAlertType, SKProduct? , SKPaymentTransaction?)->Void)) {
        self.purchaseProductCompletion = completion
        self.productToPurchase = productDict[id]

        guard let product = self.productToPurchase else {
            print(IAPHelperAlertType.setProductIds.message())
            fatalError(IAPHelperAlertType.setProductIds.message())
        }

        if self.canMakePurchases() {
            let payment = SKPayment(product: product)
            SKPaymentQueue.default().add(self)
            SKPaymentQueue.default().add(payment)

            print("Buy Products:\(product.productIdentifier)")
            productID = product.productIdentifier
        }
        else {
            completion(.disabled, nil.nil)}}// MARK: - Resume purchases
    func restorePurchase(a){
        SKPaymentQueue.default().add(self)
        SKPaymentQueue.default().restoreCompletedTransactions()
    }

    // MARK: - Get available IAP products
    func fetchAvailableProducts(by ids: [String], completion: @escaping (([SKProduct])->Void)) {self.fetchProductCompletion = completion
        // Put your IAP product ID in here

        guard! ids.isEmptyelse {
            print("No product ID set")
            fatalError(IAPHelperAlertType.setProductIds.message())
        }

        productsRequest = SKProductsRequest(productIdentifiers: Set(ids))
        productsRequest.delegate = self
        productsRequest.start()
    }

}

extension IAPHelper: SKProductsRequestDelegate.SKPaymentTransactionObserver{
    // MARK: - Request IAP products
    func productsRequest (_ request:SKProductsRequest, didReceive response:SKProductsResponse) {

        if (response.products.count > 0) {
            for product in response.products {
                print("product.productIdentifier = \(product.productIdentifier)")
                self.productDict[product.productIdentifier] = product
            }
            self.fetchProductCompletion? (response.products) } }func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
        self.purchaseProductCompletion? (.restored,nil.nil)}// MARK:- IAP payment queue
    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        print("Called several times!!")
        for transaction:AnyObject in transactions {
            if let trans = transaction as? SKPaymentTransaction {
                switch trans.transactionState {
                case .purchased:
                    print("Product Purchased")
                    SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
                    self.purchaseProductCompletion? (.purchased,self.productToPurchase, trans)
                    break

                case .failed:
                    print("Product purchase failed\(trans.error.debugDescription)")
                    SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
                    self.purchaseProductCompletion? (.disabled,self.productToPurchase, trans)
                    break
                case .purchasing:
                    print("Buying...")
                    self.purchaseProductCompletion? (.purchasing,self.productToPurchase, trans)
                    break
                case .restored:
                    print("Product purchase has resumed")
                    SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
                    self.purchaseProductCompletion? (.restored,self.productToPurchase, trans)
                    break

                default: break
                }
            }
        }
    }
}
Copy the code