Swift:String+Extension(上)

简介: Swift:String+Extension

JKSwiftExtension,测试用例在 StringExtensionViewController 里面

目录:


1、字符串基本的扩展

2、沙盒路径的获取

3、iOS CharacterSet(字符集)

4、字符串的转换

5、字符串UI的处理

6、字符串有关数字方面的扩展

7、苹果针对浮点类型计算精度问题提供出来的计算类

8、字符串包含表情的处理

9、字符串的一些正则校验

10、字符串截取的操作

11、字符串编码的处理


一、字符串基本的扩展



// MARK:- 0:字符串基本的扩展
public extension String {
   // MARK: 0.1、字符串的长度
   /// 字符串的长度
   var length: Int {
       return self.count
   }
   // MARK: 0.2、判断是否包含某个子串
   /// 判断是否包含某个子串
   /// - Parameter find: 子串
   /// - Returns: Bool
   func contains(find: String) -> Bool {
       return self.range(of: find) != nil
   }
   // MARK: 0.3、判断是否包含某个子串 -- 忽略大小写
   ///  判断是否包含某个子串 -- 忽略大小写
   /// - Parameter find: 子串
   /// - Returns: Bool
   func containsIgnoringCase(find: String) -> Bool {
       return self.range(of: find, options: .caseInsensitive) != nil
   }
   // MARK: 0.4、字符串转 Base64
   /// 字符串转 Base64
   var base64: String? {
       guard let plainData = (self as NSString).data(using: String.Encoding.utf8.rawValue) else {
           return nil
       }
       let base64String = plainData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
       return base64String
   }
   // MARK: 0.5、将16进制字符串转为Int
   /// 将16进制字符串转为Int
   var hexInt: Int {
       return Int(self, radix: 16) ?? 0
   }
   // MARK: 0.6、判断是不是九宫格键盘
   /// 判断是不是九宫格键盘
   func isNineKeyBoard() -> Bool {
       let other : NSString = "➋➌➍➎➏➐➑➒"
       let len = self.count
       for _ in 0..<len {
           if !(other.range(of: self).location != NSNotFound) {
               return false
           }
       }
       return true
   }
   // MARK: 0.7、字符串转 UIViewController
   /// 字符串转 UIViewController
   /// - Returns: 对应的控制器
   @discardableResult
   func toViewController() -> UIViewController? {
       // 1.动态获取命名空间
       let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String
       // 2.将字符串转换为类
       // 2.1.默认情况下命名空间就是项目的名称,但是命名空间的名称是可以更改的
       guard let Class: AnyClass = NSClassFromString(namespace + "." + self) else {
           return nil
       }
       // 3.通过类创建对象
       // 3.1.将AnyClass 转化为指定的类
       let vcClass = Class as! UIViewController.Type
       // 4.通过class创建对象
       let vc = vcClass.init()
       return vc
   }
   // MARK: 0.8、字符串转 AnyClass
   /// 字符串转 AnyClass
   /// - Returns: 对应的 Class
   @discardableResult
   func toClass() -> AnyClass? {
       // 1.动态获取命名空间
       let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String
       // 2.将字符串转换为类
       // 2.1.默认情况下命名空间就是项目的名称,但是命名空间的名称是可以更改的
       guard let Class: AnyClass = NSClassFromString(namespace + "." + self) else {
           return nil
       }
       return Class
   }
}

二、沙盒路径的获取



// MARK:- 1、沙盒路径的获取
/*
  - 1、Home(应用程序包)目录
  - 整个应用程序各文档所在的目录,包含了所有的资源文件和可执行文件
  - 2、Documents
  - 保存应用运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录
  - 需要保存由"应用程序本身"产生的文件或者数据,例如: 游戏进度,涂鸦软件的绘图
  - 目录中的文件会被自动保存在 iCloud
  - 注意: 不要保存从网络上下载的文件,否则会无法上架!
  - 3、Library
  - 3.1、Library/Cache
  -  保存应用运行时生成的需要持久化的数据,iTunes同步设备时不备份该目录。一般存放体积大、不需要备份的非重要数据
  - 保存临时文件,"后续需要使用",例如: 缓存的图片,离线数据(地图数据)
  - 系统不会清理 cache 目录中的文件
  - 就要求程序开发时, "必须提供 cache 目录的清理解决方案"
  - 3.2、Library/Preference
  - 保存应用的所有偏好设置,IOS的Settings应用会在该目录中查找应用的设置信息。iTunes
  - 用户偏好,使用 NSUserDefault 直接读写!
  - 如果想要数据及时写入硬盘,还需要调用一个同步方法
  - 4、tmp
  - 保存临时文件,"后续不需要使用"
  - tmp 目录中的文件,系统会自动被清空
  - 重新启动手机, tmp 目录会被清空
  - 系统磁盘空间不足时,系统也会自动清理
  - 保存应用运行时所需要的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行,系统也可能会清除该目录下的文件,iTunes不会同步备份该目录
  */
public extension String {
    // MARK: 1.1、获取Home的完整路径名
    /// 获取Home的完整路径名
    /// - Returns: Home的完整路径名
    static func homeDirectory() -> String {
        //获取程序的Home目录
        let homeDirectory = NSHomeDirectory()
         return homeDirectory
    }
    // MARK: 1.2、获取Documnets的完整路径名
    /// 获取Documnets的完整路径名
    /// - Returns: Documnets的完整路径名
    static func DocumnetsDirectory() -> String {
        //获取程序的documentPaths目录
        //方法1
        // let documentPaths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
        // let documnetPath = documentPaths[0]
        //方法2
        let ducumentPath = NSHomeDirectory() + "/Documents"
        return ducumentPath
    }
    // MARK: 1.3、获取Library的完整路径名
    /**
     这个目录下有两个子目录:Caches 和 Preferences
 Library/Preferences目录,包含应用程序的偏好设置文件。不应该直接创建偏好设置文件,而是应该使用NSUserDefaults类来取得和设置应用程序的偏好。
     Library/Caches目录,主要存放缓存文件,iTunes不会备份此目录,此目录下文件不会再应用退出时删除
     */
    /// 获取Library的完整路径名
    /// - Returns: Library的完整路径名
    static func LibraryDirectory() -> String {
        //获取程序的documentPaths目录
        //Library目录-方法1
        // let libraryPaths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.libraryDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
        // let libraryPath = libraryPaths[0]
        //
        // Library目录-方法2
        let libraryPath = NSHomeDirectory() + "/Library"
        return libraryPath
    }
    // MARK: 1.4、获取/Library/Caches的完整路径名
    /// 获取/Library/Caches的完整路径名
    /// - Returns: /Library/Caches的完整路径名
    static func CachesDirectory() -> String {
        //获取程序的/Library/Caches目录
        let cachesPath = NSHomeDirectory() + "/Library/Caches"
        return cachesPath
    }
    // MARK: 1.5、获取Library/Preferences的完整路径名
    /// 获取Library/Preferences的完整路径名
    /// - Returns: Library/Preferences的完整路径名
    static func PreferencesDirectory() -> String {
        //Library/Preferences目录-方法2
        let preferencesPath = NSHomeDirectory() + "/Library/Preferences"
        return preferencesPath
    }
    // MARK: 1.6、获取Tmp的完整路径名
    /// 获取Tmp的完整路径名,用于存放临时文件,保存应用程序再次启动过程中不需要的信息,重启后清空。
    /// - Returns: Tmp的完整路径名
    static func TmpDirectory() -> String {
        //方法1
        //let tmpDir = NSTemporaryDirectory()
        //方法2
        let tmpDir = NSHomeDirectory() + "/tmp"
        return tmpDir
    }
}

三、iOS CharacterSet(字符集)



// MARK:- 二、iOS CharacterSet(字符集)
/**
 CharacterSet是在Foundation框架下的一个结构体,用于搜索操作的一组Unicode字符值。官方的API地址:https://developer.apple.com/documentation/foundation/characterset
 概述
 字符集表示一组符合unicode的字符。基础类型使用字符集将字符组合在一起进行搜索操作,以便在搜索期间可以找到任何特定的字符集。
 这种类型提供了“写时复制”的行为,并且还连接到Objective-C NSCharacterSet类。
 总之就是将unicode字符,按组分类,便于搜索查找,验证字符串。通常我们在一些场景下会用到一个字符串是否包含某种特定字符,比如判断密码是否只包含数字,检查url是否有不规范字符,删除多余空格等操作
 属性                                    描述
 CharacterSet.alphanumerics
 controlCharacters:                     控制符
 whitespaces:                           空格
 whitespacesAndNewlines:                空格和换行
 decimalDigits:                         0-9的数字,也不包含小数点
 letters:                               所有英文字母,包含大小写 65-90 97-122
 lowercaseLetters:                      小写英文字母 97-122
 uppercaseLetters:                      大写英文字母 65-90
 nonBaseCharacters:                     非基础字符 M*
 alphanumerics:                         字母和数字的组合,包含大小写, 不包含小数点
 decomposables:                         可分解
 illegalCharacters:                     不合规字符,没有在Unicode 3.2 标准中定义的字符
 punctuationCharacters:                 标点符号,连接线,引号什么的 P*
 capitalizedLetters:                    字母,首字母大写,Lt类别
 symbols:                               符号,包含S* 所有内容,运算符,货币符号什么的
 newlines:                              返回一个包含换行符的字符集,U+000A ~ U+000D, U+0085, U+2028, and U+2029
 urlUserAllowed:
 urlPasswordAllowed:
 urlHostAllowed:
 urlPathAllowed:
 urlQueryAllowed:
 urlFragmentAllowed:
 bitmapRepresentation:
 inverted:                               相反的字符集。例如 CharacterSet.whitespaces.inverted 就是没有空格
*/
public extension String {
    // MARK: 2.1、去除字符串前后的 空格
    /// 去除字符串前后的换行和换行
    var removeBeginEndAllSapcefeed: String {
        let resultString = self.trimmingCharacters(in: CharacterSet.whitespaces)
        return resultString
    }
    // MARK: 2.2、去除字符串前后的 换行
    /// 去除字符串前后的 换行
    var removeBeginEndAllLinefeed: String {
        let resultString = self.trimmingCharacters(in: CharacterSet.newlines)
        return resultString
    }
    // MARK: 2.3、去除字符串前后的 换行和空格
    /// 去除字符串前后的 换行和空格
    var removeBeginEndAllSapceAndLinefeed: String {
        var resultString = self.trimmingCharacters(in: CharacterSet.whitespaces)
        resultString = resultString.trimmingCharacters(in: CharacterSet.newlines)
        return resultString
    }
    // MARK: 2.4、去掉所有空格
    /// 去掉所有空格
    var removeAllSapce: String {
        return replacingOccurrences(of: " ", with: "", options: .literal, range: nil)
    }
    // MARK: 2.5、去掉所有换行
    /// 去掉所有换行
    var removeAllLinefeed: String {
        return replacingOccurrences(of: "\n", with: "", options: .literal, range: nil)
    }
    // MARK: 2.6、去掉所有空格 和 换行
    /// 去掉所有的空格 和 换行
    var removeAllLineAndSapcefeed: String {
        // 去除所有的空格
        var resultString = replacingOccurrences(of: " ", with: "", options: .literal, range: nil)
        // 去除所有的换行
        resultString = resultString.replacingOccurrences(of: "\n", with: "", options: .literal, range: nil)
        return resultString
    }
    // MARK: 2.7、是否是 0-9 的数字,也不包含小数点
    /// 是否是 0-9 的数字,也不包含小数点
    /// - Returns: 结果
    func isValidNumber() -> Bool {
        /// 0-9的数字,也不包含小数点
        let rst: String = self.trimmingCharacters(in: .decimalDigits)
        if rst.count > 0 {
            return false
        }
        return true
    }
    // MARK: 2.8、url进行编码
    /// url 进行编码
    /// - Returns: 返回对应的URL
    func urlValidate() -> URL {
        return URL(string: self.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlHostAllowed) ?? "")!
    }
    // MARK: 2.9、某个字符使用某个字符替换掉
    /// 某个字符使用某个字符替换掉
    /// - Parameters:
    ///   - removeString: 原始字符
    ///   - replacingString: 替换后的字符
    /// - Returns: 替换后的整体字符串
    func removeSomeStringUseSomeString(removeString: String, replacingString: String = "") -> String {
        return replacingOccurrences(of: removeString, with: replacingString)
    }
    // MARK: 2.10、使用正则表达式替换某些子串
    /// 使用正则表达式替换
    /// - Parameters:
    ///   - pattern: 正则
    ///   - with: 用来替换的字符串
    ///   - options: 策略
    /// - Returns: 返回替换后的字符串
    func pregReplace(pattern: String, with: String,
                 options: NSRegularExpression.Options = []) -> String {
        let regex = try! NSRegularExpression(pattern: pattern, options: options)
        return regex.stringByReplacingMatches(in: self, options: [],
                                          range: NSMakeRange(0, self.count),
                                          withTemplate: with)
    }
    // MARK: 2.11、删除指定的字符
    /// 删除指定的字符
    /// - Parameter characterString: 指定的字符
    /// - Returns: 返回删除后的字符
    func removeCharacter(characterString: String) -> String {
        let characterSet = CharacterSet(charactersIn: characterString)
        return trimmingCharacters(in: characterSet)
    }
}

四、字符串的转换



// MARK:- 三、字符串的转换
public extension String {
    // MARK: 3.1、字符串 转 CGFloat
    /// 字符串 转 Float
    /// - Returns: CGFloat
    func toCGFloat() -> CGFloat? {
        if let doubleValue = Double(self) {
            return CGFloat(doubleValue)
        }
        return nil
    }
    // MARK: 3.2、字符串转 bool
    /// 字符串转 bool
    var bool: Bool? {
        switch self.lowercased() {
        case "true", "t", "yes", "y", "1":
            return true
        case "false", "f", "no", "n", "0":
            return false
        default:
            return nil
        }
    }
    // MARK: 3.3、字符串转 Int
    /// 字符串转 Int
    /// - Returns: Int
    func toInt() -> Int? {
        if let num = NumberFormatter().number(from: self) {
            return num.intValue
        } else {
            return nil
        }
    }
    // MARK: 3.4、字符串转 Double
    /// 字符串转 Double
    /// - Returns: Double
    func toDouble() -> Double? {
        if let num = NumberFormatter().number(from: self) {
            return num.doubleValue
        } else {
            return nil
       }
    }
    // MARK: 3.5、字符串转 Float
    /// 字符串转 Float
    /// - Returns: Float
    func toFloat() -> Float? {
        if let num = NumberFormatter().number(from: self) {
            return num.floatValue
        } else {
            return nil
        }
    }
    // MARK: 3.6、字符串转 Bool
    /// 字符串转 Bool
    /// - Returns: Bool
    func toBool() -> Bool? {
        let trimmedString = lowercased()
        if trimmedString == "true" || trimmedString == "false" {
            return (trimmedString as NSString).boolValue
        }
        return nil
    }
    // MARK: 3.7、字符串转 NSString
    /// 字符串转 NSString
    var toNSString: NSString {
        return self as NSString
    }
}

五、字符串UI的处理



// MARK:- 四、字符串UI的处理
extension String {
    // MARK: 4.1、对字符串(多行)指定出字体大小和最大的 Size,获取 (Size)
    /// 对字符串(多行)指定出字体大小和最大的 Size,获取展示的 Size
    /// - Parameters:
    ///   - font: 字体大小
    ///   - size: 字符串的最大宽和高
    /// - Returns: 按照 font 和 Size 的字符的Size
    public func rectSize(font: UIFont, size: CGSize) -> CGSize {
        let attributes = [NSAttributedString.Key.font: font]
        /**
         usesLineFragmentOrigin: 整个文本将以每行组成的矩形为单位计算整个文本的尺寸
         usesFontLeading:
         usesDeviceMetrics:
         @available(iOS 6.0, *)
         truncatesLastVisibleLine:
         */
        let option = NSStringDrawingOptions.usesLineFragmentOrigin
        let rect: CGRect = self.boundingRect(with: size, options: option, attributes: attributes, context: nil)
        return rect.size
    }
    // MARK: 4.2、对字符串(多行)指定字体及Size,获取 (高度)
    /// 对字符串指定字体及Size,获取 (高度)
    /// - Parameters:
    ///   - font: 字体的大小
    ///   - size: 字体的size
    /// - Returns: 返回对应字符串的高度
    public func rectHeight(font: UIFont, size: CGSize) -> CGFloat {
        return rectSize(font: font, size: size).height
    }
    // MARK: 4.3、对字符串(多行)指定字体及Size,获取 (宽度)
    /// 对字符串指定字体及Size,获取 (宽度)
    /// - Parameters:
    ///   - font: 字体的大小
    ///   - size: 字体的size
    /// - Returns: 返回对应字符串的宽度
    public func rectWidth(font: UIFont, size: CGSize) -> CGFloat {
        return rectSize(font: font, size: size).width
    }
    // MARK: 4.4、对字符串(单行)指定字体,获取 (Size)
    /// 对字符串(单行)指定字体,获取 (Size)
    /// - Parameter font: 字体的大小
    /// - Returns: 返回单行字符串的 size
    public func singleLineSize(font: UIFont) -> CGSize {
        let attrs = [NSAttributedString.Key.font: font]
        return self.size(withAttributes: attrs as [NSAttributedString.Key: Any])
    }
    // MARK: 4.5、对字符串(单行)指定字体,获取 (width)
    /// 对字符串(单行)指定字体,获取 (width)
    /// - Parameter font: 字体的大小
    /// - Returns: 返回单行字符串的 width
    public func singleLineWidth(font: UIFont) -> CGFloat {
        let attrs = [NSAttributedString.Key.font: font]
        return self.size(withAttributes: attrs as [NSAttributedString.Key: Any]).width
    }
    // MARK: 4.6、对字符串(单行)指定字体,获取 (Height)
    /// 对字符串(单行)指定字体,获取 (height)
    /// - Parameter font: 字体的大小
    /// - Returns: 返回单行字符串的 height
    public func singleLineHeight(font: UIFont) -> CGFloat {
        let attrs = [NSAttributedString.Key.font: font]
        return self.size(withAttributes: attrs as [NSAttributedString.Key: Any]).height
    }
    // MARK: 4.7、字符串通过 label 根据高度&字体 —> Size
    /// 字符串通过 label 根据高度&字体 ——> Size
    /// - Parameters:
    ///   - height: 字符串最大的高度
    ///   - font: 字体大小
    /// - Returns: 返回Size
    public func sizeAccording(width: CGFloat, height: CGFloat = CGFloat(MAXFLOAT), font: UIFont) -> CGSize {
        if self.isBlank {return CGSize(width: 0, height: 0)}
        let rect = CGRect(x: 0, y: 0, width: width, height: height)
        let label = UILabel(frame: rect).font(font).text(self).line(0)
        return label.sizeThatFits(rect.size)
    }
    // MARK: 4.8、字符串通过 label 根据高度&字体 —> Width
    /// 字符串通过 label 根据高度&字体 ——> Width
    /// - Parameters:
    ///   - height: 字符串最大高度
    ///   - font: 字体大小
    /// - Returns: 返回宽度大小
    public func widthAccording(width: CGFloat, height: CGFloat = CGFloat(MAXFLOAT), font: UIFont) -> CGFloat {
         if self.isBlank {return 0}
        let rect = CGRect(x: 0, y: 0, width: width, height: height)
        let label = UILabel(frame: rect).font(font).text(self).line(0)
        return label.sizeThatFits(rect.size).width
    }
    // MARK: 4.9、字符串通过 label 根据宽度&字体 —> height
    /// 字符串通过 label 根据宽度&字体 ——> height
    /// - Parameters:
    ///   - width: 字符串最大宽度
    ///   - font: 字体大小
     /// - Returns: 返回高度大小
    public func heightAccording(width: CGFloat, height: CGFloat = CGFloat(MAXFLOAT), font: UIFont) -> CGFloat {
        if self.isBlank {return 0}
        let rect = CGRect(x: 0, y: 0, width: width, height: height)
         let label = UILabel(frame: rect).font(font).text(self).line(0)
        return label.sizeThatFits(rect.size).height
   }
    // MARK: 4.10、字符串根据宽度 & 字体 & 行间距 —> Size
    /// 字符串根据宽度 & 字体 & 行间距 ——> Size
    /// - Parameters:
    ///   - width: 字符串最大的宽度
    ///   - heiht: 字符串最大的高度
    ///   - font: 字体的大小
    ///   - lineSpacing: 行间距
    /// - Returns: 返回对应的size
    public func sizeAccording(width: CGFloat, height: CGFloat = CGFloat(MAXFLOAT), font: UIFont, lineSpacing: CGFloat) -> CGSize {
        if self.isBlank {return CGSize(width: 0, height: 0)}
        let rect = CGRect(x: 0, y: 0, width: width, height: CGFloat(MAXFLOAT))
        let label = UILabel(frame: rect).font(font).text(self).line(0)
        let attrStr = NSMutableAttributedString(string: self)
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.lineSpacing = lineSpacing
    attrStr.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, self.count))
        label.attributedText = attrStr
        return label.sizeThatFits(rect.size)
    }
    // MARK: 4.11、字符串根据宽度 & 字体 & 行间距 —> width
    /// 字符串根据宽度 & 字体 & 行间距 ——> width
    /// - Parameters:
    ///   - width: 字符串最大的宽度
    ///   - heiht: 字符串最大的高度
    ///   - font: 字体的大小
    ///   - lineSpacing: 行间距
    /// - Returns: 返回对应的 width
public func widthAccording(width: CGFloat, height: CGFloat = CGFloat(MAXFLOAT), font: UIFont, lineSpacing: CGFloat) -> CGFloat {
        if self.isBlank {return 0}
        let rect = CGRect(x: 0, y: 0, width: width, height: height)
        let label = UILabel(frame: rect).font(font).text(self).line(0)
        let attrStr = NSMutableAttributedString(string: self)
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.lineSpacing = lineSpacing
        attrStr.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, self.count))
        label.attributedText = attrStr
        return label.sizeThatFits(rect.size).width
    }
    // MARK: 4.12、字符串根据宽度 & 字体 & 行间距 —> height
    /// 字符串根据宽度 & 字体 & 行间距 ——> height
    /// - Parameters:
    ///   - width: 字符串最大的宽度
    ///   - heiht: 字符串最大的高度
    ///   - font: 字体的大小
    ///   - lineSpacing: 行间距
    /// - Returns: 返回对应的 height
    public func heightAccording(width: CGFloat, height: CGFloat = CGFloat(MAXFLOAT), font: UIFont, lineSpacing: CGFloat) -> CGFloat {
        if self.isBlank {return 0}
        let rect = CGRect(x: 0, y: 0, width: width, height: height)
        let label = UILabel(frame: rect).font(font).text(self).line(0)
        let attrStr = NSMutableAttributedString(string: self)
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.lineSpacing = lineSpacing
        attrStr.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, self.count))
        label.attributedText = attrStr
        return label.sizeThatFits(rect.size).height
    }
}


目录
相关文章
|
Swift
Swift - 用装有控制器name的数组for循环批量创建控制器(string转UIViewController)
Swift - 用装有控制器name的数组for循环批量创建控制器(string转UIViewController)
74 0
|
存储 前端开发 Swift
Swift实用小册19:Extension扩展的使用
在本章中,你将学会Extension扩展的使用方法。
313 0
Swift实用小册19:Extension扩展的使用
|
测试技术 Swift
Swift:FileManager+Extension
Swift:FileManager+Extension
508 0
|
测试技术 Swift
Swift:CALayer+Extension
Swift:CALayer+Extension
130 0
|
测试技术 Swift
Swift:UIImage+Extension
Swift:UIImage+Extension
628 0
|
测试技术 Swift
Swift:UITextView+Extension
Swift:UITextView+Extension
203 0
|
测试技术 Swift
Swift:UIScrollView+Extension
Swift:UIScrollView+Extension
138 0
|
测试技术 Swift
Swift:UITableView+Extension
Swift:UITableView+Extension
207 0
|
测试技术 Swift
Swift:UILabel+Extension
Swift:UILabel+Extension
377 0
|
测试技术 Swift
Swift:UIButton+Extension
Swift:UIButton+Extension
168 0

相关课程

更多