【问题标题】:How to change the tint color of the clear button on a UITextField如何更改 UITextField 上清除按钮的色调颜色
【发布时间】:2015-03-12 17:53:11
【问题描述】:

我的 UITextfield 上有一个自动生成的清除按钮,默认为蓝色。我无法将色调更改为白色。我尝试修改故事板和代码但没有成功,我不想使用自定义图像。

如何在不使用自定义图像的情况下更改默认清除按钮颜色?

【问题讨论】:

  • 看起来 UITextField 清除按钮不支持全局色调(无论出于何种原因!),您可以查看 this answerthis answer 以了解其他人是如何做到的。看来您必须创建自己的按钮。

标签: ios objective-c swift uibutton uitextfield


【解决方案1】:

Swift 5 解决方案:

   if let clearButton = yourTextField.value(forKey: "_clearButton") as? UIButton {
       let templateImage = clearButton.imageView?.image?.withRenderingMode(.alwaysTemplate)
       clearButton.setImage(templateImage, for: .normal)
       clearButton.tintColor = .darkGray
   }

【讨论】:

    【解决方案2】:

    就像@Brody Robertson 的回答一样,这里是 Swift 5 版本,它适用于我:

    let textField = UITextField()
    if let button = textField.value(forKey: "clearButton") as? UIButton {
      button.tintColor = .white
      button.setImage(UIImage(named: "yourImage")?.withRenderingMode(.alwaysTemplate), for: .normal)
    }
    

    注意:您需要使用您的图标替换yourImage,或者如果您的目标是iOS 13.0或更高版本,您可以将方法UIImage(named:)替换为UIImage(systemName: "xmark.circle.fill")。 Apple 在 iOS 13.0 或更高版本中为您准备了这个清晰的图标。我希望这个能帮上忙!祝你好运!

    【讨论】:

      【解决方案3】:

      修改@3vangelos 解决方案,绕过这个for循环

      for view in subviews where view is UIButton {
              (view as! UIButton).setImage(<MY_UIIMAGE>, for: .normal)
          }
      

      我的修改:-

      class CustomTextField:UITextField { 
      override func layoutSubviews()
          {super.layoutSubviews()
          let clearButton = self.value(forKey: "clearButton") as? UIButton
          clearButton?.setImage(#imageLiteral(resourceName: "icons8-cancel.pdf"), for: .normal)
          clearButton?.tintColor = UIColor(<YOUR_COLOR>)
      }
      

      可以在 UISearchBar 中使用相同的解决方案,并添加一些代码:-

       override func viewWillAppear(_ animated: Bool)
      {
          super.viewWillAppear(animated)
      if let textField = agentsSearchBar.value(forKey: "searchField") as? UITextField
          {
      let clearButton = textField.value(forKey: "clearButton") as? UIButton
      clearButton?.setImage(#imageLiteral(resourceName: "icons8-cancel.pdf"), for: .normal)
      clearButton?.tintColor = UIColor(<YOUR_COLOR>)
      } }
      

      图片 (icons8-cancel.pdf) 可以在以下位置下载 https://icons8.com/icon/set/clear-button/ios7# 并使用以下属性添加到您的图像资产中

      【讨论】:

        【解决方案4】:

        在 Swift 中,您可以编写扩展并在项目中的任何文本字段上使用。

        extension UITextField {
            @objc func modifyClearButton(with image : UIImage) {
                let clearButton = UIButton(type: .custom)
                clearButton.setImage(image, for: .normal)
                clearButton.frame = CGRect(x: 0, y: 0, width: 15, height: 15)
                clearButton.contentMode = .scaleAspectFit
                clearButton.addTarget(self, action: #selector(UITextField.clear(_:)), for: .touchUpInside)
                rightView = clearButton
                rightViewMode = .whileEditing
            }
        
            @objc func clear(_ sender : AnyObject) {
            if delegate?.textFieldShouldClear?(self) == true {
                self.text = ""
                sendActions(for: .editingChanged)
            }
        }
        }
        

        【讨论】:

        • 这很好用,但如果框架的宽度设置为 27,提供的图像为 15x15 (@1x) 并且内容模式设置为 UIViewContentModeCenter,它更接近原始按钮。
        【解决方案5】:

        如果您在应用中使用 UIAppearance,您可以在运行时为清除按钮设置 tintColor。

        let textField = UITextField.appearance()
        textField.tintColor = .green
        

        在启动时,我们在 AppDelegate 中调用一个类函数,该函数具有许多其他控件,其中配置了 .appearance()

        假设您的应用程序外观设置类名为Beautyify,您将创建如下内容:

        @objc class Beautify: NSObject {
            class func applyAppearance() {
                let tableViewAppearance = UITableView.appearance()
                tableViewAppearance.tintColor = .blue
        
                let textField = UITextField.appearance()
                textField.tintColor = .green
            }
        }
        

        然后在 AppDelegate didFinishLaunchingWithOptions 中调用它。

        Beautify.applyAppearance()

        这是在您的应用程序中同时配置事物外观的好方法。

        【讨论】:

        • 这似乎适用于 光标,但不适用于清除按钮 (x)。
        • 糟糕,我的错。它确实适用于清除按钮的突出显示状态。正常状态保持浅灰色...
        【解决方案6】:

        给你!

        一个 TintTextField。

        不使用自定义图像,或添加按钮等。

        class TintTextField: UITextField {
        
             var tintedClearImage: UIImage?
        
             required init(coder aDecoder: NSCoder) {
               super.init(coder: aDecoder)
               self.setupTintColor()
             }
        
             override init(frame: CGRect) {
               super.init(frame: frame)
               self.setupTintColor()
             }
        
             func setupTintColor() {
               self.borderStyle = UITextField.BorderStyle.roundedRect
               self.layer.cornerRadius = 8.0
               self.layer.masksToBounds = true
               self.layer.borderColor = self.tintColor.cgColor
               self.layer.borderWidth = 1.5
               self.backgroundColor = .clear
               self.textColor = self.tintColor
             }
        
            override func layoutSubviews() {
                super.layoutSubviews()
                self.tintClearImage()
            }
        
            private func tintClearImage() {
                for view in subviews {
                    if view is UIButton {
                        let button = view as! UIButton
                        if let image = button.image(for: .highlighted) {
                            if self.tintedClearImage == nil {
                                tintedClearImage = self.tintImage(image: image, color: self.tintColor)
                            }
                            button.setImage(self.tintedClearImage, for: .normal)
                            button.setImage(self.tintedClearImage, for: .highlighted)
                        }
                    }
                }
            }
        
            private func tintImage(image: UIImage, color: UIColor) -> UIImage {
                let size = image.size
        
                UIGraphicsBeginImageContextWithOptions(size, false, image.scale)
                let context = UIGraphicsGetCurrentContext()
                image.draw(at: .zero, blendMode: CGBlendMode.normal, alpha: 1.0)
        
                context?.setFillColor(color.cgColor)
                context?.setBlendMode(CGBlendMode.sourceIn)
                context?.setAlpha(1.0)
        
                let rect = CGRect(x: CGPoint.zero.x, y: CGPoint.zero.y, width: image.size.width, height: image.size.height)
                UIGraphicsGetCurrentContext()?.fill(rect)
                let tintedImage = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
        
                return tintedImage ?? UIImage()
            }
         }
        

        【讨论】:

        • 看起来很有希望。能否提供一个 Objective-C 版本?
        • 如果我心情好,我可以阅读 Objective-C,但相信我,你不会希望看到我写 Objective-C。对 iOS 有点陌生,并与 Swift 一起加入了火车。 :)
        • @David - 很好。我从另一个实验室项目中复制粘贴了一些代码,我希望在其中对比例进行硬编码。我在这里更新了代码。
        • 因为问题要求一种不使用自定义图像的方式(我认为这是首选)。我不知道是否有一个不那么“hacky”的解决方案来实现这一点?
        • 在 iOS 11 中不再适用。此解决方案中的 Button alpha 错误
        【解决方案7】:

        想法是按键clearButton获取清除按钮,然后用alwaysTemplate模式重新渲染清除图像。

        [斯威夫特 4.2] 刚刚在这里为UITextField 做了一个扩展:

        extension UITextField {
            var clearButton: UIButton? {
                return value(forKey: "clearButton") as? UIButton
            }
        
            var clearButtonTintColor: UIColor? {
                get {
                    return clearButton?.tintColor
                }
                set {
                    let image =  clearButton?.imageView?.image?.withRenderingMode(.alwaysTemplate)
                    clearButton?.setImage(image, for: .normal)
                    clearButton?.tintColor = newValue
                }
            }
        }
        

        但此解决方案的问题在于,在您调用设置色调颜色时,清除按钮的图像是 nil

        所以每个人都在使用RxSwift 来观察图像中的清除按钮。

        import RxSwift
        
        extension UITextField {
            var clearButton: UIButton? {
                return value(forKey: "clearButton") as? UIButton
            }
        
            var clearButtonTintColor: UIColor? {
                get {
                    return clearButton?.tintColor
                }
                set {
                    _ = rx.observe(UIImage.self, "clearButton.imageView.image")
                        .takeUntil(rx.deallocating)
                        .subscribe(onNext: { [weak self] _ in
                            let image = self?.clearButton?.imageView?.image?.withRenderingMode(.alwaysTemplate)
                            self?.clearButton?.setImage(image, for: .normal)
                        })
                    clearButton?.tintColor = newValue
                }
            }
        }
        

        【讨论】:

        • 如果您不想要自定义图像,这就是要走的路。
        • for key "clearButton" 适用于 ios 14,但不适用于 ios 12.4 .. 它返回 nil 您可以查看我的解决方案以获取您的建议 stackoverflow.com/questions/50383723/…
        【解决方案8】:

        我尝试了很多答案,直到找到基于@Mikael Hellman 解决方案的解决方案。此解决方案使用 Swift 4.2

        思路是一样的:

        不使用自定义图像,或添加按钮等。

        并使用扩展 UITextField 的自定义类。

        class TintClearTextField: UITextField {
        
            private var updatedClearImage = false
        
            override func layoutSubviews() {
                super.layoutSubviews()
                tintClearImage()
            }
        
            private func tintClearImage() {
                if updatedClearImage { return }
        
                if let button = self.value(forKey: "clearButton") as? UIButton,
                    let image = button.image(for: .highlighted)?.withRenderingMode(.alwaysTemplate) {
                    button.setImage(image, for: .normal)
                    button.setImage(image, for: .highlighted)
                    button.tintColor = .white
        
                    updatedClearImage = true
                }
            }
        }
        

        您不需要updatedClearImage,但请记住,您将在每个字符添加中执行所有逻辑。

        我什至不需要设置tintColor 来获得我想要的结果。在设置为您的颜色之前尝试注释该行。

        如果它看起来不像您想要的,请将 .white 更改为您想要的颜色,仅此而已。

        PS.:我的初始屏幕中已经填充了一个字段,对于这个只有一个字段,tintColor 的颜色变化发生在显示默认项目颜色后的毫秒数,如“故障”。我无法让它变得更好,但由于我没有使用tintColor,这对我来说没问题。

        希望对你有帮助:)

        【讨论】:

          【解决方案9】:

          详情

          • Xcode 版本 10.1 (10B61)
          • 斯威夫特 4.2

          解决方案

          import UIKit
          
          extension UISearchBar {
          
              func getTextField() -> UITextField? { return value(forKey: "searchField") as? UITextField }
          
              func setClearButton(color: UIColor) {
                  getTextField()?.setClearButton(color: color)
              }
          }
          
          extension UITextField {
          
              private class ClearButtonImage {
                  static private var _image: UIImage?
                  static private var semaphore = DispatchSemaphore(value: 1)
                  static func getImage(closure: @escaping (UIImage?)->()) {
                      DispatchQueue.global(qos: .userInteractive).async {
                          semaphore.wait()
                          DispatchQueue.main.async {
                              if let image = _image { closure(image); semaphore.signal(); return }
                              guard let window = UIApplication.shared.windows.first else { semaphore.signal(); return }
                              let searchBar = UISearchBar(frame: CGRect(x: 0, y: -200, width: UIScreen.main.bounds.width, height: 44))
                              window.rootViewController?.view.addSubview(searchBar)
                              searchBar.text = "txt"
                              searchBar.layoutIfNeeded()
                              _image = searchBar.getTextField()?.getClearButton()?.image(for: .normal)
                              closure(_image)
                              searchBar.removeFromSuperview()
                              semaphore.signal()
                          }
                      }
                  }
              }
          
              func setClearButton(color: UIColor) {
                  ClearButtonImage.getImage { [weak self] image in
                      guard   let image = image,
                              let button = self?.getClearButton() else { return }
                      button.imageView?.tintColor = color
                      button.setImage(image.withRenderingMode(.alwaysTemplate), for: .normal)
                  }
              }
          
              func getClearButton() -> UIButton? { return value(forKey: "clearButton") as? UIButton }
          }
          

          完整样本

          class ViewController: UIViewController {
          
              override func viewDidLoad() {
                  super.viewDidLoad()
                  let textField = UITextField(frame: CGRect(x: 20, y: 20, width: 200, height: 44))
                  view.addSubview(textField)
                  textField.backgroundColor = .lightGray
                  textField.clearButtonMode = .always
                  textField.setClearButton(color: .red)
          
                  let searchBar = UISearchBar(frame: CGRect(x: 20, y: 80, width: 200, height: 44))
                  view.addSubview(searchBar)
                  searchBar.backgroundColor = .lightGray
                  searchBar.setClearButton(color: .red)
              }
          }
          

          结果

          【讨论】:

            【解决方案10】:

            创建这个方法。

            func configureClearButtonColor() {
            
                guard let clearButton = textField.value(forKey: "_clearButton") as? UIButton else {
                    return
                }
                let templateImage = clearButton.imageView?.image?.withRenderingMode(.alwaysTemplate)
                clearButton.setImage(templateImage, for: .normal)
                clearButton.setImage(templateImage, for: .highlighted)
                clearButton.tintColor = .white
            }
            

            并实现你的UITextFieldDelegate,在textFieldDidEndEditing调用方法。在创建一些文本之前更改您的图像。

            func textFieldDidEndEditing(_ textField: UITextField) {
                configureClearButtonColor()
            }
            

            【讨论】:

              【解决方案11】:

              您可以使用自定义图标,它适用于 iOS 11,

              searchBar.setImage(UIImage(named: "ic_clear"), for: .clear, state: .normal)
              

              【讨论】:

                【解决方案12】:

                对于 Swift 4,将其添加到 UITextField 的子类中:

                import UIKit
                
                class CustomTextField: UITextField {
                
                    override func layoutSubviews() {
                        super.layoutSubviews()
                
                        for view in subviews {
                            if let button = view as? UIButton {
                                button.setImage(button.image(for: .normal)?.withRenderingMode(.alwaysTemplate), for: .normal)
                                button.tintColor = .white
                            }
                        }
                    }
                }
                

                【讨论】:

                • tintColor 没有继承吗?
                • 在 iOS 12 中不适合我。清除按钮没有改变颜色。
                • @ihunath,是的,但是清除按钮不是,所以你不能直接访问它,你必须遍历子视图树。
                【解决方案13】:

                Swift 4,简洁明了的子类

                import UIKit
                
                class CustomTextField: UITextField {
                    override func layoutSubviews() {
                        super.layoutSubviews()
                        for view in subviews where view is UIButton {
                            (view as! UIButton).setImage(<MY_UIIMAGE>, for: .normal)
                        }
                    }
                }
                

                【讨论】:

                  【解决方案14】:

                  Swift 4,这对我有用(将 tintColor 更改为您自己的颜色):

                  var didSetupWhiteTintColorForClearTextFieldButton = false
                  
                  private func setupTintColorForTextFieldClearButtonIfNeeded() {
                      // Do it once only
                      if didSetupWhiteTintColorForClearTextFieldButton { return }
                  
                      guard let button = yourTextField.value(forKey: "_clearButton") as? UIButton else { return }
                      guard let icon = button.image(for: .normal)?.withRenderingMode(.alwaysTemplate) else { return }
                      button.setImage(icon, for: .normal)
                      button.tintColor = .white
                      didSetupWhiteTintColorForClearTextFieldButton = true
                  }
                  
                  override func viewDidLayoutSubviews() {
                      super.viewDidLayoutSubviews()
                      setupTintColorForTextFieldClearButtonIfNeeded()
                  }
                  

                  需要在viewDidLayoutSubviews() 中调用它,以便确保它最终被调用,因为clearButtonMode 的情况不同(alwayswhileEditing 等)。我相信这些按钮是懒惰地创建的。所以在viewDidLoad() 中调用它大部分都不起作用。

                  【讨论】:

                    【解决方案15】:

                    你可以使用我的库 LSCategories 来做到这一点:

                    [textField lsSetClearButtonWithColor:[UIColor redColor] mode:UITextFieldViewModeAlways];
                    

                    它不使用任何私有 api,它不搜索 UITextField 子视图层次结构中的原始 UIButton,并且它不需要将 UITextField 子类化为这里的一些其他答案。相反,它使用 rightView 属性来模拟系统清除按钮,因此如果 Apple 更改某些内容,您无需担心它会在未来停止工作。它也适用于 Swift。

                    【讨论】:

                      【解决方案16】:

                      上面马特发布的答案是正确的。 UITextField 中的清除按钮如果未显示则不存在。可以在UITextField 执行其 layoutSubviews 并检查按钮是否存在后立即尝试访问它。 最简单的方法是继承 UITextField,覆盖 layoutSubviews,如果按钮是第一次显示,则存储它的原始图像以供以后使用,然后在任何后续显示期间应用色调。

                      下面我将向您展示如何使用扩展来执行此操作,因为这样您就可以将自定义色调应用到任何 UITextField,包括嵌套在 UISearchBar 等现成类中的那些。

                      玩得开心,喜欢的话就点个赞吧:)

                      Swift 3.2

                      这是主要的扩展:

                      import UIKit
                      
                      extension UITextField {
                      
                          private struct UITextField_AssociatedKeys {
                              static var clearButtonTint = "uitextfield_clearButtonTint"
                              static var originalImage = "uitextfield_originalImage"
                          }
                      
                          private var originalImage: UIImage? {
                              get {
                                  if let cl = objc_getAssociatedObject(self, &UITextField_AssociatedKeys.originalImage) as? Wrapper<UIImage> {
                                      return cl.underlying
                                  }
                                  return nil
                              }
                              set {
                                  objc_setAssociatedObject(self, &UITextField_AssociatedKeys.originalImage, Wrapper<UIImage>(newValue), .OBJC_ASSOCIATION_RETAIN)
                              }
                          }
                      
                          var clearButtonTint: UIColor? {
                              get {
                                  if let cl = objc_getAssociatedObject(self, &UITextField_AssociatedKeys.clearButtonTint) as? Wrapper<UIColor> {
                                      return cl.underlying
                                  }
                                  return nil
                              }
                              set {
                                  UITextField.runOnce
                                  objc_setAssociatedObject(self, &UITextField_AssociatedKeys.clearButtonTint, Wrapper<UIColor>(newValue), .OBJC_ASSOCIATION_RETAIN)
                                  applyClearButtonTint()
                              }
                          }
                      
                          private static let runOnce: Void = {
                              Swizzle.for(UITextField.self, selector: #selector(UITextField.layoutSubviews), with: #selector(UITextField.uitextfield_layoutSubviews))
                          }()
                      
                          private func applyClearButtonTint() {
                              if let button = UIView.find(of: UIButton.self, in: self), let color = clearButtonTint {
                                  if originalImage == nil {
                                      originalImage = button.image(for: .normal)
                                  }
                                  button.setImage(originalImage?.tinted(with: color), for: .normal)
                              }
                          }
                      
                          func uitextfield_layoutSubviews() {
                              uitextfield_layoutSubviews()
                              applyClearButtonTint()
                          }
                      
                      }
                      

                      这里是上面代码中使用的额外sn-ps:

                      你想以对象方式访问的任何东西的好包装:

                      class Wrapper<T> {
                          var underlying: T?
                      
                          init(_ underlying: T?) {
                              self.underlying = underlying
                          }
                      }
                      

                      用于查找任何类型的嵌套子视图的少数扩展:

                      extension UIView {
                      
                          static func find<T>(of type: T.Type, in view: UIView, includeSubviews: Bool = true) -> T? where T: UIView {
                              if view.isKind(of: T.self) {
                                  return view as? T
                              }
                              for subview in view.subviews {
                                  if subview.isKind(of: T.self) {
                                      return subview as? T
                                  } else if includeSubviews, let control = find(of: type, in: subview) {
                                      return control
                                  }
                              }
                              return nil
                          }
                      
                      }
                      

                      UIImage 的扩展以应用淡色

                      extension UIImage {
                      
                          func tinted(with color: UIColor) -> UIImage? {
                              UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
                              color.set()
                              self.withRenderingMode(.alwaysTemplate).draw(in: CGRect(origin: CGPoint(x: 0, y: 0), size: self.size))
                      
                              let result = UIGraphicsGetImageFromCurrentImageContext()
                              UIGraphicsEndImageContext()
                      
                              return result
                          }
                      
                      }
                      

                      ...最后是 Swizzling 的东西:

                      class Swizzle {
                      
                          class func `for`(_ className: AnyClass, selector originalSelector: Selector, with newSelector: Selector) {
                              let method: Method = class_getInstanceMethod(className, originalSelector)
                              let swizzledMethod: Method = class_getInstanceMethod(className, newSelector)
                              if (class_addMethod(className, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
                                  class_replaceMethod(className, newSelector, method_getImplementation(method), method_getTypeEncoding(method))
                              } else {
                                  method_exchangeImplementations(method, swizzledMethod)
                              }
                          }
                      
                      }
                      

                      【讨论】:

                        【解决方案17】:

                        在 SWIFT 3 中:这对我有用

                            if let clearButton = self.textField.value(forKey: "_clearButton") as? UIButton {
                                // Create a template copy of the original button image
                                let templateImage =  clearButton.imageView?.image?.withRenderingMode(.alwaysTemplate)
                        
                                // Set the template image copy as the button image
                                clearButton.setImage(templateImage, for: .normal)
                                clearButton.setImage(templateImage, for: .highlighted)
                        
                                // Finally, set the image color
                                clearButton.tintColor = .white
                            }
                        

                        【讨论】:

                          【解决方案18】:

                          经过所有的答案和可能性,我找到了这个简单直接的解决方案。

                          -(void)updateClearButtonColor:(UIColor *)color ofTextField:(UITextField *)textField {
                          
                              UIButton *btnClear = [textField valueForKey:@"_clearButton"];
                              UIImage * img = [btnClear imageForState:UIControlStateNormal];
                          
                              if (img) {
                                  UIImage * renderingModeImage = [img imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
                                  [btnClear setImage:renderingModeImage forState:UIControlStateNormal];
                                  //-- Add states you want to update
                                  [btnClear setImage:renderingModeImage forState:UIControlStateSelected];
                              }
                          
                              [btnClear setTintColor:color];
                          }
                          
                          
                          [self updateClearButtonColor:[UIColor whiteColor] ofTextField:self.textField];
                          

                          【讨论】:

                          • 执行 valueForKey:@"_clearButton" 正在使用私有 API,这可能会在未来的 iOS 版本中中断,并可能导致您的应用被 Apple 拒绝。
                          【解决方案19】:

                          这里是 Swift 3 更新的解决方案:

                          extension UITextField {
                              func modifyClearButtonWithImage(image : UIImage) {
                                  let clearButton = UIButton(type: .custom)
                                  clearButton.setImage(image, for: .normal)
                                  clearButton.frame = CGRect(x: 0, y: 0, width: 15, height: 15)
                                  clearButton.contentMode = .scaleAspectFit
                                  clearButton.addTarget(self, action: #selector(self.clear(sender:)), for: .touchUpInside)
                                  self.rightView = clearButton
                                  self.rightViewMode = .whileEditing
                              }
                          
                              func clear(sender : AnyObject) {
                                  self.text = ""
                              }
                          }
                          

                          享受;)

                          【讨论】:

                            【解决方案20】:

                            这对我使用 Objective-C 很有用。我从关于这个主题的其他线程中提取了一些片段并提出了这个解决方案:

                            UIButton *btnClear = [self.textFieldUserID valueForKey:@"clearButton"];
                            [btnClear setImage:[UIImage imageNamed:@"facebookLoginButton"] forState:UIControlStateNormal];
                            

                            【讨论】:

                              【解决方案21】:

                              这可能比评分最高的答案更容易,适用于 iOS 7 及更高版本。

                              @interface MyTextField
                              
                              @end
                              
                              @implementation MyTextField
                              
                              - (void)layoutSubviews {
                                  [super layoutSubviews];
                              
                                  for (UIView *subView in self.subviews) {
                                      if ([subView isKindOfClass:[UIButton class]]) {
                                          UIButton *button = (UIButton *)subView;
                                          [button setImage:[[button imageForState:UIControlStateNormal] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]
                                                  forState:UIControlStateNormal];
                                          button.tintColor = self.tintColor;
                                      }
                                  }
                              }
                              
                              @end
                              

                              【讨论】:

                              • tintColor 没有继承吗?
                              【解决方案22】:

                              您可以使用 KVO 访问清除按钮并对其进行更新:

                                  UIButton *clearButton = [myTextField valueForKey:@"_clearButton"]
                                  if([clearButton respondsToSelector:@selector(setImage:forState:)]){
                              
                                      //ensure that the app won't crash in the future if _clearButton reference changes to a different class instance
                                      [clearButton setImage:[UIImage imageNamed:@"MyImage.png"] forState:UIControlStateNormal];
                              
                                  }
                              

                              注意:此解决方案并非面向未来 - 如果 Apple 更改清除按钮的实现,这将正常停止工作。

                              【讨论】:

                              • 绝对糟糕的主意。我们曾经在 UIAlertViews 上做类似的事情来访问按钮。当苹果改变他们的实施时,所有应用内购买停止工作,我们只剩下数周的“修复工作”。 This solution may not be future proof 是不够的警告 ;)
                              • @James Webster,了解 UITextField 的实现可能会发生变化,但我们正在自定义一个清除按钮 - 不是完全关键的功能。最糟糕的事情是没有自定义清除按钮。
                              • 这不是真的。该应用程序可能会崩溃。如果您在登录表单上有此文本框,用户会突然无法再使用您的应用
                              • @James Webster,这将如何崩溃?如果按钮不存在,valueForKey 将返回 nil,并且可以安全地向 nil 发送消息。 Apple 将不得不用不同的类替换 _clearButton。我将更新我的答案以使用 respondsToSelector 使其 100% 安全
                              • 它有效,但我会说它非常冒险。如果未实现 valueForUndefinedKey,valueForKey 可能会崩溃。例如,如果您尝试在 UIViewController 对象 (NSUnknownKeyException) 上不存在的键上使用它,您可以检查它是否崩溃。似乎对于 UIButton 它当前不会因不存在的键而崩溃,因此 UIButton 可能实现 valueForUndefinedKey 但是我不认为它将来会始终实现它,因此如果Apple更改它,它可能会崩溃。
                              【解决方案23】:

                              基于@Mikael Hellman 的回复,我为Objective-C 准备了UITextField 子类的类似实现。唯一的区别是我允许对 Normal 和 Highlighted 状态使用不同的颜色。

                              .h 文件

                              #import <UIKit/UIKit.h>
                              
                              
                              @interface TextFieldTint : UITextField
                              
                              -(void) setColorButtonClearHighlighted:(UIColor *)colorButtonClearHighlighted;
                              -(void) setColorButtonClearNormal:(UIColor *)colorButtonClearNormal;
                              
                              @end
                              

                              .m 文件

                              #import "TextFieldTint.h"
                              
                              @interface TextFieldTint()
                              
                              @property (nonatomic,strong) UIColor *colorButtonClearHighlighted;
                              @property (nonatomic,strong) UIColor *colorButtonClearNormal;
                              
                              @property (nonatomic,strong) UIImage *imageButtonClearHighlighted;
                              @property (nonatomic,strong) UIImage *imageButtonClearNormal;
                              
                              
                              @end
                              
                              @implementation TextFieldTint
                              
                              
                              -(void) layoutSubviews
                              {
                                  [super layoutSubviews];
                                  [self tintButtonClear];
                              }
                              
                              -(void) setColorButtonClearHighlighted:(UIColor *)colorButtonClearHighlighted
                              {
                                  _colorButtonClearHighlighted = colorButtonClearHighlighted;
                              }
                              
                              -(void) setColorButtonClearNormal:(UIColor *)colorButtonClearNormal
                              {
                                  _colorButtonClearNormal = colorButtonClearNormal;
                              }
                              
                              -(UIButton *) buttonClear
                              {
                                  for(UIView *v in self.subviews)
                                  {
                                      if([v isKindOfClass:[UIButton class]])
                                      {
                                          UIButton *buttonClear = (UIButton *) v;
                                          return buttonClear;
                                      }
                                  }
                                  return nil;
                              }
                              
                              
                              
                              -(void) tintButtonClear
                              {
                                  UIButton *buttonClear = [self buttonClear];
                              
                                  if(self.colorButtonClearNormal && self.colorButtonClearHighlighted && buttonClear)
                                  {
                                      if(!self.imageButtonClearHighlighted)
                                      {
                                          UIImage *imageHighlighted = [buttonClear imageForState:UIControlStateHighlighted];
                                          self.imageButtonClearHighlighted = [[self class] imageWithImage:imageHighlighted
                                                                                                tintColor:self.colorButtonClearHighlighted];
                                      }
                                      if(!self.imageButtonClearNormal)
                                      {
                                          UIImage *imageNormal = [buttonClear imageForState:UIControlStateNormal];
                                          self.imageButtonClearNormal = [[self class] imageWithImage:imageNormal
                                                                                           tintColor:self.colorButtonClearNormal];
                                      }
                              
                                      if(self.imageButtonClearHighlighted && self.imageButtonClearNormal)
                                      {
                                          [buttonClear setImage:self.imageButtonClearHighlighted forState:UIControlStateHighlighted];
                                          [buttonClear setImage:self.imageButtonClearNormal forState:UIControlStateNormal];
                                      }
                                  }
                              }
                              
                              
                              + (UIImage *) imageWithImage:(UIImage *)image tintColor:(UIColor *)tintColor
                              {
                                  UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0);
                                  CGContextRef context = UIGraphicsGetCurrentContext();
                              
                                  CGRect rect = (CGRect){ CGPointZero, image.size };
                                  CGContextSetBlendMode(context, kCGBlendModeNormal);
                                  [image drawInRect:rect];
                              
                                  CGContextSetBlendMode(context, kCGBlendModeSourceIn);
                                  [tintColor setFill];
                                  CGContextFillRect(context, rect);
                              
                                  UIImage *imageTinted  = UIGraphicsGetImageFromCurrentImageContext();
                                  UIGraphicsEndImageContext();
                                  return imageTinted;
                              }
                              @end
                              

                              【讨论】:

                              • 我仍在生产版本中使用它 - 在 iOS 10+ 上没有任何问题
                              • 如何从另一个类中调用这些方法来改变清除按钮的色调?
                              【解决方案24】:

                              您在执行此操作时遇到问题的原因是清除按钮图像未着色。它们只是普通的图像。

                              清除按钮是一个按钮,位于 UITextField 内部。像任何按钮一样,它可以有图像,而且确实如此。特别是,它有 两张 图像:一张用于 Normal 状态,一张用于 Highlighted 状态。 OP 反对的蓝色是突出显示的图像,可以通过在出现清除按钮时运行此代码来捕获它:

                                  let tf = self.tf // the text view
                                  for sv in tf.subviews as! [UIView] {
                                      if sv is UIButton {
                                          let b = sv as! UIButton
                                          if let im = b.imageForState(.Highlighted) {
                                              // im is the blue x
                                          }
                                      }
                                  }
                              

                              一抓起来,你会发现是14x14双分辨率tiff图片,这里是:

                              理论上,您可以将图像更改为不同的颜色,并且可以将其指定为文本视图的清除按钮图像以突出显示状态。但实际上这并不容易做到,因为按钮并不总是存在;当它不存在时,您无法引用它(它不仅是不可见的;它实际上根本不是视图层次结构的一部分,因此无法访问它)。

                              而且,没有 UITextField API 可以自定义清除按钮。

                              因此,最简单的解决方案是建议here:创建一个带有自定义正常和突出显示图像的按钮,并将其作为 UITextField 的rightView 提供。然后将clearButtonMode 设置为从不(因为您使用的是正确的视图)并将rightViewMode 设置为您喜欢的任何值。

                              当然,您必须检测到对此按钮的点击并通过清除文本字段的文本来做出响应;但这很容易做到,留给读者作为练习。

                              【讨论】:

                              • 我不认为我们可以将突出显示的图像的renderingMode更改为UIImageRenderingModeAlwaysTemplate? :)
                              • @PetahChristian 没有效果。试试吧。正如我在开始时所说,这些图像没有被着色。
                              • 点头。 我确实看到了,但不知道这是否取决于控件如何呈现清除按钮。感谢您清除它!您可能认为这对 Apple 来说是一个小问题,但 2013 年的原始雷达 #14970472 仍然开放。
                              • 图片的highlighted状态似乎响应UITextField.appearance().tintColor。正常的总是浅灰色的。
                              • 马特,你有没有透明版的清除按钮?所以我将它添加到我的项目中并将其更改为不同的颜色。
                              猜你喜欢
                              • 2020-03-27
                              • 1970-01-01
                              • 2016-08-15
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 2021-12-22
                              相关资源
                              最近更新 更多