DeckyFx
DeckyFx

Reputation: 1308

Custom Clear Button

I want to create custom clear button on UITextField, that is to use rightView and put image there, the problem is attaching the original clear button event to that custom rightView.

In Objective-C i can do that this way:

SEL clearButtonSelector = NSSelectorFromString(@"clearButton");
// Reference clearButton getter
IMP clearButtonImplementation = [self methodForSelector:clearButtonSelector];
// Create function pointer that returns UIButton from implementation of method that contains clearButtonSelector
UIButton * (* clearButtonFunctionPointer)(id, SEL) = (void *)clearButtonImplementation;
// Set clearTextFieldButton reference to “clearButton” from clearButtonSelector
UIButton *_clearTextFieldButton = clearButtonFunctionPointer(self, clearButtonSelector);
[_clearTextFieldButton setImage:[UIImage imageNamed:@"icon_remove"] forState:UIControlStateNormal];
self.hasClearButtonAsRightView = YES;

now how to convert this to Swift? or any ideas to workaround it?

Upvotes: 7

Views: 15540

Answers (6)

Ali Ihsan URAL
Ali Ihsan URAL

Reputation: 1974

For rigth padding & listen the clear delegate of textfield

class SearchBoxTextField: UITextField {

override open func awakeFromNib() {
    super.awakeFromNib()
    self.initialize()
}

override init(frame: CGRect) {
    super.init(frame: frame)
    self.initialize()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

func initialize() {
    let clearButton = UIButton(frame: CGRect(x: 0, y: 0, width: 12, height: 12))
    clearButton.setImage(UIImage(named: "removeIcon")!, for: .normal)
    let clearView = UIView(frame: CGRect(x: 0, y: 0, width: 22, height: 12))
    clearView.addSubview(clearButton)
    
    self.rightView = clearView
    clearButton.addTarget(self, action: #selector(clearClicked), for: .touchUpInside)
    
    self.clearButtonMode = .never
    self.rightViewMode = .whileEditing
}

@objc func clearClicked(sender:UIButton) {
    self.text = ""
    _ = self.delegate?.textFieldShouldClear?(self)
}

}

Upvotes: 0

SBlincov
SBlincov

Reputation: 381

Updated to Swift 5, based on @marmoy answer:

public func addClearAllCustomButton() {
    clearButtonMode = .never
    rightViewMode = .whileEditing
    
    let clearButton = UIButton(frame: rightViewRect(forBounds: bounds))
    clearButton.setImage(UIImage(named: "clearAll"), for: .normal)
    clearButton.addTarget(self, action: #selector(didTouchClearAllButton(sender:)), for: .touchUpInside)

    rightView = clearButton
}

public func removeClearAllButton() {
    rightViewMode = .never
}

@objc func didTouchClearAllButton(sender: UIButton) {
    text = ""
}

Upvotes: 0

subodh1989
subodh1989

Reputation: 716

with iOS 14, none of the solution were working for me. the clear button was getting wrong offset for different device sizes. I had the image. if you dont have it, you can download it from SF Symbols. the name is xmark.circle.fill In the end, I used this

        let customClearButton = UIButton.appearance(whenContainedInInstancesOf: [UITextField.self])
        customClearButton.setImage(UIImage(named: "icon-x"), for: .normal)

Upvotes: 0

rakeshbs
rakeshbs

Reputation: 24572

You can add a custom button as right view of the UITextField like this

class CustomTextField : UITextField
{
    override init(frame: CGRect) {
        super.init(frame: frame)

        let clearButton = UIButton(frame: CGRect(origin: .zero, size: CGSize(width: 15, height: 15))
        clearButton.setImage(UIImage(named: "clear.png")!, forState: UIControlState.Normal)

        self.rightView = clearButton
        clearButton.addTarget(self, action: "clearClicked:", forControlEvents: .touchUpInside)

        self.clearButtonMode = .never
        self.rightViewMode = .always
    }

    func clearClicked(sender: UIButton)
    {
        self.text = ""
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

Upvotes: 17

Marmoy
Marmoy

Reputation: 8079

Implementing a custom text field as suggested in the other answers is not a good idea. You should try to use extensions rather than inheritance if at all possible, because with inheritance you are much more likely to need to make major changes to your codebase in response to changes, whereas using extensions you are much more flexible to change.

I strongly suggest that instead of implementing a custom text field, you extend the UITextField class like this:

extension UITextField {
    func applyCustomClearButton() {
        clearButtonMode = .Never
        rightViewMode   = .WhileEditing

        let clearButton = UIButton(frame: CGRectMake(0, 0, 16, 16))
        clearButton.setImage(UIImage(name: "iCFieldClear")!, forState: .Normal)
        clearButton.addTarget(self, action: "clearClicked:", forControlEvents: .TouchUpInside)

        rightView = clearButton
    }

    func clearClicked(sender:UIButton) {
        text = ""
    }
}

Then to use it you just do this:

yourTextField.applyCustomClearButton()

Upvotes: 11

Andi
Andi

Reputation: 9064

Here is my solution in Swift 3. In addition to the already existing answer, I also made sure that both left and right views of the textfield (i.e. the search magnifier image view and the custom clear button) have a padding to their left/right by overriding leftViewRect() and rightViewRect(). Otherwise, they will stick right on the edges of the textfield.

class CustomTextField: UITextField
{
    fileprivate let searchImageLength: CGFloat = 22
    fileprivate let cancelButtonLength: CGFloat = 15
    fileprivate let padding: CGFloat = 8


    override init( frame: CGRect )
    {
        super.init( frame: frame )
        self.customLayout()
    }

    required init?( coder aDecoder: NSCoder )
    {
        super.init( coder: aDecoder )
        self.customLayout()
    }

    override func leftViewRect( forBounds bounds: CGRect ) -> CGRect
    {
        let x = self.padding
        let y = ( bounds.size.height - self.searchImageLength ) / 2
        let rightBounds = CGRect( x: x, y: y, width: self.searchImageLength, height: self.searchImageLength )
        return rightBounds
    }

    override func rightViewRect( forBounds bounds: CGRect ) -> CGRect
    {
        let x = bounds.size.width - self.cancelButtonLength - self.padding
        let y = ( bounds.size.height - self.cancelButtonLength ) / 2
        let rightBounds = CGRect( x: x, y: y, width: self.cancelButtonLength, height: self.cancelButtonLength )
        return rightBounds
    }

    fileprivate func customLayout()
    {
        // Add search icon on left side
        let searchImageView = UIImageView()
        searchImageView.contentMode = .scaleAspectFit
        let searchIcon = UIImage( named: "search_magnifier" )
        searchImageView.image = searchIcon
        self.leftView = searchImageView
        self.leftViewMode = .always

        // Set custom clear button on right side
        let clearButton = UIButton()
        clearButton.setImage( UIImage( named: "search_cancel" ), for: .normal )
        clearButton.contentMode = .scaleAspectFit
        clearButton.addTarget( self, action: #selector( self.clearClicked ), for: .touchUpInside )
        self.rightView = clearButton
        self.clearButtonMode = .never
        self.rightViewMode = .whileEditing
    }

    @objc fileprivate func clearClicked( sender: UIButton )
    {
        self.text = ""
    }
}

Upvotes: 3

Related Questions