Travis Griggs
Travis Griggs

Reputation: 22282

Put two (side by side) buttons as a right side overlay of an UITextField

(Swift3/iOS10)

I'm trying to put two buttons (save/cancel) inside of a UITextField using the rightView property. My basic setup code (called from viewDidLoad) is as follows:

private func accesorizeRenameField() {
    let saveButton = UIButton(type: .system)
    saveButton.setImage(#imageLiteral(resourceName: "buttonCheckmark"), for: .normal)
    saveButton.addTarget(self, action: #selector(renameSave), for: .touchUpInside)
    saveButton.tintColor = UIColor(white: 0.15, alpha: 1)

    let cancelButton = UIButton(type: .system)
    cancelButton.setImage(#imageLiteral(resourceName: "buttonX"), for: .normal)
    cancelButton.addTarget(self, action: #selector(renameCancel), for: .touchUpInside)
    cancelButton.tintColor = UIColor(227, 34, 60, 1)

    let container = UIView()
    container.addSubview(saveButton)
    container.addSubview(cancelButton)
    container.translatesAutoresizingMaskIntoConstraints = false
    container.backgroundColor = UIColor.cyan

    saveButton.centerYAnchor.constraint(equalTo: container.centerYAnchor).isActive = true
    cancelButton.centerYAnchor.constraint(equalTo: container.centerYAnchor).isActive = true
    saveButton.heightAnchor.constraint(equalTo: container.heightAnchor).isActive = true
    cancelButton.heightAnchor.constraint(equalTo: container.heightAnchor).isActive = true
    saveButton.leadingAnchor.constraint(equalTo: container.leadingAnchor).isActive = true
    saveButton.trailingAnchor.constraint(equalTo: container.centerXAnchor).isActive = true
    cancelButton.leadingAnchor.constraint(equalTo: container.centerXAnchor).isActive = true
    cancelButton.trailingAnchor.constraint(equalTo: container.trailingAnchor).isActive = true

    self.renameField.rightView = container
    self.renameField.rightViewMode = .always
}

Later on, when I expose the rename field through some animation, I call

self.renameField.rightView?.bounds = CGRect(x: 0, y: 0, width: self.renameField.bounds.height * 2, height: self.renameField.bounds.height)
self.renameField.rightView?.setNeedsLayout()
"rightView.frame \(self.renameField.rightView?.frame)".print()
self.renameField.rightView?.subviews.enumerated().forEach() {index,child in
    "child \(index) frame \(child.frame)".print()
}

However, I cannot get the buttons to show up. The cyan background (for debugging) shows up, but actually is square (it should be a 2:1 rectangle). The print shows that the subviews have frames of size 0. What is the right/idiomatic way to do this? I feel like I'm just throwing hammers here...

Upvotes: 0

Views: 561

Answers (1)

Satachito
Satachito

Reputation: 5888

Create UIButton as type of .custom instead of .system.

And as you already know renameField's height in viewDidLoad, you don't need to bother with constraint.

The code below is enough.

override func
viewDidLoad() {
    super.viewDidLoad()

    let wSize = renameField.frame.size.height - 2

    let saveButton = UIButton( type: .custom )
    saveButton.setImage( #imageLiteral(resourceName: "buttonCheckmark"), for: .normal )
    saveButton.addTarget( self, action: #selector(renameSave), for: .touchUpInside )
    saveButton.frame = CGRect( x: 0, y: 0, width: wSize, height: wSize )

    let cancelButton = UIButton( type: .custom )
    cancelButton.setImage( #imageLiteral(resourceName: "buttonX"), for: .normal )
    cancelButton.addTarget( self, action: #selector(renameCancel), for: .touchUpInside )
    cancelButton.frame = CGRect( x: wSize, y: 0, width: wSize, height: wSize )

    let wV = UIView()
    wV.frame = CGRect( x:0, y:0, width: wSize * 2, height: wSize )
    wV.addSubview( saveButton )
    wV.addSubview( cancelButton )

    renameField.rightView = wV;
    renameField.rightViewMode = .always;
}

Upvotes: 2

Related Questions