MCProxyTextFieldRx.swift 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. //
  2. // MCProxyTextFieldRx.swift
  3. // HCQuanfangtong
  4. //
  5. // Created by Apple on 2022/4/7.
  6. // Copyright © 2022 Jyp. All rights reserved.
  7. //
  8. import Foundation
  9. import RxSwift
  10. import RxCocoa
  11. import UIKit
  12. enum KeybordInputType : NSInteger{
  13. case number = 0 //数字 整数位 【最大输入长度】
  14. case decimal //数字强校验 不能0开头 ,可以输入小数 [整数位长度,小数位长度,最大值可以为空]
  15. case charater //文本 (不可以输入中文) [最大输入长度]
  16. case text //可以输入任何东西 【最大输入长度】
  17. case degital //数字强校验 不能0开头 整数 [最大输入长度,最大值可以为空]
  18. case phone //电话号码
  19. case numberOrLetter //数字或26个字母 【最大输入长度】
  20. case chinese //只可以输入中文
  21. }
  22. //自动注入
  23. @propertyWrapper class TextFieldWrapper{
  24. /// 输入类型
  25. var type : KeybordInputType
  26. /// 最大长度
  27. var maxLength : Int?
  28. /// 最大值
  29. var maxValue : Float?
  30. /// 小数位
  31. var decimalLength : Int?
  32. /// 检验的block
  33. var checkBlock : (()->Bool)?
  34. /// 结束输入
  35. var endBlock : (()->Void)
  36. var defaultValue : UITextField?
  37. /// 是否可以输入负数
  38. var limitNagativeNumber : Bool
  39. let disposeAutoRelease = DisposeBag()
  40. var wrappedValue : UITextField?{
  41. didSet{
  42. wrappedValue?.rx.bindDelegate(delegate: MCTextFieldDelegate.init(type: type, maxLength: maxLength, maxValue: maxValue, decimalLength: decimalLength, limitNagativeNumber: limitNagativeNumber, checkBlock: checkBlock, endBlock: endBlock)).subscribe().disposed(by: disposeAutoRelease)
  43. }
  44. }
  45. init(wrappedValue defaultValue: UITextField? = nil, type:KeybordInputType = .decimal, maxLength: Int? = nil, decimalLength: Int? = nil, maxValue: Float? = nil, limitNagativeNumber: Bool = true, checkBlock: (()->Bool)? = nil, endBlock: @escaping (()->Void)){
  46. self.defaultValue = defaultValue
  47. self.maxLength = maxLength
  48. self.maxValue = maxValue
  49. self.decimalLength = decimalLength
  50. self.endBlock = endBlock
  51. self.type = type
  52. self.limitNagativeNumber = limitNagativeNumber
  53. }
  54. }
  55. @propertyWrapper class TextFieldPhoneWrapper : TextFieldWrapper{
  56. override var wrappedValue : UITextField?{
  57. didSet{
  58. if wrappedValue.isNotNil{
  59. wrappedValue?.rx.bindDelegate(delegate: MCTextFieldDelegate.init(type: type, maxLength: maxLength, maxValue: maxValue, decimalLength: decimalLength, limitNagativeNumber: limitNagativeNumber, checkBlock: checkBlock, endBlock: endBlock)).subscribe().disposed(by: disposeAutoRelease)
  60. }
  61. }
  62. }
  63. init(wrappedValue defaultValue: UITextField? = nil, checkBlock: (()->Bool)? = nil, block: @escaping (()->Void)){
  64. super.init(wrappedValue: defaultValue, type: .phone, maxLength: nil, decimalLength: nil, maxValue: nil, limitNagativeNumber: true, checkBlock: checkBlock, endBlock: block)
  65. }
  66. }
  67. @propertyWrapper class TextFieldDecimalWrapper : TextFieldWrapper{
  68. override var wrappedValue : UITextField?{
  69. didSet{
  70. if wrappedValue.isNotNil{
  71. wrappedValue?.rx.bindDelegate(delegate: MCTextFieldDelegate.init(type: type, maxLength: maxLength, maxValue: maxValue, decimalLength: decimalLength, limitNagativeNumber: limitNagativeNumber, checkBlock: checkBlock, endBlock: endBlock)).subscribe().disposed(by: disposeAutoRelease)
  72. }
  73. }
  74. }
  75. init(wrappedValue defaultValue: UITextField? = nil, maxValue: Float? = nil, checkBlock: (()->Bool)? = nil, block: @escaping (()->Void)){
  76. super.init(wrappedValue: defaultValue, type: .decimal, maxLength: 9, decimalLength: 2, maxValue: maxValue, limitNagativeNumber: true, checkBlock: checkBlock, endBlock: block)
  77. }
  78. }
  79. @propertyWrapper class TextFieldDegitalWrapper : TextFieldWrapper{
  80. override var wrappedValue : UITextField?{
  81. didSet{
  82. if wrappedValue.isNotNil{
  83. wrappedValue?.rx.bindDelegate(delegate: MCTextFieldDelegate.init(type: type, maxLength: maxLength, maxValue: maxValue, decimalLength: decimalLength, limitNagativeNumber: limitNagativeNumber, checkBlock: checkBlock, endBlock: endBlock)).subscribe().disposed(by: disposeAutoRelease)
  84. }
  85. }
  86. }
  87. init(wrappedValue defaultValue: UITextField? = nil, maxLength: Int? = nil, maxValue: Float? = nil, checkBlock: (()->Bool)? = nil, block: @escaping (()->Void)){
  88. super.init(wrappedValue: defaultValue, type: .decimal, maxLength: maxLength, decimalLength: nil, maxValue: maxValue, limitNagativeNumber: true, checkBlock: checkBlock, endBlock: block)
  89. }
  90. }
  91. @propertyWrapper class TextFieldOtherWrapper : TextFieldWrapper{
  92. override var wrappedValue : UITextField?{
  93. didSet{
  94. if wrappedValue.isNotNil{
  95. if self.type == .chinese || self.type == .text{
  96. wrappedValue!.bindChineseDelegate(delegate: MCTextFieldDelegate.chinese(maxLength: maxLength, block: endBlock))
  97. return
  98. }
  99. wrappedValue?.rx.bindDelegate(delegate: MCTextFieldDelegate.init(type: type, maxLength: maxLength, maxValue: maxValue, decimalLength: decimalLength, limitNagativeNumber: limitNagativeNumber, checkBlock: checkBlock, endBlock: endBlock)).subscribe().disposed(by: disposeAutoRelease)
  100. }
  101. }
  102. }
  103. init(wrappedValue defaultValue: UITextField? = nil, type: KeybordInputType, maxLength: Int? = nil, checkBlock: (()->Bool)? = nil, block: @escaping (()->Void)){
  104. super.init(wrappedValue: defaultValue, type: type, maxLength: maxLength, decimalLength: nil, maxValue: nil, limitNagativeNumber: true, checkBlock: checkBlock, endBlock: block)
  105. }
  106. }
  107. class MCTextFieldDelegate : NSObject, UITextFieldDelegate{
  108. /// 输入类型
  109. var type : KeybordInputType
  110. /// 最大长度
  111. var maxLength : Int?
  112. /// 最大值
  113. var maxValue : Float?
  114. /// 小数位数
  115. var decimalLength : Int?
  116. /// 是否限制输入负数
  117. var limitNagativeNumber : Bool!
  118. /// 检验的block
  119. var checkBlock : (()->Bool)?
  120. /// 输入结束
  121. var endBlock : (()->Void)
  122. init(type:KeybordInputType = .decimal, maxLength: Int? = nil, maxValue: Float? = nil, decimalLength: Int? = nil, limitNagativeNumber: Bool = true, checkBlock: (()->Bool)? = nil, endBlock: @escaping (()->Void)){
  123. self.maxLength = maxLength
  124. self.maxValue = maxValue
  125. self.decimalLength = decimalLength
  126. self.endBlock = endBlock
  127. self.checkBlock = checkBlock
  128. self.type = type
  129. self.limitNagativeNumber = limitNagativeNumber
  130. }
  131. public static func decimalDefault(maxValue: Float? = nil, checkBlock: (()->Bool)? = nil, block: @escaping (()->Void)) -> MCTextFieldDelegate{
  132. MCTextFieldDelegate.init(type: .decimal, maxLength: 9, maxValue: maxValue, decimalLength: 2, limitNagativeNumber: true, checkBlock: checkBlock, endBlock: block)
  133. }
  134. public static func phone(checkBlock: (()->Bool)? = nil, block: @escaping (()->Void)) -> MCTextFieldDelegate{
  135. MCTextFieldDelegate.init(type: .phone, checkBlock: checkBlock, endBlock: block)
  136. }
  137. public static func text(maxLength: Int? = nil, checkBlock: (()->Bool)? = nil, block: @escaping (()->Void)) -> MCTextFieldDelegate{
  138. MCTextFieldDelegate.init(type: .text, maxLength: maxLength, checkBlock: checkBlock, endBlock: block)
  139. }
  140. public static func charater(maxLength: Int? = nil, checkBlock: (()->Bool)? = nil, block: @escaping (()->Void)) -> MCTextFieldDelegate{
  141. MCTextFieldDelegate.init(type: .charater, maxLength: maxLength, checkBlock: checkBlock, endBlock: block)
  142. }
  143. public static func degital(maxLength: Int? = nil, maxValue: Float? = nil, checkBlock: (()->Bool)? = nil, block: @escaping (()->Void)) -> MCTextFieldDelegate{
  144. MCTextFieldDelegate.init(type: .degital, maxLength: maxLength, maxValue: maxValue, checkBlock: checkBlock, endBlock: block)
  145. }
  146. public static func number(maxLength: Int? = nil, maxValue: Float? = nil, checkBlock: (()->Bool)? = nil, block: @escaping (()->Void)) -> MCTextFieldDelegate{
  147. MCTextFieldDelegate.init(type: .number, maxLength: maxLength, maxValue: maxValue, checkBlock: checkBlock, endBlock: block)
  148. }
  149. public static func numberOrLetter(maxLength: Int? = nil, checkBlock: (()->Bool)? = nil, block: @escaping (()->Void)) -> MCTextFieldDelegate{
  150. MCTextFieldDelegate.init(type: .numberOrLetter, maxLength: maxLength, checkBlock: checkBlock, endBlock: block)
  151. }
  152. public static func chinese(maxLength: Int? = nil, checkBlock: (()->Bool)? = nil, block: @escaping (()->Void)) -> MCTextFieldDelegate{
  153. MCTextFieldDelegate.init(type: .chinese, maxLength: maxLength, checkBlock: checkBlock, endBlock: block)
  154. }
  155. var regex : String{
  156. get{
  157. if type == .decimal{
  158. if maxLength.isNil || decimalLength.isNil{
  159. fatalError("配置有问题")
  160. }
  161. return self.limitNagativeNumber ? "(([0]|(0[.]\\d{0,\(decimalLength!)}))|([1-9]\\d{0,\(maxLength! - 1)}(([.]\\d{0,\(decimalLength!)})?)))?" : "(\\-)?(([0]|(0[.]\\d{0,\(decimalLength!)}))|([1-9]\\d{0,\(maxLength! - 1)}(([.]\\d{0,\(decimalLength!)})?)))?"
  162. }
  163. if type == .degital{
  164. //纯数字 强效验以0开头
  165. if maxLength.isNotNil{
  166. return self.limitNagativeNumber ? "([1-9]\\d{0,\(maxLength! - 1)})?" : "(\\-)?([1-9]\\d{0,\(maxLength! - 1)})?"
  167. }
  168. else{
  169. return self.limitNagativeNumber ? "([1-9][0-9]*)?" : "(\\-)?([1-9][0-9]*)?"
  170. }
  171. }
  172. if type == .number{
  173. return maxLength.isNotNil ? "(\\d{0,\(maxLength!)})?" : "([0-9]*)?"
  174. }
  175. if type == .phone{
  176. return "([1]\\d{0,10})?"
  177. }
  178. if type == .text{
  179. // return maxLength.isNotNil ? "([\\S\\s]{0,\(maxLength!)})?" : "([\\S\\s]*)?"
  180. return "([\\S\\s]*)?"
  181. }
  182. if type == .charater{
  183. return maxLength.isNotNil ? "([^\\u4e00-\\u9fa5]{0,\(maxLength!)})?" : "([^\\u4e00-\\u9fa5]*)?"
  184. }
  185. if type == .numberOrLetter{
  186. return maxLength.isNotNil ? "([\\w]{0,\(maxLength!)})?" : "([\\w]*)?"
  187. }
  188. if type == .chinese{
  189. // return maxLength.isNotNil ? "([\\u4e00-\\u9fa5]{0,\(maxLength!)})?" : "([\\u4e00-\\u9fa5]*)?"
  190. return "([\\u4e00-\\u9fa5]*)?"
  191. }
  192. return ""
  193. }
  194. }
  195. func getRegexStr(_ text: String?) -> String?{
  196. if let text = text{
  197. do {
  198. let regularRxpression = try NSRegularExpression.init(pattern: self.regex, options: .caseInsensitive)
  199. let result = regularRxpression.matches(in: text, range: NSMakeRange(0, text.count))
  200. var resultArray : [String] = []
  201. var resultStr: String?
  202. switch self.type {
  203. case .number, .text, .charater, .numberOrLetter, .chinese:
  204. for item in result{
  205. resultArray.append(NSString.init(format: "%@", text).substring(with: item.range))
  206. }
  207. resultStr = resultArray.joined(separator: "")
  208. if self.maxLength.isNotNil{
  209. if (resultStr?.count ?? 0) > self.maxLength! && self.maxLength! > 0{
  210. resultStr = NSString.init(string: resultStr!).substring(with: NSRange.init(location: 0, length: self.maxLength!))
  211. }
  212. }
  213. if self.type == .number{
  214. if self.maxValue.isNotNil{
  215. if Convert.toString(resultStr, decimalNumber: 0) > self.maxValue!{
  216. resultStr = Convert.toString(self.maxValue, decimalNumber: 0)
  217. }
  218. }
  219. resultStr = Convert.toString(resultStr, decimalNumber: 0)
  220. }
  221. case .decimal, .degital:
  222. for item in result{
  223. resultArray.append(NSString.init(format: "%@", text).substring(with: item.range))
  224. break
  225. }
  226. resultStr = resultArray.joined(separator: "")
  227. if self.maxValue.isNotNil{
  228. if Convert.toString(resultStr, decimalNumber: self.type == .decimal ? self.decimalLength:0) > self.maxValue!{
  229. resultStr = Convert.toString(self.maxValue, decimalNumber: self.type == .decimal ? self.decimalLength:0)
  230. }
  231. }
  232. resultStr = Convert.toString(resultStr, decimalNumber: self.type == .decimal ? self.decimalLength:0)
  233. case .phone:
  234. for item in result{
  235. resultArray.append(NSString.init(format: "%@", text).substring(with: item.range))
  236. }
  237. resultStr = resultArray.joined(separator: "")
  238. if (resultStr?.count ?? 0) > 11{
  239. resultStr = NSString.init(string: resultStr!).substring(with: NSRange.init(location: 0, length: 11))
  240. }
  241. }
  242. return resultStr
  243. } catch _ {
  244. return nil
  245. }
  246. }
  247. return nil
  248. }
  249. func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
  250. let toString : String? = (textField.text as NSString?)?.replacingCharacters(in: range, with: string)
  251. if self.type == .text || self.type == .chinese{
  252. return true
  253. }
  254. if toString?.count ?? 0 > 0{
  255. let pricePredicate = NSPredicate.init(format: "SELF MATCHES %@", regex)
  256. if !pricePredicate.evaluate(with: toString){
  257. return false
  258. }
  259. else{
  260. if self.maxValue.isNotNil && (type == .decimal || type == .degital || type == .number){
  261. if Convert.toString(toString, decimalNumber: decimalLength) > self.maxValue!{
  262. return false
  263. }
  264. }
  265. }
  266. }
  267. return true
  268. }
  269. func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
  270. if self.checkBlock.isNil{
  271. return true
  272. }
  273. return self.checkBlock!()
  274. }
  275. func textFieldDidEndEditing(_ textField: UITextField) {
  276. if self.maxValue.isNotNil && (type == .decimal || type == .degital || type == .number){
  277. if Convert.toString(textField.text, decimalNumber: decimalLength) > self.maxValue!{
  278. textField.text = Convert.toString(self.maxValue, decimalNumber: decimalLength)
  279. }
  280. }
  281. if type == .decimal{
  282. textField.text = Convert.toString(textField.text, decimalNumber: decimalLength)
  283. // if textField.text.isEmptyStr{
  284. // textField.text = Convert.toString(0, decimalNumber: decimalLength)
  285. // }
  286. }
  287. else if type == .degital || type == .number{
  288. textField.text = Convert.toString(textField.text, decimalNumber: 0)
  289. }
  290. else{
  291. textField.text = textField.text
  292. }
  293. self.endBlock()
  294. }
  295. }
  296. extension UITextField{
  297. private struct AssociatedDelegateKey{
  298. static var delegate = "AssociatedDelegateKeyObservables"
  299. }
  300. private var associatedDelegate : MCTextFieldDelegate?{
  301. set{
  302. objc_setAssociatedObject(self, &AssociatedDelegateKey.delegate, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  303. }
  304. get{
  305. objc_getAssociatedObject(self, &AssociatedDelegateKey.delegate) as? MCTextFieldDelegate
  306. }
  307. }
  308. func bindChineseDelegate(delegate: MCTextFieldDelegate){
  309. self.delegate = delegate
  310. self.associatedDelegate = delegate
  311. self.addTarget(self, action: #selector(mcDelegateConstraintTextFieldDidChange), for: UIControlEvents.editingChanged)
  312. }
  313. @objc func mcDelegateConstraintTextFieldDidChange(){
  314. guard let delegate = self.delegate as? MCTextFieldDelegate else{
  315. return
  316. }
  317. if delegate.type == .text || delegate.type == .chinese{
  318. let lang = self.textInputMode?.primaryLanguage
  319. if lang == "zh-Hans"{
  320. let textRange = self.markedTextRange
  321. if let range = textRange{
  322. if self.position(from: range.start, offset: 0) != nil{
  323. return
  324. }
  325. }
  326. }
  327. if let text = self.text{
  328. do {
  329. let regularRxpression = try NSRegularExpression.init(pattern: delegate.regex, options: .caseInsensitive)
  330. let result = regularRxpression.matches(in: text, range: NSMakeRange(0, text.count))
  331. var resultArray : [String] = []
  332. for item in result{
  333. resultArray.append(NSString.init(format: "%@", text).substring(with: item.range))
  334. }
  335. let resultStr = resultArray.joined(separator: "")
  336. if delegate.maxLength.isNotNil{
  337. if resultStr.count > (delegate.maxLength ?? 0){
  338. self.text = NSString.init(string: resultStr).substring(with: NSRange.init(location: 0, length: delegate.maxLength ?? 0))
  339. return
  340. }
  341. }
  342. if resultStr == self.text{
  343. return
  344. }
  345. self.text = resultStr
  346. } catch _ {
  347. }
  348. }
  349. }
  350. }
  351. }
  352. extension Reactive where Base:UITextField{
  353. var textDelegate : DelegateProxy<UITextField, UITextFieldDelegate>{
  354. return TextFieldDelegateProxy.proxy(for: base)
  355. }
  356. func bindDelegate(delegate:UITextFieldDelegate) ->RxSwift.Observable<[Any]>{
  357. self.base.delegate = delegate
  358. return textDelegate.methodInvoked(#selector(UITextFieldDelegate.textField(_:shouldChangeCharactersIn:replacementString:)))
  359. }
  360. var bindEndEditing : RxSwift.Observable<[Any]>{
  361. get{
  362. return textDelegate.methodInvoked(#selector(UITextFieldDelegate.textFieldDidEndEditing(_:)))
  363. }
  364. }
  365. var bindBeginEditing : RxSwift.Observable<[Any]>{
  366. get{
  367. return textDelegate.methodInvoked(#selector(UITextFieldDelegate.textFieldDidBeginEditing(_:)))
  368. }
  369. }
  370. }
  371. public class TextFieldDelegateProxy : DelegateProxy<UITextField, UITextFieldDelegate>,UITextFieldDelegate,DelegateProxyType{
  372. public static func currentDelegate(for object: UITextField) -> UITextFieldDelegate? {
  373. return object.delegate
  374. }
  375. public static func setCurrentDelegate(_ delegate: UITextFieldDelegate?, to object: UITextField) {
  376. object.delegate = delegate
  377. }
  378. public weak private(set) var textField : UITextField?
  379. init(textField : UITextField) {
  380. self.textField = textField
  381. super.init(parentObject: textField, delegateProxy: TextFieldDelegateProxy.self)
  382. }
  383. public static func registerKnownImplementations() {
  384. self.register {
  385. TextFieldDelegateProxy.init(textField: $0)
  386. }
  387. }
  388. public override func setForwardToDelegate(_ delegate: DelegateProxy<UITextField, UITextFieldDelegate>.Delegate?, retainDelegate: Bool) {
  389. super.setForwardToDelegate(delegate, retainDelegate: true)
  390. }
  391. }