Comment changer la couleur de la teinte du button d'effacement sur un UITextField

J'ai un button d'effacement généré automatiquement sur mon champ UIText, avec la couleur bleue par défaut. Je ne peux pas changer la couleur de la teinte en blanc. J'ai essayé de modifier le storyboard et le code sans succès, et je ne veux pas utiliser une image personnalisée.

Comment puis-je changer la couleur de la teinte du button d'effacement par défaut sans utiliser une image personnalisée?

cliqué

Voici!

Un champTintTextField.

Utilisation d'aucune image personnalisée, ou de buttons ajoutés, etc.

entrez la description de l'image ici

class TintTextField: UITextField { var tintedClearImage: UIImage? required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) setupTintColor() } override init(frame: CGRect) { super.init(frame: frame) setupTintColor() } func setupTintColor() { clearButtonMode = UITextFieldViewMode.WhileEditing borderStyle = UITextBorderStyle.RoundedRect layer.cornerRadius = 8.0 layer.masksToBounds = true layer.borderColor = tintColor.CGColor layer.borderWidth = 1.5 backgroundColor = UIColor.clearColor() textColor = tintColor } override func layoutSubviews() { super.layoutSubviews() tintClearImage() } private func tintClearImage() { for view in subviews as! [UIView] { if view is UIButton { let button = view as! UIButton if let uiImage = button.imageForState(.Highlighted) { if tintedClearImage == nil { tintedClearImage = tintImage(uiImage, tintColor) } button.setImage(tintedClearImage, forState: .Normal) button.setImage(tintedClearImage, forState: .Highlighted) } } } } } func tintImage(image: UIImage, color: UIColor) -> UIImage { let size = image.size UIGraphicsBeginImageContextWithOptions(size, false, image.scale) let context = UIGraphicsGetCurrentContext() image.drawAtPoint(CGPointZero, blendMode: CGBlendMode.Normal, alpha: 1.0) CGContextSetFillColorWithColor(context, color.CGColor) CGContextSetBlendMode(context, CGBlendMode.SourceIn) CGContextSetAlpha(context, 1.0) let rect = CGRectMake( CGPointZero.x, CGPointZero.y, image.size.width, image.size.height) CGContextFillRect(UIGraphicsGetCurrentContext(), rect) let tintedImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return tintedImage } 

La raison pour laquelle vous avez du mal à faire cela est que les images des buttons d'effacement ne sont pas teintées. Ce ne sont que des images normales.

Le button d'effacement est un button interne à UITextField. Comme n'importe quel button, il peut avoir une image, et c'est le cas. En particulier, il a deux images: une pour l'état Normal et une pour l'état Surligné. Le bleu auquel l'OP s'oppose est l'image en surbrillance, et il peut être capturé en exécutant ce code au moment où le button d'effacement est présent:

  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 } } } 

Une fois que vous l'aurez capturé, vous constaterez qu'il s'agit d'une image tiff à double résolution 14×14, et voici:

entrez la description de l'image ici

En théorie, vous pouvez changer l'image en une couleur différente, et vous pouvez l'assigner comme l'image du button d'effacement de la vue de text pour l'état en surbrillance. Mais en pratique ce n'est pas du tout facile à faire, car le button n'est pas toujours présent; vous ne pouvez pas vous y référer quand il est absent (ce n'est pas seulement invisible, il ne fait pas du tout partie de la hiérarchie des vues, donc il n'y a aucun moyen d'y accéder).

De plus, il n'y a pas d'API UITextField pour personnaliser le button d'effacement.

Ainsi, la solution la plus simple est ce qui est conseillé ici : créer un button avec des images normales et en surbrillance personnalisées et le fournir en tant que rightView de rightView . Vous définissez ensuite le clearButtonMode sur Never (puisque vous utilisez la vue correcte à la place) et le rightViewMode sur ce que vous voulez.

entrez la description de l'image ici

Vous devrez, bien sûr, alors détecter un tap sur ce button et répondre en effaçant le text du champ de text; mais cela est facile à faire et rest un exercice pour le lecteur.

En se basant sur la réponse de @Mikael Hellman, j'ai préparé une implémentation similaire de la sous-class UITextField pour Objective-C. La seule différence est que je permets d'avoir des colors séparées pour les états normaux et en surbrillance.

file .h

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

file .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 

Dans Swift, vous pouvez écrire l'extension et l'utiliser sur n'importe quel champ de text de votre projet.

 extension UITextField { 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 } func clear(_ sender : AnyObject) { self.text = "" sendActions(for: .editingChanged) } } 

Vous pouvez utiliser KVO pour accéder au button d'effacement et le mettre à jour:

  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]; } 

Note: Cette solution n'est pas à l'épreuve du time – si Apple change l'implémentation du button d'effacement, cela s'arrêtera gracieusement.

Si vous utilisez UIAppearance dans votre application, vous pouvez définir tintColor pour le button d'effacement lors de l'exécution.

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

Au démarrage, nous appelons une fonction de class dans notre AppDelegate qui a un certain nombre d'autres controls qui ont leur .appearance() configuré en elle.

Supposons que votre class pour définir l'apparence de votre application s'appelle Beautyify vous créez quelque chose comme ceci:

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

Ensuite, à l'intérieur de AppDelegate, didFinishLaunchingWithOptions l'appelle.

Beautify.applyAppearance()

C'est un excellent moyen de configurer l'apparence des choses dans votre application en même time.

Il pourrait être encore plus facile que la réponse la mieux notée, disponible pour iOS 7 et plus tard.

 @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 

Cela a fonctionné pour moi en utilisant l'objective-C. J'ai tiré des morceaux d'autres discussions sur ce sujet et suis venu avec cette solution:

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

Voici la solution mise à jour de 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 = "" } } 

Prendre plaisir 😉

Fonctionne parfaitement dans iOS8

 searchBar.setImage(UIImage(named: "iconSearchBarClear"), forSearchBarIcon: .Clear, state: .Normal) searchBar.setImage(UIImage(named: "iconSearchBarClear"), forSearchBarIcon: .Clear, state: .Highlighted) 

La réponse affichée par matt ci-dessus est correcte. Le button d' UITextField intérieur de UITextField n'existe pas s'il n'est pas affiché. On peut essayer d'y accéder juste après que UITextField effectue ses layoutSubviews et vérifie l'existence du button. L'approche la plus simple consiste à sous- UITextField , à redéfinir layoutSubviews et, si le button est affiché pour la première fois, à stocker ses images originales pour une utilisation ultérieure, puis à appliquer une teinte lors des prochaines présentations.

Ci-dessous, je vous montre comment utiliser l'extension, car vous pouvez ainsi appliquer votre teinte personnalisée à n'importe quel object UITextField, y compris ceux nesteds dans des classs prêtes comme UISearchBar.

Amusez-vous et donnez vos pouces si vous l'aimez 🙂

Swift 3.2

Voici l'extension principale:

 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() } } 

Voici des extraits supplémentaires utilisés dans le code ci-dessus:

Bon emballage pour tout ce que vous voulez accéder aux objects:

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

Une extension pratique pour find des sous-vues nestedes de tout type:

 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 } } 

Extension pour UIImage pour appliquer une couleur de teinte

 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 } } 

… et enfin des trucs 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) } } } 

Après avoir parcouru toutes les réponses et possibilités, j'ai trouvé cette solution simple et directe.

 -(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]; 

Dans SWIFT 3: cela fonctionne pour moi

  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 }