This question already has answers here:
Easy way to disable a UITextField?
(5 answers)
Closed 5 years ago.
I have UITextField and I put a UIButton in the left view of it. I want to disable editing of UITextField while my UIButton response to on click action. I tried textField.isUserInteractionEnabled and also textField.isEnabled but both of them also disable my UIButton. is there any way to do this? my custom UITextField class is like this
import UIKit
#IBDesignable
class UITextFieldWithButton: UITextField, UITextFieldDelegate {
private var happyButton: UIButton = UIButton(type: .system)
#IBInspectable
var buttonText: String {
get {
let string = happyButton.titleLabel!.text!
let start = string.index(string.startIndex, offsetBy: 2)
let end = string.endIndex
return String(happyButton.titleLabel!.text![start..<end])
}
set {
happyButton.setTitle(" " + newValue, for: .normal)
happyButton.sizeToFit()
self.leftView = happyButton
self.leftViewMode = .always
}
}
var isButtonEnable: Bool {
get {
return self.isButtonEnable
}
set {
happyButton.isEnabled = newValue
}
}
var buttonDelegate: UITextFieldWithButtonDelegate?
required override init(frame: CGRect) {
super.init(frame: frame)
delegate = self
happyButton.addTarget(self,action: #selector(pressButton(_:)), for: .touchUpInside)
happyButton.titleLabel?.font = UIFont(name: (font?.fontName)!, size: (font?.pointSize)!)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
delegate = self
happyButton.addTarget(self,action: #selector(pressButton(_:)), for: .touchUpInside)
happyButton.titleLabel?.font = UIFont(name: (font?.fontName)!, size: (font?.pointSize)!)
}
#objc private func pressButton(_ sender: UIButton){
if let click = buttonDelegate {
click.clickOnUITextFieldButton(self)
}
}
}
protocol UITextFieldWithButtonDelegate {
func clickOnUITextFieldButton(_ sender: UITextFieldWithButton)
}
"I tried textField.isUserInteractionEnabled and also textField.isEnabled but both of them also disable my UIButton" :your code is working fine you might have added button behind uitextfield try to move it forward in view hierarchy
Related
I made a subclass of a UIView with a text field and a button which allows to configure if text field is secured via isSecureTextEntry property.
I am using two instances of that view one for setting a password and another one for confirming it in a view controller like this
let passwordTextField = PasswordTextFieldView(placeholder: "New password")
let confirmPasswordTextField = PasswordTextFieldView(placeholder: "Confirm password")
Text field subclass code
final class PasswordTextFieldView: UIView {
lazy var textField: UITextField = {
let textField = UITextField()
textField.textColor = .black
textField.placeholder = placeholder
textField.textAlignment = .left
textField.textContentType = .password
textField.autocorrectionType = .no
textField.isSecureTextEntry = true
return textField
}()
private let securedButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(UIImage(systemName: "eye.slash.fill")?.withRenderingMode(.alwaysTemplate), for: .normal)
button.tintColor = .gray
button.addTarget(self, action: #selector(securedButtonTapped), for: .touchUpInside)
return button
}()
private var isSecured: Bool = true
var placeholder: String
required init(placeholder: String) {
self.placeholder = placeholder
super.init(frame: CGRect.zero)
// Layout views
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func securedButtonTapped() {
isSecured.toggle()
securedButton.setImage(UIImage(systemName: isSecured ? "eye.slash.fill" : "eye.fill"), for: .normal)
textField.isSecureTextEntry = isSecured
}
}
So the problem is, that tapping a button changes isSecureTextEntry on a textField which is being edited. How to fix it?
When you create your let variable with a self calling block, you shouldn't use self as it's not determined at that moment. You can add print to see it's value, it's not your view, because the view is not yet created.
Sometimes it'll be correct value at the end(I tried running your code and it run fine on my simulator), and sometimes it's not.
You have two options in such cases:
Replace let with lazy var. In this case self if always gonna be determined
private lazy var securedButton: UIButton = {
...
}
Move adding target to your init. If you're using many inits don't forget to add to all of them, preferably moving to an other function. I prefer this way to make sure all my props are let if I don't need to change it. Like this:
required init(placeholder: String) {
self.placeholder = placeholder
super.init(frame: CGRect.zero)
initialize()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
initialize()
// I know you're not using this initializer, just in case you'd need in future
}
private func initialize() {
securedButton.addTarget(self, action: #selector(securedButtonTapped), for: .touchUpInside)
}
so this question is super simple, but i checked that like a 100 times but it still won't work.
Basically, when user taps on tableView cell, it open another VC with different views, depending whether or not user is owner of post.
First condition, works just fine, adding target to button, while when second is being executed, nothing happens
lazy var buttonsView = DetailButtonsView() // those are almost the same
lazy var addvertView = AdvertiseView()
// inside of buttonsView()
lazy var skipButton: UIButton = {
let button = UIButton()
button.setTitle("Пожаловаться", for: .normal)
button.setTitleColor(.mainColor, for: .normal)
button.titleLabel?.font = .getPoppinsMediumFont(on: 15)
return button
}()
override init(frame: CGRect) {
super.init(frame: .zero)
setupView() //set up constraints
}
//inside of AdvertiseView()
lazy var blackButton:UIButton = {
var button = UIButton()
button.layer.cornerRadius = 8
button.backgroundColor = .black
return button
}()
override init(frame: CGRect) {
super.init(frame: .zero)
setupView() //set up constraints
}
//
func setUpBottom() -> Void {
if dataInfo!.user_id! == UserSettings.userModel.id
{
self.backView.addSubview(addvertView) //also works
addvertView.snp.makeConstraints //works
{
(make) in
make.left.equalToSuperview()
make.top.equalTo(userView.snp.bottom).offset(24)
make.height.equalTo(450)
make.bottom.lessThanOrEqualTo(-34)
}
addvertView.blackButton.addTarget(self, action: #selector(blackButtonMethod), for: .touchUpInside) // does not add target
}
else {
backView.addSubview(buttonsView) //works
buttonsView.snp.makeConstraints { (make) in
make.left.right.equalToSuperview()
make.top.equalTo(userView.snp.bottom).offset(24)
make.bottom.lessThanOrEqualTo(-34)
}
buttonsView.skipButton.addTarget(self, action: #selector(toComplain), for: .touchUpInside) //works
}
#objc func toComplain(){ //works
let vc = ComplaintTypeViewController()
vc.advertID = dataInfo!.id!
navigationController?.pushViewController(vc, animated: true)
}
#objc func blackButtonMethod(){ //does not work
print("hello")
parameters["action"] = "hot"
parameters["advert_id"] = String(describing: dataInfo!.id)
updateAdvert(parameters: parameters)
}
The target is probably added, but you cannot interact with it. That usually happens when constraints/frame is not set right. I see that the skip button uses:
button.setTitle("Пожаловаться", for: .normal)
which will infer autolayout width/height.
I don't see black blackButton's autolayout constraints or label set anywhere.
I have a simple subclass of UIButton
class IconButton: UIButton {
init(type: FontAwesomeStyle, icon: FontAwesome, color: UIColor = .black, size: CGFloat = 20) {
super.init(frame: CGRect.zero)
let iconAsText = String.fontAwesomeIcon(name: icon)
let iconText = NSMutableAttributedString(string: iconAsText, attributes: [
NSAttributedString.Key.font: UIFont.fontAwesome(ofSize: size, style: type)
])
setAttributedTitle(iconText, for: .normal)
setTitleColor(color, for: .normal)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The problem I am having is that I would like the button to have the behavior that system buttons have. Specifically when you press and hold the button, it changes color.
let button = UIButton(type: .system)
Since buttonType is a get only property of UIButton and UIButton.init(type: UIButton.ButtonType) is a convenience initailizer, I am not sure how to implement this a subclass.
Still not sure if its possible to replicate the .system button type in a subclass, but the solution for getting the behavior I wanted was the following:
class IconButton: UIButton {
override var isHighlighted: Bool {
willSet(newVal) {
if newVal != isHighlighted {
// newVal is true when the button is being held down
}
}
}
// Rest of class...
}
So Im trying to create a UIBarButtonItem with a custom UIView by subclassing it like so.
import UIKit
import SnapKit
class LocationManager: UIBarButtonItem {
let createdView = UIView()
lazy var currentCityLabel: UILabel = {
let currentCityLabel = UILabel()
currentCityLabel.text = "Philadelphia, PA"
guard let customFont = UIFont(name: "NoirPro-SemiBold", size: 20) else {
fatalError("""
Failed to load the "CustomFont-Light" font.
Make sure the font file is included in the project and the font name is spelled correctly.
"""
)
}
currentCityLabel.adjustsFontForContentSizeCategory = true
return currentCityLabel
}()
lazy var downArrow: UIImageView = {
let downArrow = UIImageView()
downArrow.contentMode = .scaleAspectFit
downArrow.image = UIImage(named: "downArrow")
return downArrow
}()
override init() {
super.init()
setupViews()
}
#objc func setupViews(){
customView = createdView
createdView.addSubview(currentCityLabel)
currentCityLabel.snp.makeConstraints { (make) in
make.left.equalTo(createdView.snp.left)
make.top.bottom.equalTo(createdView)
}
createdView.addSubview(downArrow)
downArrow.snp.makeConstraints { (make) in
make.left.equalTo(currentCityLabel.snp.right).offset(5)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
However, when I create it and assign it in my viewController I see nothing
import UIKit
class ViewController: UICollectionViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
#objc func setupViews(){
guard let collection = collectionView else {
return
}
collection.backgroundColor = .white
let customLeftBar = LocationManager()
self.navigationController?.navigationItem.leftBarButtonItem = customLeftBar
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I've looked at other post and none seem to quite match my situation. I'm beginning to think it is because I didn't give the UIView a frame but I am not exactly sure how I would do that in this instance if that is the case. Anyone see anything I don't that could potentially help me solve this problem. Also setting a target doesn't work I tried two different ways and none of them triggers a thing
#objc func setupBarButtonItems(){
let customLeftBar = LocationManager()
customLeftBar.action = #selector(self.leftBarPressed)
customLeftBar.target = self
customLeftBar.customView?.isUserInteractionEnabled = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.leftBarPressed))
customLeftBar.customView?.addGestureRecognizer(tapGesture)
navigationItem.leftBarButtonItem = customLeftBar
}
#objc func leftBarPressed(){
print("left bar tapped")
}
Change your adding line from
self.navigationController?.navigationItem.leftBarButtonItem = customLeftBar
to
self.navigationItem.leftBarButtonItem = customLeftBar
When add the barItem, you need to add it via navigationItem of the ViewController, not NavigationController
EDITED for add the action
Your custom UIBarButtonItem is a Custom View's BarButtonItem, so the target and selector will not working.
You can add your custom action by adding a button into your customView, and send the action via closure
Init your closure
var didSelectItem: (() -> Void)?
Add the create button code in your #objc func setupViews()
let button = UIButton(type: .custom)
createdView.addSubview(button)
button.snp.makeConstraints { (maker) in
maker.top.bottom.leading.trailing.equalTo(createdView)
}
// button.backgroundColor = UIColor.cyan // uncomment this line for understand about the barbuttonitem's frame
button.addTarget(self, action: #selector(didTap(_:)), for: .touchUpInside)
and add the function
#objc func didTap(_ button: UIButton) {
print("Did tap button")
}
In your viewController, you can get the tap action by
customLeftBar.didSelectItem = { [weak self] in
self?.leftBarPressed()
}
Unfortunately, your barbuttonitem's default frame is 30x30, so you must be set the frame for your barbuttonitem. If not, you can only catch the tap action in 30x30 area (uncomment the code for see it)
I have a xib UIView that contains a TextField. I want my TextField to gain focus when the Parent UIView get touched. What's the updated proper way to do this using Swift 3+? And is it possible to do this entirely from the storyboard? (I'm currently on Xcode 9 beta, but earlier solution are welcome)
Here is my code:
import UIKit
#IBDesignable
open class MinimalEditView: UIView, UITextFieldDelegate {
#IBOutlet var textField: UITextField!
#IBInspectable
public var cornerRadius: CGFloat = 2.0 {
didSet {
self.layer.cornerRadius = self.cornerRadius
}
}
#IBInspectable
public var borderWidth: CGFloat = 2.0 {
didSet {
self.layer.borderWidth = self.borderWidth
}
}
#IBInspectable
public var borderColor: UIColor = UIColor.intactBeigeLight {
didSet {
self.layer.borderColor = self.borderColor.cgColor
}
}
public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
self.textField.becomeFirstResponder()
return true
}
public override init(frame: CGRect) {
super.init(frame: frame)
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
First, in your viewDidLoad add a gesture recognizer to detect a tap action on your view. The view should be placed on top of your text field
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(viewTapped))
view.isUserInteractionEnabled = true
view.addGestureRecognizer(tapGestureRecognizer)
Then whenever your view is tapped, assign focus to the text field
func viewTapped()
{
self.textField.becomeFirstResponder()
}
You can add UITapGestureRecognizer on your parent view. Handle the tap gesture, as whenever your view is tapped ,assign focus to your textfield.
In your awakefromnib add UITapGestureRecognizer as
let tap = UITapGestureRecognizer(target: self, action: #selector(viewTapped))
yourParentView.isUserInteractionEnabled = true
yourParentView.addGestureRecognizer(tap)
Handle tap as
func handleTap(_ gesture: UITapGestureRecognizer){
{
self.yourTextField.becomeFirstResponder()
}