jsdw_ios/QuickLocation/Core/Extension/String+Extension.swift

549 lines
18 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// String+public extension.swift
// SwiftBasics
//
// Created by 侯伟 on 17/1/11.
// Copyright © 2017年 侯伟. All rights reserved.
//
import Foundation
import SwiftyJSON
import CommonCrypto
import URLNavigator
import SwiftDate
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
}
}
extension String {
var isPhoneNumber: Bool {
let reg = "^1[3-9]\\d{9}$"
return NSPredicate(format: "SELF MATCHES %@", reg).evaluate(with: self)
}
}
// MARK: -
extension String {
// MARK: - 解析 ISO8601 字符串 2026-06-26T14:18:52+08:00 -> Date
private func parseISO8601(_ isoStr: String) -> Date? {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withTimeZone]
return formatter.date(from: isoStr)
}
// MARK: - Date 转自定义格式字符串
/// - Parameters:
/// - date: 目标日期
/// - format: 格式模板 如 yyyy-MM-dd HH:mm:ss
/// - Returns: 格式化后的日期字符串
func formatDate(_ date: Date, format: String) -> String {
let region = Region(zone: TimeZone(identifier: "Asia/Shanghai")!, locale: Locale(identifier: "zh_CN"))
return date.in(region: region).toFormat(format)
}
// MARK: - ISO字符串 直接转自定义格式
func isoStringToCustom(_ isoStr: String, format: String) -> String? {
guard let date = parseISO8601(isoStr) else { return nil }
return formatDate(date, format: format)
}
}