BFString+Ext.swift 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. //
  2. // String+Ext.swift
  3. // PQSpeed
  4. //
  5. // Created by SanW on 2020/7/22.
  6. // Copyright © 2020 BytesFlow. All rights reserved.
  7. //
  8. import CommonCrypto
  9. import Foundation
  10. import MobileCoreServices
  11. public extension String {
  12. /// md5加密
  13. var md5: String {
  14. let str = cString(using: String.Encoding.utf8)
  15. let strLen = CUnsignedInt(lengthOfBytes(using: String.Encoding.utf8))
  16. let digestLen = Int(CC_MD5_DIGEST_LENGTH)
  17. let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
  18. CC_MD5(str!, strLen, result)
  19. let hash = NSMutableString()
  20. for i in 0..<digestLen {
  21. hash.appendFormat("%02x", result[i])
  22. }
  23. result.deallocate()
  24. return hash as String
  25. }
  26. // 文件后缀名
  27. var pathExtension: String {
  28. return (self as NSString).pathExtension
  29. }
  30. func ga_widthForComment(font: UIFont, height: CGFloat = 15) -> CGFloat {
  31. let rect = NSString(string: self).boundingRect(with: CGSize(width: CGFloat(MAXFLOAT), height: height), options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
  32. return ceil(rect.width)
  33. }
  34. //
  35. // public func ga_heightForComment(fontSize: CGFloat, width: CGFloat) -> CGFloat {
  36. // let font = UIFont.systemFont(ofSize: fontSize)
  37. // let rect = NSString(string: self).boundingRect(with: CGSize(width: width, height: CGFloat(MAXFLOAT)), options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
  38. // return ceil(rect.height)
  39. // }
  40. //
  41. // public func ga_heightForComment(fontSize: CGFloat, width: CGFloat, maxHeight: CGFloat) -> CGFloat {
  42. // let font = UIFont.systemFont(ofSize: fontSize)
  43. // let rect = NSString(string: self).boundingRect(with: CGSize(width: width, height: CGFloat(MAXFLOAT)), options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil)
  44. // return ceil(rect.height)>maxHeight ? maxHeight : ceil(rect.height)
  45. // }
  46. func enumerateSearchText(searchText: String?, complate: (_ idx: Int, _ range: NSRange) -> Void) {
  47. if searchText == nil || (searchText?.count ?? 0) <= 0 || !contains(searchText!) {
  48. return
  49. }
  50. let len: Int = searchText!.count
  51. var loc: Int = 0
  52. let subStrings: [String] = NSString(string: self).components(separatedBy: searchText!)
  53. for (idx, subText) in subStrings.enumerated() {
  54. loc = loc + subText.count
  55. let range = NSRange(location: loc, length: len)
  56. complate(idx, range)
  57. loc = loc + len
  58. if idx == subStrings.count - 2 {
  59. break
  60. }
  61. }
  62. }
  63. func attributedTextWithSearchText(searchText: String?, textColor: UIColor, textFont: UIFont, searchTextColor: UIColor, searchTextFont: UIFont) -> NSMutableAttributedString {
  64. let attbText = NSMutableAttributedString(string: self, attributes: [NSAttributedString.Key.font: textFont, NSAttributedString.Key.foregroundColor: textColor])
  65. if count <= 0 || searchText == nil || (searchText?.count ?? 0) <= 0 {
  66. return attbText
  67. }
  68. for tempStr in searchText! {
  69. enumerateSearchText(searchText: "\(tempStr)") { _, range in
  70. attbText.setAttributes([NSAttributedString.Key.font: searchTextFont, NSAttributedString.Key.foregroundColor: searchTextColor], range: range)
  71. }
  72. }
  73. return attbText
  74. }
  75. // 判断是否为空
  76. var isSpace: Bool {
  77. return allSatisfy { $0.isWhitespace }
  78. }
  79. /// 通过 文件路径/文件名/文件后缀 获取mimeType(文件媒体类型)
  80. /// - Parameter pathExtension: <#pathExtension description#>
  81. /// - Returns: <#description#>
  82. func mimeType() -> String {
  83. if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (self as NSString).pathExtension as CFString, nil)?.takeRetainedValue() {
  84. if let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?
  85. .takeRetainedValue()
  86. {
  87. return mimetype as String
  88. }
  89. }
  90. return "application/octet-stream"
  91. }
  92. // 将原始的url编码为合法的url
  93. func urlEncoded() -> String {
  94. let encodeUrlString = addingPercentEncoding(withAllowedCharacters:
  95. .urlQueryAllowed)
  96. return encodeUrlString ?? ""
  97. }
  98. // 将编码后的url转换回原始的url
  99. func urlDecoded() -> String {
  100. return removingPercentEncoding ?? ""
  101. }
  102. // 判断是否包含Emoji表情
  103. func isEmoji() -> Bool {
  104. let numbers = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
  105. guard !numbers.contains(self) else {
  106. return false
  107. }
  108. let tempEmoji: [Unicode.Scalar] = ["\u{26F8}", "\u{26F7}", "\u{263A}", "\u{2639}", "\u{2620}", "\u{270C}", "\u{261D}", "\u{0001F441}", "\u{0001F5E3}", "\u{0001F575}", "\u{0001F574}", "\u{26D1}", "\u{0001F576}", "\u{0001F577}", "\u{0001F54A}", "\u{0001F43F}", "\u{2618}", "\u{0001F32A}", "\u{2600}", "\u{0001F324}", "\u{0001F325}", "\u{2601}", "\u{0001F326}", "\u{0001F327}", "\u{26C8}", "\u{0001F329}", "\u{0001F328}", "\u{2744}", "\u{2603}", "\u{0001F32C}", "\u{2602}", "\u{0001F32B}", "\u{0001F336}", "\u{0001F37D}"]
  109. let scalars = unicodeScalars.map { $0.value }
  110. for element in scalars {
  111. if let scalar = Unicode.Scalar(element) {
  112. if #available(iOS 10.2, *) {
  113. if (scalar.properties.isEmoji && scalar.properties.isEmojiPresentation) || tempEmoji.contains(scalar) {
  114. return true
  115. } else {
  116. // BFLog(message: "是表情==\(element),\(scalar)")
  117. }
  118. }
  119. }
  120. }
  121. return false
  122. }
  123. internal var isContainsEmoji: Bool {
  124. for scalar in unicodeScalars {
  125. switch scalar.value {
  126. case 0x1F600...0x1F64F, // Emoticons
  127. 0x1F300...0x1F5FF, // Misc Symbols and Pictographs
  128. 0x1F680...0x1F6FF, // Transport and Map
  129. 0x2600...0x26FF, // Misc symbols
  130. 0x2700...0x27BF, // Dingbats
  131. 0xFE00...0xFE0F: // Variation Selectors
  132. return true
  133. default:
  134. continue
  135. }
  136. }
  137. return false
  138. }
  139. // 是否包含表情
  140. internal var containsEmoji: Bool {
  141. for scalar in unicodeScalars {
  142. switch scalar.value {
  143. case
  144. 0x00A0...0x00AF,
  145. 0x2030...0x204F,
  146. 0x2120...0x213F,
  147. 0x2190...0x21AF,
  148. 0x2310...0x329F,
  149. 0x1F000...0x1F9CF:
  150. return true
  151. default:
  152. continue
  153. }
  154. }
  155. return false
  156. }
  157. /**
  158. * 字母、数字、中文正则判断(不包括空格)
  159. *注意: 因为考虑到输入习惯,许多人习惯使用九宫格,这里在正常选择全键盘输入错误的时候,进行九宫格判断,九宫格对应的是下面➋➌➍➎➏➐➑➒的字符
  160. */
  161. internal static func isInputRuleNotBlank(str: String) -> Bool {
  162. let pattern = "^[a-zA-Z\\u4E00-\\u9FA5\\d]*$"
  163. let pred = NSPredicate(format: "SELF MATCHES %@", pattern)
  164. let isMatch = pred.evaluate(with: str)
  165. if !isMatch {
  166. let other = "➋➌➍➎➏➐➑➒"
  167. let len = str.count
  168. for i in 0..<len {
  169. let tmpStr = str as NSString
  170. let tmpOther = other as NSString
  171. let c = tmpStr.character(at: i)
  172. if !(isalpha(Int32(c)) > 0 || isalnum(Int32(c)) > 0 || (Int(c) == "_".hashValue) || (Int(c) == "-".hashValue) || (c >= 0x4E00 && c <= 0x9FA6) || (tmpOther.range(of: str).location != NSNotFound)) {
  173. return false
  174. }
  175. return true
  176. }
  177. }
  178. return isMatch
  179. }
  180. }
  181. public extension Optional where Wrapped == String {
  182. var isSpace: Bool {
  183. return self?.isSpace ?? true
  184. }
  185. }