Ví dụ về tiện ích mở rộng Swift


82

Tôi ban đầu muốn biết cách làm một cái gì đó như thế này

UIColor.myCustomGreen

để tôi có thể xác định màu sắc của riêng mình và sử dụng chúng trong toàn bộ ứng dụng của mình.

Tôi đã nghiên cứu tiện ích mở rộng trước đây và tôi nghĩ rằng tôi có thể sử dụng chúng để giải quyết vấn đề của mình, nhưng tôi không thể nhớ chính xác cách thiết lập tiện ích mở rộng. Tìm kiếm trên Google tại thời điểm viết bài này cho "tiện ích mở rộng Swift" đã dẫn đến tài liệu , một số hướng dẫn dài và một câu hỏi về Stack Overflow khá vô ích .

Vì vậy, câu trả lời đã có, nhưng cần phải tìm hiểu kỹ các tài liệu và hướng dẫn. Tôi quyết định viết câu hỏi này và câu trả lời sau để thêm một số từ khóa tìm kiếm tốt hơn vào Stack Overflow và cung cấp thông tin cập nhật nhanh về cách thiết lập tiện ích mở rộng.

Cụ thể tôi muốn biết:

  • Các phần mở rộng nằm ở đâu (tập tin và quy ước đặt tên)?
  • Cú pháp mở rộng là gì?
  • Một vài ví dụ sử dụng phổ biến đơn giản là gì?

Câu trả lời:


172

Tạo tiện ích mở rộng

Thêm một tệp nhanh mới với Tệp> Mới> Tệp ...> iOS> Nguồn> Tệp Swift . Bạn có thể gọi nó là gì bạn muốn.

Quy ước đặt tên chung là gọi nó là TypeName + NewF Chức năng.swift .

nhập mô tả hình ảnh ở đây

Ví dụ 1 - Double

Double + Conversions.swift

import Swift // or Foundation

extension Double {

    func celsiusToFahrenheit() -> Double {
        return self * 9 / 5 + 32
    }

    func fahrenheitToCelsius() -> Double {
        return (self - 32) * 5 / 9
    }
}

Sử dụng:

let boilingPointCelsius = 100.0
let boilingPointFarenheit = boilingPointCelsius.celsiusToFahrenheit()
print(boilingPointFarenheit) // 212.0

Ví dụ 2 - String

String + Shortcuts.swift

import Swift // or Foundation

extension String {

    func replace(target: String, withString: String) -> String {
        return self.replacingOccurrences(of: target, with: withString)
    }
}

Sử dụng:

let newString = "the old bike".replace(target: "old", withString: "new")
print(newString) // "the new bike"

Dưới đây là một số Stringtiện ích mở rộng phổ biến hơn .

Ví dụ 3 - UIColor

UIColor + CustomColor.swift

import UIKit

extension UIColor {

    class var customGreen: UIColor {
        let darkGreen = 0x008110
        return UIColor.rgb(fromHex: darkGreen)
    }

    class func rgb(fromHex: Int) -> UIColor {

        let red =   CGFloat((fromHex & 0xFF0000) >> 16) / 0xFF
        let green = CGFloat((fromHex & 0x00FF00) >> 8) / 0xFF
        let blue =  CGFloat(fromHex & 0x0000FF) / 0xFF
        let alpha = CGFloat(1.0)

        return UIColor(red: red, green: green, blue: blue, alpha: alpha)
    }
}

Xem thêm tại đây .

Sử dụng:

view.backgroundColor = UIColor.customGreen

nhập mô tả hình ảnh ở đây

Ghi chú

  • Sau khi bạn xác định một tiện ích mở rộng, nó có thể được sử dụng ở bất kỳ đâu trong ứng dụng của bạn giống như các hàm lớp được tích hợp sẵn.
  • Nếu bạn không chắc chắn về chính xác cú pháp của hàm hoặc thuộc tính sẽ trông như thế nào, bạn có thể Option+ nhấp vào một phương thức tích hợp sẵn tương tự. Ví dụ, khi tôi Option+ nhấp vào, UIColor.greenColortôi thấy khai báo là class func greenColor() -> UIColor. Điều đó cho tôi một manh mối tốt về cách thiết lập phương thức tùy chỉnh của mình.
  • Tài liệu Apple dành cho Tiện ích mở rộng
  • Trong Objective-C, phần mở rộng được gọi là danh mục.

2
tại sao UIColor sử dụng classđể định nghĩa chức năng nhưng không sử dụng String?
JZAU

5
@jacky, từ khóa 'class' trước hàm làm cho nó trở thành một Phương thức Kiểu tĩnh thay vì Phương thức Phiên bản. Bằng cách này, bạn không phải cài đặt UIColor để có được màu tùy chỉnh. Xem câu trả lời này để biết thêm chi tiết: stackoverflow.com/a/31630431/3681880
Suragch

điều này sẽ đề cập đến tôi hôm nay, nhưng làm thế nào để bạn thực hiện các phần mở rộng độc đáo, tức là động vật lớp, con bò mở rộng, con mèo mở rộng, con chó mở rộng?
Lorne K

2
@LorneK, Tôi có vẻ như bạn đang nói về phân lớp . Tiện ích mở rộng chỉ thêm chức năng hoặc phương thức bổ sung vào một loại lớp hiện có. Xem bài viết này cũng để so sánh.
Suragch

Cần lưu ý rằng xcode có thể không nhận ngay các phương thức mở rộng và coi các cuộc gọi của bạn là chưa được giải quyết. Việc kích hoạt một bản dựng sẽ tự động thực hiện điều đó! Khó khăn.
Akash Agarwal

9

Hãy thử một số phương pháp mở rộng mới này:

UIColor

extension UIColor{
 //get new color from rgb value
  class func RGB(_ red:CGFloat , andGreenColor green:CGFloat, andBlueColor blue:CGFloat, withAlpha alpha:CGFloat) -> UIColor
  {
    let color = UIColor(red: red/255.0, green: green/255.0, blue: blue/255.0, alpha: alpha)
    return color
  }
}

 //return color from comma separated string of RGB paramater
  convenience init(rgbString :String, alpha:CGFloat = 1.0){
    let arrColor = rgbString.components(separatedBy: ",")
    let red:CGFloat = CGFloat(NumberFormatter().number(from: arrColor[0])!)
    let green:CGFloat = CGFloat(NumberFormatter().number(from: arrColor[1])!)
    let blue:CGFloat = CGFloat(NumberFormatter().number(from: arrColor[2])!)
    self.init(red: red/255.0, green: green/255.0, blue: blue/255.0, alpha: alpha)
  }

  //return color from hexadecimal value
  //let color2 = UIColor(rgbHexaValue: 0xFFFFFFFF)
  convenience init(rgbHexaValue: Int, alpha: CGFloat = 1.0) {
    self.init(red:  CGFloat((rgbHexaValue >> 16) & 0xFF), green: CGFloat((rgbHexaValue >> 8) & 0xFF), blue: CGFloat(rgbHexaValue & 0xFF), alpha: alpha)
  }
}

UITextField

extension UITextField{

//set cornerRadius
  func cornerRadius(){
    self.layoutIfNeeded()
    self.layer.cornerRadius = self.frame.height / 2
    self.clipsToBounds = true
  }

  //set bordercolor
  func borderColor(){
      self.layer.borderColor = TEXTFIELD_BORDER_COLOR.cgColor
      self.layer.borderWidth = 1.0
  }

  //set borderWidth
  func borderWidth(size:CGFloat){
    self.layer.borderWidth = size
  }

  //check textfield is blank
  func blank() -> Bool{
    let strTrimmed = self.text!.trim()//get trimmed string
    if(strTrimmed.characters.count == 0)//check textfield is nil or not ,if nil then return false
    {
      return true
    }
    return false
  }

  //set begginning space - left space
  func setLeftPadding(paddingValue:CGFloat) {
    let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: paddingValue, height: self.frame.size.height))
    self.leftViewMode = .always
    self.leftView = paddingView
  }

  //set end of space
  func setRightPadding(paddingValue:CGFloat){
    let paddingView = UIView(frame: CGRect(x: (self.frame.size.width - paddingValue), y: 0, width: paddingValue, height: self.frame.size.height))
    self.rightViewMode = .always
    self.rightView = paddingView
  }
}

UIFont

extension UIFont{
 // Returns a scaled version of UIFont
  func scaled(scaleFactor: CGFloat) -> UIFont {
    let newDescriptor = fontDescriptor.withSize(fontDescriptor.pointSize * scaleFactor)
    return UIFont(descriptor: newDescriptor, size: 0)
  }
}

UIImage

public enum ImageFormat {
  case PNG
  case JPEG(CGFloat)
}


extension UIImage {
  //convert image to base64 string
  func toBase64() -> String {
    var imageData: NSData
    switch format {
    case .PNG: imageData = UIImagePNGRepresentation(self)! as NSData
    case .JPEG(let compression): imageData = UIImageJPEGRepresentation(self, compression)! as NSData
    }
    return imageData.base64EncodedString(options: .lineLength64Characters)
  }

  //convert string to image
  class func base64ToImage(toImage strEncodeData: String) -> UIImage {
    let dataDecoded  = NSData(base64Encoded: strEncodeData, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters)!
    let image = UIImage(data: dataDecoded as Data)
    return image!
  }

  //Function for store file/Image into local directory. If image is already on the directory then first remove it and replace new image/File on that location
  func storedFileIntoLocal(strImageName:String) -> String{
    var strPath = ""
    let documentDirectory1 = NSString.init(string: String.documentDirectory())
    let imageName:String = strImageName + ".png"
    let imagePath = documentDirectory1.appendingPathComponent(imageName)
    strPath = imagePath
    let fileManager = FileManager.default
    let isExist = fileManager.fileExists(atPath: String.init(imagePath))
    if(isExist == true)
    {
      do {
        try fileManager.removeItem(atPath: imagePath as String)//removing file if exist
        // print("Remove success")
      } catch {
        print(error)
      }
    }
    let imageData:Data = UIImageJPEGRepresentation(self, 0.5)!
    do {
      try imageData.write(to: URL(fileURLWithPath: imagePath as String), options: .atomic)
    } catch {
      print(error)
      strPath = "Failed to cache image data to disk"
      return strPath
    }

    return strPath
  }


  //function for resize image
  func resizeImage(targetSize: CGSize) -> UIImage {
    let size = self.size

    let widthRatio  = targetSize.width  / self.size.width
    let heightRatio = targetSize.height / self.size.height

    // Figure out what our orientation is, and use that to form the rectangle
    var newSize: CGSize
    if(widthRatio > heightRatio) {
      newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
    } else {
      //                        newSize = size
      newSize = CGSize(width: size.width * widthRatio,  height: size.height * widthRatio)
    }

    // This is the rect that we've calculated out and this is what is actually used below
    let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)

    // Actually do the resizing to the rect using the ImageContext stuff
    UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
    self.draw(in: rect)
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage!
  }
}

Ngày

let YYYY_MM_DD_HH_MM_SS_zzzz = "yyyy-MM-dd HH:mm:ss +zzzz"
let YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"
let DD_MM_YYYY = "dd-MM-yyyy"
let MM_DD_YYYY = "MM-dd-yyyy"
let YYYY_DD_MM = "yyyy-dd-MM"
let YYYY_MM_DD_T_HH_MM_SS = "yyyy-MM-dd'T'HH:mm:ss"

extension Date{

  //convert string to date
  static func convertStringToDate(strDate:String, dateFormate strFormate:String) -> Date{
    let dateFormate = DateFormatter()
    dateFormate.dateFormat = strFormate
    dateFormate.timeZone = TimeZone.init(abbreviation: "UTC")
    let dateResult:Date = dateFormate.date(from: strDate)!

    return dateResult
  }

  //Function for old date format to new format from UTC to local
  static func convertDateUTCToLocal(strDate:String, oldFormate strOldFormate:String, newFormate strNewFormate:String) -> String{
    let dateFormatterUTC:DateFormatter = DateFormatter()
    dateFormatterUTC.timeZone = NSTimeZone(abbreviation: "UTC") as TimeZone!//set UTC timeZone
    dateFormatterUTC.dateFormat = strOldFormate //set old Format
    if let oldDate:Date = dateFormatterUTC.date(from: strDate)  as Date?//convert date from input string
    {
      dateFormatterUTC.timeZone = NSTimeZone.local//set localtimeZone
      dateFormatterUTC.dateFormat = strNewFormate //make new dateformatter for output format
      if let strNewDate:String = dateFormatterUTC.string(from: oldDate as Date) as String?//convert dateInUTC into string and set into output
      {
        return strNewDate
      }
      return strDate
    }
    return strDate
  }

  //Convert without UTC to local
  static func convertDateToLocal(strDate:String, oldFormate strOldFormate:String, newFormate strNewFormate:String) -> String{
    let dateFormatterUTC:DateFormatter = DateFormatter()
    //set local timeZone
    dateFormatterUTC.dateFormat = strOldFormate //set old Format
    if let oldDate:Date = dateFormatterUTC.date(from: strDate) as Date?//convert date from input string
    {
      dateFormatterUTC.timeZone = NSTimeZone.local
      dateFormatterUTC.dateFormat = strNewFormate //make new dateformatter for output format
      if let strNewDate = dateFormatterUTC.string(from: oldDate as Date) as String?//convert dateInUTC into string and set into output
      {
        return strNewDate
      }
      return strDate
    }
    return strDate
  }

  //Convert Date to String
  func convertDateToString(strDateFormate:String) -> String{
      let dateFormatter = DateFormatter()
      dateFormatter.dateFormat = strDateFormate
      let strDate = dateFormatter.string(from: self)
//      dateFormatter = nil
      return strDate
  }


  //Convert local to utc
  static func convertLocalToUTC(strDate:String, oldFormate strOldFormate:String, newFormate strNewFormate:String) -> String{
    let dateFormatterUTC:DateFormatter = DateFormatter()
    dateFormatterUTC.timeZone = NSTimeZone.local as TimeZone!//set UTC timeZone
    dateFormatterUTC.dateFormat = strOldFormate //set old Format
    if let oldDate:Date = dateFormatterUTC.date(from: strDate)  as Date?//convert date from input string
    {
      dateFormatterUTC.timeZone = NSTimeZone.init(abbreviation: "UTC")! as TimeZone//set localtimeZone
      dateFormatterUTC.dateFormat = strNewFormate //make new dateformatter for output format
      if let strNewDate:String = dateFormatterUTC.string(from: oldDate as Date) as String?//convert dateInUTC into string and set into output
      {
        return strNewDate
      }
      return strDate
    }
    return strDate
  }

  //Comparison two date
  static func compare(date:Date, compareDate:Date) -> String{
    var strDateMessage:String = ""
    let result:ComparisonResult = date.compare(compareDate)
    switch result {
    case .orderedAscending:
      strDateMessage = "Future Date"
      break
    case .orderedDescending:
      strDateMessage = "Past Date"
      break
    case .orderedSame:
      strDateMessage = "Same Date"
      break
    default:
      strDateMessage = "Error Date"
      break
    }
    return strDateMessage
  }
}

Gọi các chức năng này:

let color1 = UIColor.RGB(100.0, andGreenColor: 200.0, andBlueColor: 300.0, withAlpha: 1.0)
let color2 = UIColor.init(rgbHexaValue: 800000, alpha: 1.0)
let color3 = UIColor.init(rgbString: ("100.0,200.0,300.0", alpha: 1.0)

self.txtOutlet.cornerRadius()
self.txtOutlet.borderColor()
self.txtOutlet.setLeftPadding(paddingValue: 20.0)
self.txtOutlet.setRightPadding(paddingValue: 20.0)

let yourScaledFont = self.dependentView.font.scaled(scaleFactor: n as! CGFloat)
let base64String = (image?.toBase64(format: ImageFormat.PNG))!
let resultImage = UIImage.base64ToImage(toImage: base64String)
let path = yourImage.storedFileIntoLocal(strImageName: "imagename")

6

Ví dụ về Swift 3.0:

extension UITextField 
{    

    func useUnderline() {
        let border = CALayer()
        let borderWidth = CGFloat(1.0)
        border.borderColor = UIColor.lightGray.cgColor
        border.frame = CGRect(origin: CGPoint(x: 0,y :self.frame.size.height - borderWidth), size: CGSize(width: self.frame.size.width, height: self.frame.size.height))
        border.borderWidth = borderWidth
        self.layer.addSublayer(border)
        self.layer.masksToBounds = true
    }
}

Trong trường hợp của bạn, tôi muốn tạo một lớp mới kế thừa từ UITextField thay vì mở rộng UITextField ban đầu. Nó mang lại sự linh hoạt hơn. Điều gì sẽ xảy ra nếu tôi muốn sử dụng các kiểu khác nhau cho các trường văn bản của mình trong cùng một ứng dụng? Các tiện ích mở rộng được thêm vào lớp gốc trên toàn cầu.
Michal Cichon

4

Gạch chân văn bản trong UITextField

Được sử dụng trong chức năng ViewDidLoad()

firstNametext.underlined(0.5)

Sự mở rộng

extension UITextField {

    func underlined(_ size:Double){
        let border = CALayer()
        let width = CGFloat(size)
        border.borderColor = UIColor.red.cgColor
        border.frame = CGRect(x: 0, y: self.frame.size.height - width, 
        width:  self.frame.size.width, height: self.frame.size.height)
        border.borderWidth = width
        self.layer.addSublayer(border)
        self.layer.masksToBounds = true }
    }
}

Chào! Chào mừng bạn đến với stackoverflow! Các câu trả lời hay về stackoverflow, thường sẽ có một số loại giải thích cùng với chúng. Chỉ là một cái gì đó để nghĩ đến lần sau khi bạn trả lời một câu hỏi!
Qwerty

@Qwerty, nó có giải thích, nhưng nó được định dạng giống như mã. Tôi đã định dạng lại nó.
Suragch

3

UIColor + use.swift

import UIKit


extension UIColor{


    class func getCustomBlueColor() -> UIColor
    {
        return UIColor(red:0.043, green:0.576 ,blue:0.588 , alpha:1.00)
    }

    func getNameofColour() ->String
    {
        return "myOrange"
    }

}

Cách sử dụng :

NSLog("\(UIColor.getCustomBlueColor())")
let color=UIColor(red:0.043, green:0.576 ,blue:0.588 , alpha:1.00);
NSLog(color.getNameofColour())

Tôi hy vọng bạn thấy rằng sự khác biệt là gì. Một trong các Hàm bắt đầu bằng lớp func, một hàm khác chỉ bắt đầu bằng func . bạn có thể sử dụng mà bạn thích.


nó báo lỗi khi tôi nhập uikit, tôi có đang làm gì sai không?
Nabeel Khan

0

Một trong những ví dụ tốt nhất về trình khởi tạo tiện ích và mở rộng:

 extension UIActivityIndicatorView {
    convenience init(activityIndicatorStyle: UIActivityIndicatorViewStyle, color: UIColor, placeInTheCenterOf parentView: UIView) {
    self.init(activityIndicatorStyle: activityIndicatorStyle)
    center = parentView.center
    self.color = color
    parentView.addSubview(self)
  }
}

Bạn có thể sử dụng nó theo những cách sau:

  1. Khởi tạo activityIndicator

    let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge, color: .gray,  placeInTheCenterOf: view)
    
  2. Bắt đầu tạo hoạt ảnh chỉ báo hoạt động

    activityIndicator.startAnimating()
    
  3. Dừng chỉ báo hoạt động hoạt ảnh

    activityIndicator.stopAnimating()
    

0

Nếu bạn thích sử dụng một màu với một sắc thái nhất định như được sử dụng trong sách hướng dẫn sử dụng thương hiệu: Swift 4.2 + xcode 9.4.1.

extension UIColor {
    func withTint(tint: CGFloat)->UIColor {

        var tint = max(tint, 0)
        tint = min(tint, 1)
        /* Collect values of sender */
        var r : CGFloat = 0
        var g : CGFloat = 0
        var b : CGFloat = 0
        var a : CGFloat = 0
        self.getRed(&r, green: &g, blue: &b, alpha: &a)

        /* Calculate the tint */
        r = r+(1-r)*(1-tint)
        g = g+(1-g)*(1-tint)
        b = b+(1-b)*(1-tint)
        a = 1

        return UIColor.init(red: r, green: g, blue: b, alpha: a)
    }
}

Trong mã của bạn

let redWithTint = UIColor.red.withTint(tint: 0.4)

0

Đây là một ví dụ mở rộng về hiệu ứng hoạt ảnh bắt mắt hoạt động với các ô từ UITableView. Mỗi ô phát triển từ một nguồn điểm thành kích thước bình thường khi bạn cuộn UITableView. Điều chỉnh thời gian hoạt ảnh như mong muốn.

Vì mỗi ô hiển thị với một chút thời gian chùng xuống trong khi cuộn, nên hiệu ứng gợn sóng rất độc đáo! Xem đoạn clip dài 15 giây giới thiệu hiệu ứng này: https://www.youtube.com/watch?v=BVeQpno56wU&feature=youtu.be


extension UITableViewCell {

    func growCellDuringPresentation(thisCell : UITableViewCell) {

        thisCell.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)

        UIView.animate(withDuration: TimeInterval(0.35), delay: 0.0, options: UIView.AnimationOptions.allowUserInteraction,   animations: {

            thisCell.transform = CGAffineTransform(scaleX: 1, y: 1)

        }, completion: nil)

    }
}

Để sử dụng tiện ích mở rộng, bạn thực hiện cuộc gọi đến nó ngay trước khi ô được trả về trong cellForRowAt, như hình dưới đây:


            cell.growCellDuringPresentation(thisCell: cell)
            return cell

Lưu ý rằng phương pháp này cũng hoạt động khi trả về các ô cho dạng xem bộ sưu tập.

Đây là một tiện ích mở rộng hoạt động hoàn toàn giống nhau, ngoại trừ việc nó xoay các ô trong khi trình bày:


extension UITableViewCell {

    func rotateCellDuringPresentation(thisCell : UITableViewCell) {

        thisCell.transform = CGAffineTransform(rotationAngle: .pi)

        UIView.animate(withDuration: TimeInterval(0.35), delay: 0.0, options: UIView.AnimationOptions.allowUserInteraction,   animations: {

            thisCell.transform = CGAffineTransform(rotationAngle: 0)

        }, completion: nil)

    }
}

Nó được gọi tương tự:


            cell.rotateCellDuringPresentation(thisCell: cell)
            return cell

Đây là một phần mở rộng dọc theo các dòng giống nhau để dịch các ô theo hướng X.


extension UITableViewCell {

    func translateCellDuringPresentation(thisCell : UITableViewCell) {

        thisCell.layer.transform = CATransform3DMakeTranslation(-300, 0, 0)

        UIView.animate(withDuration: TimeInterval(0.5), delay: 0.0, options: UIView.AnimationOptions.allowUserInteraction,   animations: {

            thisCell.layer.transform = CATransform3DMakeTranslation(0, 0, 0)

        }, completion: nil)

    }
}

Nó được gọi tương tự:


            cell.translateCellDuringPresentation(thisCell: cell)
            return cell
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.