515 lines
16 KiB
Swift
515 lines
16 KiB
Swift
//
|
||
// String+public extension.swift
|
||
// SwiftBasics
|
||
//
|
||
// Created by 侯伟 on 17/1/11.
|
||
// Copyright © 2017年 侯伟. All rights reserved.
|
||
//
|
||
|
||
import Foundation
|
||
import SwiftyJSON
|
||
import CommonCrypto
|
||
import URLNavigator
|
||
|
||
|
||
public extension String {
|
||
var completePicUrl: String {
|
||
if !self.hasPrefix("http") {
|
||
if self.hasPrefix("//") {
|
||
return "http:" + self
|
||
} else {
|
||
return "http://fec-img.gan-hai.com" + self
|
||
}
|
||
} else {
|
||
return self
|
||
}
|
||
}
|
||
var length: Int { return self.count }
|
||
}
|
||
|
||
public extension JSON {
|
||
/// JSON to String
|
||
var jsonString: String { return rawString(String.Encoding.utf8, options: JSONSerialization.WritingOptions()) ?? "" }
|
||
}
|
||
|
||
// MARK: URL encode/decode
|
||
|
||
public extension String {
|
||
/// URL编码
|
||
var urlEncoded: String {
|
||
return self.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlHostAllowed) ?? self
|
||
}
|
||
|
||
var urlEncodingWithQueryAllowedCharacters: String {
|
||
return self.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) ?? self
|
||
}
|
||
|
||
/// URL解码
|
||
var urlDecoded: String {
|
||
return self.removingPercentEncoding ?? self
|
||
}
|
||
}
|
||
|
||
// MARK: md5
|
||
public extension String {
|
||
var MD5String: String {
|
||
|
||
let cStrl = cString(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue));
|
||
|
||
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: 16);
|
||
|
||
CC_MD5(cStrl, CC_LONG(strlen(cStrl!)), buffer);
|
||
|
||
var md5String = "";
|
||
|
||
for idx in 0...15 {
|
||
|
||
let obcStrl = String.init(format: "%02x", buffer[idx]);
|
||
|
||
md5String.append(obcStrl);
|
||
|
||
}
|
||
|
||
free(buffer);
|
||
|
||
return md5String;
|
||
|
||
}
|
||
}
|
||
// MARK: Base64
|
||
|
||
public extension String {
|
||
/// Base64编码
|
||
var base64Encoded: String {
|
||
return data.base64EncodedString(options: [])
|
||
}
|
||
|
||
/// Base64解码
|
||
var base64Decoded: String {
|
||
return Data(base64Encoded: self, options: [])?.string ?? ""
|
||
}
|
||
}
|
||
|
||
|
||
public extension String {
|
||
var md5: String {
|
||
let data = self.data
|
||
var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
|
||
CC_MD5((data as NSData).bytes, CC_LONG(data.count), &digest)
|
||
let hexBytes = digest.map { String(format: "%02hhx", $0) }
|
||
return hexBytes.joined(separator: "")
|
||
}
|
||
|
||
var sha1: String {
|
||
let data = self.data
|
||
var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
|
||
CC_SHA1((data as NSData).bytes, CC_LONG(data.count), &digest)
|
||
let hexBytes = digest.map { String(format: "%02hhx", $0) }
|
||
return hexBytes.joined(separator: "")
|
||
}
|
||
}
|
||
// MARK: Get the size of the text
|
||
|
||
public extension String {
|
||
/// 计算制定字体大小的文字显示尺寸(系统默认字体)
|
||
func size(fontSize: CGFloat, width: CGFloat = CGFloat.greatestFiniteMagnitude) -> CGSize {
|
||
return size(font: UIFont.systemFont(ofSize: fontSize), width: width)
|
||
}
|
||
|
||
/// 计算制定字体的文字显示尺寸
|
||
func size(font: UIFont, width: CGFloat = CGFloat.greatestFiniteMagnitude) -> CGSize {
|
||
let size = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)
|
||
let paragraphStyle = NSMutableParagraphStyle()
|
||
paragraphStyle.lineBreakMode = .byWordWrapping
|
||
let attributes = [
|
||
NSAttributedString.Key.font: font,
|
||
NSAttributedString.Key.paragraphStyle: paragraphStyle.copy()
|
||
]
|
||
|
||
return (self as NSString).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attributes, context: nil).size
|
||
}
|
||
|
||
/// 获取文字几行的高度
|
||
/// - Parameter count: 几行
|
||
/// - Returns: 高度
|
||
func lineHeight(_ count: Int = 1,
|
||
font: UIFont,
|
||
width: CGFloat = CGFloat.greatestFiniteMagnitude) -> CGFloat {
|
||
size(font: font, width: width).height * CGFloat(count)
|
||
}
|
||
}
|
||
|
||
// MARK: Substring
|
||
public extension String {
|
||
func substring(_ fromIndex: Int, _ toIndex: Int = Int.max) -> String? {
|
||
let len = self.length
|
||
var start: Int
|
||
var end: Int
|
||
if fromIndex < 0 {
|
||
start = 0
|
||
end = len + fromIndex
|
||
} else {
|
||
start = fromIndex
|
||
if toIndex < 0 {
|
||
end = len + toIndex
|
||
} else {
|
||
end = min(toIndex, len)
|
||
}
|
||
}
|
||
|
||
if start > end {
|
||
return nil
|
||
}
|
||
|
||
return self[start..<end]
|
||
}
|
||
|
||
subscript(range: Range<Int>) -> String? {
|
||
let len = self.length
|
||
if range.lowerBound >= len || range.upperBound < 0 {
|
||
return nil
|
||
}
|
||
|
||
let startIndex = self.index(self.startIndex, offsetBy: max(0, range.lowerBound))
|
||
let endIndex = self.index(self.startIndex, offsetBy: min(len, range.upperBound))
|
||
|
||
return String(self[startIndex..<endIndex])
|
||
}
|
||
|
||
subscript(index: Int) -> Character? {
|
||
if index < 0 || index >= self.length {
|
||
return nil
|
||
}
|
||
return self[self.index(self.startIndex, offsetBy: index)]
|
||
}
|
||
}
|
||
|
||
// MARK: Trim
|
||
|
||
public extension String {
|
||
/// 删除前后空白符(包含空格、Tab、回车、换行符)
|
||
var trimmed: String {
|
||
return self.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
|
||
}
|
||
}
|
||
|
||
// MARK: Localized String
|
||
|
||
public extension String {
|
||
var localizedString: String {
|
||
return NSLocalizedString(self, comment: "")
|
||
}
|
||
}
|
||
|
||
// MARK: - NSData
|
||
|
||
public extension String {
|
||
/// 返回UTF8字符集的NSData对象
|
||
var data: Data {
|
||
return self.data(using: String.Encoding.utf8, allowLossyConversion: false)!
|
||
}
|
||
}
|
||
|
||
public extension Data {
|
||
/// 返回UTF8字符串
|
||
var string: String {
|
||
return String(data: self, encoding: String.Encoding.utf8) ?? ""
|
||
}
|
||
}
|
||
|
||
// MARK: - Int
|
||
|
||
public extension String {
|
||
var int: Int32 { return (self as NSString).intValue }
|
||
var integer: Int { return (self as NSString).integerValue }
|
||
}
|
||
|
||
public extension Int {
|
||
var string: String { return String(self) }
|
||
}
|
||
|
||
// MARK: - Float
|
||
|
||
public extension String {
|
||
var float: Float { return (self as NSString).floatValue }
|
||
}
|
||
|
||
public extension Float {
|
||
var string: String { return String(self) }
|
||
}
|
||
|
||
// MARK: - Double
|
||
|
||
public extension String {
|
||
var double: Double { return (self as NSString).doubleValue }
|
||
}
|
||
|
||
public extension Double {
|
||
var string: String { return String(self) }
|
||
}
|
||
|
||
// MARK: - Bool
|
||
|
||
public extension String {
|
||
var bool: Bool { return (self as NSString).boolValue }
|
||
}
|
||
|
||
public extension Bool {
|
||
var string: String { return self ? "true" : "false" }
|
||
}
|
||
|
||
public extension String {
|
||
// 对象方法
|
||
func getFileSize() -> UInt64 {
|
||
var size: UInt64 = 0
|
||
let fileManager = FileManager.default
|
||
var isDir: ObjCBool = false
|
||
let isExists = fileManager.fileExists(atPath: self, isDirectory: &isDir)
|
||
// 判断文件存在
|
||
if isExists {
|
||
// 是否为文件夹
|
||
if isDir.boolValue {
|
||
// 迭代器 存放文件夹下的所有文件名
|
||
let enumerator = fileManager.enumerator(atPath: self)
|
||
for subPath in enumerator! {
|
||
// 获得全路径
|
||
let fullPath = self.appending("/\(subPath)")
|
||
do {
|
||
let attr = try fileManager.attributesOfItem(atPath: fullPath)
|
||
size += attr[FileAttributeKey.size] as! UInt64
|
||
} catch {
|
||
print("error :\(error)")
|
||
}
|
||
}
|
||
} else { // 单文件
|
||
do {
|
||
let attr = try fileManager.attributesOfItem(atPath: self)
|
||
size += attr[FileAttributeKey.size] as! UInt64
|
||
|
||
} catch {
|
||
print("error :\(error)")
|
||
}
|
||
}
|
||
}
|
||
return size
|
||
}
|
||
}
|
||
|
||
// MARK: - 文字颜色
|
||
public extension String {
|
||
func changeTextColor(color: UIColor, font: UIFont, text: String) -> NSMutableAttributedString {
|
||
let nsString = NSString(string: self)
|
||
let attributeString = NSMutableAttributedString(string: self)
|
||
let arrRange = rangeOfString(string: nsString, andInString: text)
|
||
for range in arrRange {
|
||
attributeString.addAttributes([NSAttributedString.Key.font: font], range: range)
|
||
attributeString.addAttributes([NSAttributedString.Key.foregroundColor: color], range: range)
|
||
}
|
||
|
||
return attributeString
|
||
}
|
||
|
||
func changeTextColor(color: UIColor, font: UIFont, firstText: String, secondText: String) -> NSMutableAttributedString {
|
||
let nsString = NSString(string: self)
|
||
let attributeString = NSMutableAttributedString(string: self)
|
||
let firstArrRange = rangeOfString(string: nsString, andInString: firstText)
|
||
for range in firstArrRange {
|
||
attributeString.addAttributes([NSAttributedString.Key.font: font], range: range)
|
||
attributeString.addAttributes([NSAttributedString.Key.foregroundColor: color], range: range)
|
||
}
|
||
let secondArrRange = rangeOfString(string: nsString, andInString: secondText)
|
||
for range in secondArrRange {
|
||
attributeString.addAttributes([NSAttributedString.Key.font: font], range: range)
|
||
attributeString.addAttributes([NSAttributedString.Key.foregroundColor: color], range: range)
|
||
}
|
||
|
||
return attributeString
|
||
}
|
||
|
||
/// 获取字符出现的位置信息(支持多次位置获取)
|
||
/// - Parameter string: 原始文本
|
||
/// - Parameter inString: 需要查找的字符
|
||
private func rangeOfString(string:NSString,
|
||
andInString inString:String) -> [NSRange] {
|
||
|
||
var arrRange = [NSRange]()
|
||
var _fullText = string
|
||
var rang:NSRange = _fullText.range(of: inString)
|
||
|
||
while rang.location != NSNotFound {
|
||
var location:Int = 0
|
||
if arrRange.count > 0 {
|
||
if arrRange.last!.location + arrRange.last!.length < string.length {
|
||
location = arrRange.last!.location + arrRange.last!.length
|
||
}
|
||
}
|
||
|
||
_fullText = NSString.init(string: _fullText.substring(from: rang.location + rang.length))
|
||
|
||
if arrRange.count > 0 {
|
||
rang.location += location
|
||
}
|
||
arrRange.append(rang)
|
||
|
||
rang = _fullText.range(of: inString)
|
||
}
|
||
|
||
return arrRange
|
||
}
|
||
}
|
||
|
||
public extension String {
|
||
|
||
/// 从URL字符串解析查询参数
|
||
var urlQueryParameters: [String: String] {
|
||
guard let url = URL(string: self) else { return [:] }
|
||
return url.queryParameters
|
||
}
|
||
|
||
/// 从String中截取出参数
|
||
var urlParameters: [String: AnyObject]? {
|
||
// 判断是否有参数
|
||
let text = self as NSString
|
||
let range = text.range(of: "?")
|
||
var params = [String: AnyObject]()
|
||
// 截取参数
|
||
let paramsString = text.substring(with: range)
|
||
// 判断参数是单个参数还是多个参数
|
||
if paramsString.contains("&") {
|
||
// 多个参数,分割参数
|
||
let urlComponents = paramsString.components(separatedBy: "&")
|
||
// 遍历参数
|
||
for keyValuePair in urlComponents {
|
||
// 生成Key/Value
|
||
let pairComponents = keyValuePair.components(separatedBy: "=")
|
||
let key = pairComponents.first?.removingPercentEncoding
|
||
let value = pairComponents.last?.removingPercentEncoding
|
||
// 判断参数是否是数组
|
||
if let key = key, let value = value {
|
||
params[key] = value as AnyObject
|
||
// 已存在的值,生成数组
|
||
// if let existValue = params[key] {
|
||
// if var existValue = existValue as? [AnyObject] {
|
||
// existValue.append(value)
|
||
// } else {
|
||
// params[key] = [existValue, value]
|
||
// }
|
||
// } else {
|
||
// params[key] = value as AnyObject
|
||
// }
|
||
}
|
||
}
|
||
} else {
|
||
// 单个参数
|
||
let pairComponents = paramsString.components(separatedBy: "=")
|
||
// 判断是否有值
|
||
if pairComponents.count == 1 {
|
||
return nil
|
||
}
|
||
let key = pairComponents.first?.removingPercentEncoding
|
||
let value = pairComponents.last?.removingPercentEncoding
|
||
if let key = key, let value = value {
|
||
params[key] = value as AnyObject
|
||
}
|
||
}
|
||
return params
|
||
}
|
||
}
|
||
|
||
public extension String {
|
||
func custemReplaceString(of targetStr:String,
|
||
with replaceStr:String,
|
||
replaceColor: UIColor) -> NSMutableAttributedString {
|
||
let labelString = self.replacingOccurrences(of: targetStr, with: replaceStr)
|
||
let nsString = labelString as NSString
|
||
let range = nsString.range(of: replaceStr)
|
||
let mutableStr = NSMutableAttributedString(string: labelString)
|
||
mutableStr.setAttributes([NSAttributedString.Key.foregroundColor: replaceColor], range: range)
|
||
return mutableStr
|
||
}
|
||
|
||
}
|
||
|
||
public extension String {
|
||
/// 手机号格式化去0
|
||
var phoneFromatter: String {
|
||
if let phone = Int64(self) {
|
||
return String(phone)
|
||
}
|
||
return self
|
||
}
|
||
}
|
||
|
||
// MARK: - 转换为NSNumber
|
||
public extension String {
|
||
|
||
var number: NSNumber? {
|
||
let boolNumbers = ["true": true, "false": false, "yes": true, "no": false]
|
||
let nilNumbers = ["nil", "null", "(null)", "<null>"]
|
||
let lowerStr = self.lowercased()
|
||
if let value = boolNumbers[lowerStr] { return NSNumber(value: value) }
|
||
if nilNumbers.contains(lowerStr) { return nil }
|
||
|
||
guard let cstring = self.cString(using: .utf8) else { return nil }
|
||
if self.rangeOfCharacter(from: CharacterSet(charactersIn: ".")) != nil {
|
||
let cnumber = atof(cstring)
|
||
if cnumber.isNaN || cnumber.isInfinite { return nil }
|
||
return NSNumber(value: cnumber)
|
||
} else {
|
||
return NSNumber(value: atoll(cstring))
|
||
}
|
||
}
|
||
}
|
||
|
||
// MARK: - 正则
|
||
public extension String {
|
||
/// 检测Email 邮箱格式是否有效
|
||
static func checkEmailAddressIsValid(address: String?) -> Bool {
|
||
guard address != nil else {
|
||
return false
|
||
}
|
||
let regular = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"
|
||
let regularEmail = NSPredicate(format: "SELF MATCHES %@", regular)
|
||
return regularEmail.evaluate(with: address)
|
||
}
|
||
}
|
||
|
||
// MARK: - 手机号中间用 **** 替换
|
||
extension String {
|
||
/// 手机号中间隐藏
|
||
func phoneFormat() -> String {
|
||
guard self.length > 6 else { return self }
|
||
let preStr = self.substring(0, 3)
|
||
let suffixStr = self.substring(self.length - 4, self.length)
|
||
return (preStr ?? "") + "****" + (suffixStr ?? "")
|
||
}
|
||
}
|
||
|
||
// MARK: - 生成二维码
|
||
extension String {
|
||
func createQRCode(size: CGSize = CGSize(width: 200, height: 200)) -> UIImage? {
|
||
// 1. 字符串转 Data
|
||
guard let data = self.data(using: .utf8) else { return nil }
|
||
|
||
// 2. 创建二维码滤镜
|
||
guard let qrFilter = CIFilter(name: "CIQRCodeGenerator") else { return nil }
|
||
qrFilter.setDefaults()
|
||
qrFilter.setValue(data, forKey: "inputMessage")
|
||
// 容错率:L(7%)/M(15%)/Q(25%)/H(30%),越高容错越强
|
||
qrFilter.setValue("H", forKey: "inputCorrectionLevel")
|
||
|
||
// 3. 获取 CIImage 并放大(原生默认很小,必须放大否则模糊)
|
||
guard let ciImage = qrFilter.outputImage else { return nil }
|
||
let scaleX = size.width / ciImage.extent.width
|
||
let scaleY = size.height / ciImage.extent.height
|
||
let transform = CGAffineTransform(scaleX: scaleX, y: scaleY)
|
||
let scaledCI = ciImage.transformed(by: transform)
|
||
|
||
// 4. 转 UIImage
|
||
if let cgImage = CIContext(options: [.useSoftwareRenderer: false]).createCGImage(scaledCI, from: scaledCI.extent) {
|
||
return UIImage(cgImage: cgImage)
|
||
}
|
||
return nil
|
||
}
|
||
}
|