// // CLTextField.swift // Crush // // Created by Leon on 2025/7/15. // import UIKit class CLTextField: UITextField { // MARK: - Properties private var focusBorderColor: UIColor = .orange private var errorBorderColor: UIColor = .red private var nowBorderColor: UIColor = .clear private var tapTopButtonAction: (() -> Void)? /// 在此模式下,errorBorder 在新修改内容之前都会显示 border。 private var errorBorderShowMode: Bool = false private var alwaysShowRightViewConfig: Bool = false private var alwaysShowClearButtonIfHave: Bool = false private var disableAllBorder: Bool = false private var fakeTextFieldForInputTrigger: Bool = false var cornerRadiusConfig:CGFloat = 8 // private var defaultTxt: String? // Closures for delegate-like events (assumed from Objective-C) var textDidChanged: ((CLTextField) -> Void)? var textDidBeginEditing: ((CLTextField) -> Void)? var textDidEndEditing: ((CLTextField) -> Void)? var textShouldBeginEditing: ((CLTextField) -> Bool)? // MARK: - Initialization override init(frame: CGRect) { super.init(frame: frame) baseCommonSetup() } required init?(coder: NSCoder) { super.init(coder: coder) baseCommonSetup() } private func baseCommonSetup() { // keyboardAppearance = .dark // reloadInputViews() setContentHuggingPriority(UILayoutPriority(244), for: .horizontal) let leftView = UIView(frame: CGRect(x: 0, y: 0, width: 16, height: 16)) leftView.backgroundColor = .clear self.leftViewMode = .always self.leftView = leftView // --- 3 events addTarget(self, action: #selector(textDidChanged(_:)), for: .editingChanged) NotificationCenter.default.addObserver(self, selector: #selector(notiTextDidBeginEditing), name: UITextField.textDidBeginEditingNotification, object: self) NotificationCenter.default.addObserver(self, selector: #selector(notiTextDidEndEditing), name: UITextField.textDidEndEditingNotification, object: self) NotificationCenter.default.addObserver(self, selector: #selector(notiTextShouldBeginEditing(_:)), name: UITextField.textDidBeginEditingNotification, object: self) setupStandard() } // MARK: - Public Methods func setupStandard() { autocorrectionType = .no backgroundColor = .c.csen font = CLSystemToken.font(token: .tbl) textColor = .white focusBorderColor = .theme errorBorderColor = .red layer.borderWidth = 1 layer.borderColor = UIColor.clear.cgColor layer.cornerRadius = cornerRadiusConfig // NSLayoutConstraint.activate([ // heightAnchor.constraint(equalToConstant: 48) // ]) snp.makeConstraints { make in make.height.equalTo(48) } // default setupRightClearButton() } func setupPlaceholder(_ placeholder: String) { let font = UIFont.systemFont(ofSize: 15) let color = UIColor.gray let attributes: [NSAttributedString.Key: Any] = [ .font: font, .foregroundColor: color ] attributedPlaceholder = NSAttributedString(string: placeholder, attributes: attributes) } func setupTopButton(with action: (() -> Void)?) { let topButton = UIButton(type: .custom) topButton.backgroundColor = .clear addSubview(topButton) topButton.addTarget(self, action: #selector(tapTopButton), for: .touchUpInside) tapTopButtonAction = action // Using SnapKit or native Auto Layout topButton.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ topButton.topAnchor.constraint(equalTo: topAnchor), topButton.bottomAnchor.constraint(equalTo: bottomAnchor), topButton.leadingAnchor.constraint(equalTo: leadingAnchor), topButton.trailingAnchor.constraint(equalTo: trailingAnchor) ]) } func showAlwayErrorBorder() { errorBorderShowMode = true layer.borderWidth = 1 layer.borderColor = errorBorderColor.cgColor } func hideErrorBorder() { errorBorderShowMode = false layer.borderColor = UIColor.clear.cgColor } func sendTextChangedNoti(){ NotificationCenter.default.post( name: UITextField.textDidChangeNotification, object: self) } // MARK: - Helper Methods func setupRightClearButton() { rightViewMode = .whileEditing let rightView = UIView(frame: CGRect(x: 0, y: 0, width: 48, height: 48)) self.rightView = rightView let clearButton = EPIconGhostSecondaryButton(radius: .none, iconSize: .small, iconCode: .delete02) rightView.addSubview(clearButton) clearButton.addTarget(self, action: #selector(tapClearButton), for: .touchUpInside) clearButton.snp.makeConstraints { make in make.size.equalTo(clearButton.bgImageSize()) make.centerY.equalToSuperview() make.trailing.equalToSuperview().offset(-8) } } func setupRightEyeButton() { isSecureTextEntry = true rightViewMode = .always alwaysShowRightViewConfig = true let rightView = UIView(frame: CGRect(x: 0, y: 0, width: 48, height: 48)) self.rightView = rightView } func setupRightRightArrowButton() -> UIButton? { rightViewMode = .always alwaysShowRightViewConfig = true let rightView = UIView(frame: CGRect(x: 0, y: 0, width: 48, height: 48)) self.rightView = rightView return nil } func setupRightViewEmpty() { rightView = nil } func setupLeftCoinIconView() { leftViewMode = .always let leftView = UIView(frame: CGRect(x: 0, y: 0, width: 44, height: 40)) let iconView = UIImageView(frame: CGRect(x: 16, y: 12, width: 16, height: 16)) iconView.image = UIImage(named: "icon_16_diamond") leftView.addSubview(iconView) self.leftView = leftView } func setupLeftBlankView() { leftViewMode = .always let leftView = UIView(frame: CGRect(x: 0, y: 0, width: 20, height: 40)) self.leftView = leftView } func adjustRightViewMode() { if alwaysShowRightViewConfig { rightViewMode = .always return } // 空数据不显示删除按钮 if text?.isEmpty ?? true { rightViewMode = .never } else { rightViewMode = alwaysShowClearButtonIfHave ? .always : .whileEditing } } // MARK: - Actions @objc private func tapTopButton() { tapTopButtonAction?() } @objc private func tapClearButton() { text = "" textDidChanged(self) NotificationCenter.default.post( name: UITextField.textDidChangeNotification, object: self) } @objc private func tapRightArrowButton(_ button: UIButton) { // Empty implementation as in original } // MARK: - Textfield Events @objc private func textDidChanged(_ textField: UITextField) { errorBorderShowMode = false nowBorderColor = focusBorderColor if !disableAllBorder && textField.isFirstResponder { layer.borderColor = nowBorderColor.cgColor } adjustRightViewMode() textDidChanged?(self) } @objc private func notiTextDidBeginEditing() { if !errorBorderShowMode && !disableAllBorder { layer.borderColor = focusBorderColor.cgColor layer.borderWidth = 1 } textDidBeginEditing?(self) adjustRightViewMode() } @objc private func notiTextDidEndEditing() { if !errorBorderShowMode { layer.borderColor = nil layer.borderWidth = 0 } textDidEndEditing?(self) } @objc private func notiTextShouldBeginEditing(_ notification: Notification) { if fakeTextFieldForInputTrigger { resignFirstResponder() } textShouldBeginEditing?(self) } // MARK: - Setters override var delegate: UITextFieldDelegate? { get { super.delegate } set { if newValue !== self { // print("请用头文件中的block回调") // Uncomment if assertion is needed } super.delegate = newValue } } override var placeholder: String? { didSet { if let placeholder = placeholder { setupPlaceholder(placeholder) } } } // var defaultTxt: String? { // get { _defaultTxt } // set { // _defaultTxt = newValue // super.text = newValue // } // } override var text: String? { didSet { if text != oldValue { textDidChanged(self) } } } // MARK: - Layout override func rightViewRect(forBounds bounds: CGRect) -> CGRect { var rightViewRect = super.rightViewRect(forBounds: bounds) if #available(iOS 13.0, *) { // fix iOS 13 rightView显示在最左边,和异常显示问题 let left = frame.size.width - (rightView?.frame.size.width ?? 0) let top = round((frame.size.height - (rightView?.frame.size.height ?? 0)) / 2.0) rightViewRect = CGRect(x: left, y: top, width: rightView?.frame.size.width ?? 0, height: rightView?.frame.size.height ?? 0) } return rightViewRect } override func layoutSubviews() { super.layoutSubviews() layer.cornerRadius = self.cornerRadiusConfig } deinit { NotificationCenter.default.removeObserver(self) } } extension CLTextField{ func switchToNaviSearchField(){ setupPlaceholder("Search") font = .t.tbm cornerRadiusConfig = 16// height: 32 snp.updateConstraints { make in make.height.equalTo(32) } leftViewMode = .always let leftView = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 32)) leftView.snp.makeConstraints { make in make.size.equalTo(CGSize(width: 40, height: 32)) } self.leftView = leftView let image = MWIconFont.image(fromIcon: .search, size: CGSize(width: 12, height: 12), color: .white) let icon = UIImageView(image: image) leftView.addSubview(icon) icon.snp.makeConstraints { make in make.size.equalTo(CGSize(width: 12, height: 12)) make.centerY.equalToSuperview() make.leading.equalToSuperview().offset(12) } setNeedsLayout() layoutIfNeeded() } }