Add a button on right view of UItextfield in such way that, text should not overlap the button - ios

I can add a button to a textfield on the right hand side of the UITextField using the right view however, the text overlaps on the button. Below is the code for right view button
UIView.commitAnimations()
var btnColor = UIButton(type: .Custom)
btnColor.addTarget(self, action: #selector(self.openEmoji), forControlEvents: .TouchUpInside)
btnColor.frame = CGRect(x: CGFloat(textField.frame.size.width - 25), y: CGFloat(5), width: CGFloat(25), height: CGFloat(25))
btnColor.setBackgroundImage(UIImage(named: "send.png"), forState: .Normal)
textField.addSubview(btnColor)
Please let me know how to give padding from right view for text.

use the rightView property of the UITextField. Basically, there are two properties (this one and leftView accordingly) that allow you to place custom views/controls inside the UITextField. You can control whether those views are shown or not by means of rightViewMode/leftViewMode properties:
textField.rightView = btnColor
textField.rightViewMode = .unlessEditing
for e.g
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "send.png"), for: .normal)
button.imageEdgeInsets = UIEdgeInsetsMake(0, -16, 0, 0)
button.frame = CGRect(x: CGFloat(txt.frame.size.width - 25), y: CGFloat(5), width: CGFloat(25), height: CGFloat(25))
button.addTarget(self, action: #selector(self.refresh), for: .touchUpInside)
textField.rightView = button
textField.rightViewMode = .always
and call the action as
#IBAction func refresh(_ sender: Any) {
}

Create UITextField extension and add below method in it and you can change UIButton code as per your requirement.
func setRightViewIcon(icon: UIImage) {
let btnView = UIButton(frame: CGRect(x: 0, y: 0, width: ((self.frame.height) * 0.70), height: ((self.frame.height) * 0.70)))
btnView.setImage(icon, for: .normal)
btnView.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 3)
self.rightViewMode = .always
self.rightView = btnView
}

Correct answer Swift3+
If you just override the methods, the text may overlap the right view. This code completely solves this problem.
You UITextField subclass:
//...
private func setInsets(forBounds bounds: CGRect) -> CGRect {
var totalInsets = insets //property in you subClass
if let leftView = leftView { totalInsets.left += leftView.frame.origin.x }
if let rightView = rightView { totalInsets.right += rightView.bounds.size.width }
return UIEdgeInsetsInsetRect(bounds, totalInsets)
}
override func textRect(forBounds bounds: CGRect) -> CGRect {
return setInsets(forBounds: bounds)
}
override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
return setInsets(forBounds: bounds)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return setInsets(forBounds: bounds)
}
override func rightViewRect(forBounds bounds: CGRect) -> CGRect {
var rect = super.rightViewRect(forBounds: bounds)
rect.origin.x -= insets.right
return rect
}
override func leftViewRect(forBounds bounds: CGRect) -> CGRect {
var rect = super.leftViewRect(forBounds: bounds)
rect.origin.x += insets.left
return rect
}

you can assign button at right view of textfield
var btnColor = UIButton(type: .Custom)
btnColor.addTarget(self, action: #selector(self.openEmoji), forControlEvents: .TouchUpInside)
btnColor.frame = CGRect(x: CGFloat(textField.frame.size.width - 25), y: CGFloat(5), width: CGFloat(25), height: CGFloat(25))
btnColor.setBackgroundImage(UIImage(named: "send.png"), forState: .Normal)
textField.rightView = btnColor

internal var btnDropDown: UIButton {
let size: CGFloat = 25.0
let button = UIButton(type: .custom)
button.setImage(#imageLiteral(resourceName: "DropDownImage"), for: .normal)
button.imageEdgeInsets = UIEdgeInsetsMake(0, -(size/2.0), 0, 0)
button.frame = CGRect(x: self.frame.size.width - size, y: 0.0, width: size, height: size)
return button
}
And I used it like this:
override func awakeFromNib() {
super.awakeFromNib()
textField.rightView = self.btnDropDown
textField.rightViewMode = .always
}

You can achieve same as shown below:
#IBOutlet weak var textField: UITextField!
var textFieldBtn: UIButton {
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "image.png"), for: .normal)
button.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0)
button.frame = CGRect(x: CGFloat(textField.frame.size.width - 40), y: CGFloat(5), width: CGFloat(40), height: CGFloat(30))
button.backgroundColor = UIColor.darkGray
button.addTarget(self, action: #selector(self.refreshContent), for: .touchUpInside)
return button
}
func refreshContent() {
// Add your code here to handle button.
}
Add to textField.
textField.rightView = textFieldBtn
textField.rightViewMode = .always

For Swift4
func setupUI() {
let imgSearch = UIImageView();
let imgSearch = UIImage(named: "search");
// set frame on image before adding it to the uitextfield
imgSearch.image = imagePassword;
imgSearch.frame = CGRect(x: txtSearchField.frame.size.width - 40 , y: 5, width: 22, height: 22)
txtSearchField.rightView = imgSearch
txtSearchField.rightViewMode = .always
}
Call function like this
// MARK: View lifecycle
override func viewDidLoad() {
super.viewDidLoad()
self.setupUI()
}

Related

UIbutton inside UIViewControllers inside UIScrollView not getting triggered on touch

I'v added two UIViewControllers inside of a UIScrollView, and now I'm trying to add targets to the button inside of the UIViewControllers, I've checked the internet but none of those solutions worked for me, this is what I'm doing
inside of the main ViewController
scrollView.contentSize = CGSize(width: view.frame.width * 2, height:0)
let postsViewController = PostsViewController()
postsViewController.scrollView = scrollView
postsViewController.view.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
let chatsViewController = ChatsViewController()
chatsViewController.scrollView = scrollView
chatsViewController.view.frame = CGRect(x: view.frame.width, y: 0, width: view.frame.width, height: view.frame.height)
scrollView.addSubview(postsViewController.view)
scrollView.addSubview(chatsViewController.view)
postsViewController.didMove(toParent: self)
chatsViewController.didMove(toParent: self)
then inside the postsViewController I'm doing this
let a = UIButton()
a.setImage(currentTheme.chat, for: .normal)
a.imageEdgeInsets = UIEdgeInsets(top: 1.5, left: 0.0, bottom: 1.5, right: 0.0)
view.addSubview(a)
view.addConstraint("V:|-200-[v0(80)]-0-|",a)
view.addConstraint("H:|-200-[v0(80)]-0-|",a)
a.addTarget(self, action: #selector(abc), for: .touchUpInside)
the add "addConstraint" is an extension function just to add constraint to views
the abc function
#objc func abc() {
print("hello")
}
you can see the scrollView setting here
https://ibb.co/ZB1LJsy
Try to initialize your button like this
let a : UIButton = UIButton(type: .custom)
And add this after to the initialize of your UIScrollView
scrollView.delaysContentTouches = false
I tested in an xcode project. I can not execute the function (button) in the other UIViewController.
By cons if you sent the #selector it works.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let scrollView : UIScrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.width))
scrollView.delaysContentTouches = false
view.addSubview(scrollView)
let TestViewController = TestViewController1()
TestViewController.select = #selector(demande(sender:))
TestViewController.view.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
scrollView.addSubview(TestViewController.view)
print("dd")
}
#objc func demande(sender: UIButton!) {
print("button click", sender.tag)
}
}
class TestViewController1: UIViewController {
var select : Selector!
override func viewDidLoad() {
super.viewDidLoad()
let button : UIButton = UIButton(type: .custom)
button.setImage(#imageLiteral(resourceName: "turn_off_on_power-512"), for: .normal)
button.tag = 2
button.addTarget(self, action: select, for: .touchUpInside)
button.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
view.addSubview(button)
}
}
Hoping that it helps you
okay, I solved my problem, the reason why the button wasn't getting trigger is because I forgot to attach the viewController of the view to the scrollview, so all I had to do is to attach the viewController as well and it worked.
self.addChild(postsViewController)
self.addChild(chatsViewController)
now it is working.

Get titleLabel during drag and drop

An array of UIButton are generated programmatically. Is it possible to get the titleLabel of the UIButton triggering the drag? Or are there any ways to get info of the UIButton in the drag function?
override func viewDidLoad() {
super.viewDidLoad()
for q in question{
addButton(title:q)
}
}
func addButton(title:String){
var tbutton: UIButton = {
let button = UIButton(frame: CGRect(x: 0, y: 0,
width: buttonWidth,
height: buttonHeight))
button.center = self.view.center
button.layer.cornerRadius = 5
button.layer.masksToBounds = true
button.backgroundColor = UIColor.yellow
button.setTitleColor(.black, for: .normal)
button.setTitle(title, for: .normal)
return button
}()
view.addSubview(tbutton)
tbutton.addTarget(self,
action: #selector(drag(control:event:)),
for: UIControl.Event.touchDragInside)
tbutton.addTarget(self,
action: #selector(drag(control:event:)),
for: [UIControl.Event.touchDragExit,
UIControl.Event.touchDragOutside])
self.buttonArray.append(tbutton)
}
#objc func drag(control: UIControl, event: UIEvent) {
//print(event)
if let center = event.allTouches?.first?.location(in: self.view) {
control.center = center
}
/////////////////////////////////////////
// Get titleLabel of the button here???????????
/////////////////////////////////////////
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton.init(frame: CGRect.init(x: 0, y: 0, width: 100, height: 100))
button.backgroundColor = .yellow
button.center = CGPoint.init(x: view.bounds.width / 2, y: view.bounds.height / 2)
view.addSubview(button)
let pangesture = UIPanGestureRecognizer.init(target: self, action: #selector(ViewController.panning(_:)))
button.addGestureRecognizer(pangesture)
}
#objc func panning(_ panGesture : UIPanGestureRecognizer){
let button = panGesture.view as! UIButton
switch panGesture.state{
case .changed:
button.center = panGesture.location(in: view)
panGesture.setTranslation(.zero, in: view)
default:
break
}
// here you can get the button's properties too.
}
}

How to set the custom inputview of the textview as same height as that of the default keyboard in iOS?

I want the height of custom inputView of the textView to be same height as that of default keyboard. I tried a lot, including:
self.textView.inputView?.autoresizingMask = .flexibleHeight
I couldn't find a solution. It's always coming less than that of default keyboard. (EDIT: This issue exists only for iPhone X)
My code:
class ViewController: UIViewController {
let textView = UITextView()
let button = UIButton()
var keyboardView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
textView.backgroundColor = .lightGray
button.backgroundColor = .red
self.view.addSubview(textView)
self.view.addSubview(button)
}
#objc func buttonAction() {
if self.textView.inputView == nil {
self.textView.inputView = keyboardView
self.textView.inputView?.autoresizingMask = .flexibleHeight
self.textView.reloadInputViews()
} else {
self.textView.inputView = nil
self.textView.reloadInputViews()
}
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
textView.frame = CGRect(x: 0, y: 50, width: self.view.frame.size.width - 50, height: 50)
button.frame = CGRect(x: self.view.frame.size.width - 50, y: 50, width: 50, height: 50)
}
}

UIButton inside a UIView not responding to gesture

I have a UIView with a UIButton inside it but the button does not recognise gestures
Any ideas why? I have tried using the solutions in the questions related to this but have not succeeded.
The whole class code:
import UIKit
import Foundation
public class HUDView: UIView , UIGestureRecognizerDelegate {
var stopwatch: StopwatchView
var gamePoints: CounterLabelView
var hintButton: UIButton!
required public init(coder aDecoder: NSCoder) {
fatalError("Never call this... Use init(frame:)")
}
override init(frame: CGRect) {
self.stopwatch = StopwatchView(frame: CGRect(x: ScreenWidth/2 - 150, y: 0, width: 300, height: 100))
self.stopwatch.setSecondsRemaining(0)
self.gamePoints = CounterLabelView(font: FontHUD!, frame: CGRect(x: ScreenWidth - 200, y: 30, width: 320, height: 70))
gamePoints.textColor = UIColor.black
gamePoints.value = 0
super.init(frame: frame)
self.addSubview(gamePoints)
let pointsLabel = UILabel(frame: CGRect(x: ScreenWidth - 340, y: 30, width: 140, height: 70))
pointsLabel.backgroundColor = UIColor.clear
pointsLabel.font = FontHUD
pointsLabel.text = " Points:"
self.addSubview(pointsLabel)
self.isUserInteractionEnabled = false
self.addSubview(self.stopwatch)
//load the button image
let hintButtonImage = UIImage(named: "btn")!
//the help button
self.hintButton = UIButton()
// hintButton.perform(#selector(HUDView.s))
hintButton.setTitle("Hint!", for:.normal)
hintButton.titleLabel?.font = lHud
hintButton.setBackgroundImage(hintButtonImage, for: .normal)
hintButton.frame = CGRect(x: 50, y: 30, width: hintButtonImage.size.width, height: hintButtonImage.size.height)
// hintButton.center = self.center
//50, 30, hintButtonImage.size.width, hintButtonImage.size.height
hintButton.alpha = 1.0
hintButton.isUserInteractionEnabled = true
self.addSubview(hintButton)
// hintButton.addTarget(self, action: #selector(self.tapButton(_:)), for: .touchUpInside)
let g = UITapGestureRecognizer(target: self, action: #selector(self.h(_:)))
g.delegate = self
hintButton.addGestureRecognizer(g)
}
func h(_ sender: UITapGestureRecognizer) {
print("hey!")
fatalError()
}
}
The problem is in line self.isUserInteractionEnabled = false
You are adding button as a subview to HUDView. The button tap will not work because the parent view ie:HUDView has user interaction disabled and so the user interaction of all the subViews will be disabled.
Make the changes as self.isUserInteractionEnabled = true it will work.

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?
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)
}
}
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()
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 = ""
}
}
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)
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 = ""
}
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)
}
}

Resources