class added dynamically doesn't respond touched in swift - ios

I declare TestClass contain button and textField and constraint it and then add this class to view controller
import Foundation
import UIKit
class TestClass : UIView {
let testButton : UIButton = {
let button = UIButton()
button.setTitle("Test", for: .normal)
button.setTitleColor(.red ,for:.normal)
return button
}()
let testInput : UITextField = {
let input = UITextField()
input.placeholder = "type here"
return input
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
func setupView(){
addSubview(testButton)
addSubview(testInput)
testButton.translatesAutoresizingMaskIntoConstraints = false
testInput.translatesAutoresizingMaskIntoConstraints = false
testButton.topAnchor.constraint(equalTo: self.topAnchor, constant: 20).isActive = true
testButton.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20).isActive = true
testButton.heightAnchor.constraint(equalToConstant: 30).isActive = true
testButton.widthAnchor.constraint(equalToConstant: 50).isActive = true
testInput.topAnchor.constraint(equalTo: self.topAnchor, constant: 70).isActive = true
testInput.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20).isActive = true
testInput.heightAnchor.constraint(equalToConstant: 30).isActive = true
testInput.widthAnchor.constraint(equalToConstant: 100).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
in the viewDidLoad I add this class and it appeared .
but when I tap on textField or button it doesn't respond
class ViewControll : UIViewController {
let newClass : TestClass = {
let view = TestClass()
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(newClass)
newClass.frame.size = CGSize(width: 200, height: 200)
newClass.translatesAutoresizingMaskIntoConstraints = false
newClass.topAnchor.constraint(equalTo: contView.topAnchor, constant: 300).isActive = true
newClass.leadingAnchor.constraint(equalTo: contView.leadingAnchor, constant: 30).isActive = true
newClass.testButton.addTarget(self, action: #selector(prnt), for: .touchUpInside)
}
#objc func prnt(){
print("bla bla bla")
}
}
when I click on button or tap on textField nothing happened
can anyone help me please

Constrains don't define height and width of your TestClass - set frame doesn't work. You need to add constraints for this as well, for example, in viewDidLoad method:
newClass.heightAnchor.constraint(equalToConstant: 200).isActive = true
newClass.widthAnchor.constraint(equalToConstant: 200).isActive = true

Related

Setting UITextField heightAnchor to 0 does not work on iOS 15

I have an iOS application where i have a textfield and a button and on tap of button i have to hide the textfield.
I am setting heightAnchor to 0 on tap of button. Everything is working fine on iOS 14(14.5) but does not work(does not hide the text field) on iOS 15. Also, I have tried setting up the isHidden property on UITextField but it does not work.
Can you please help tell if something changed or i am doing something wrong. Thank you.
Code reference:
import UIKit
class ViewController: UIViewController {
private lazy var mytextFeild: UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.text = "Hello world"
textField.backgroundColor = .green
return textField
}()
private lazy var testView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .systemPink
return view
}()
private lazy var button: UIButton = {
let view = UIButton()
view.backgroundColor = .blue
view.setTitle("hide it", for: .normal)
view.translatesAutoresizingMaskIntoConstraints = false
view.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
return view
}()
var heightConstraint: NSLayoutConstraint?
#objc func buttonTapped() {
heightConstraint?.isActive = false
heightConstraint = mytextFeild.heightAnchor.constraint(equalToConstant: 0)
heightConstraint?.isActive = true
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(mytextFeild)
view.addSubview(testView)
view.addSubview(button)
mytextFeild.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32).isActive = true
mytextFeild.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32.0).isActive = true
mytextFeild.topAnchor.constraint(equalTo: view.topAnchor, constant: 64).isActive = true
heightConstraint = mytextFeild.heightAnchor.constraint(equalToConstant: 32.0)
heightConstraint?.isActive = true
button.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32.0).isActive = true
button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32.0).isActive = true
button.heightAnchor.constraint(equalToConstant: 32.0).isActive = true
button.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -64.0).isActive = true
testView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
testView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
testView.topAnchor.constraint(equalTo: mytextFeild.bottomAnchor).isActive = true
testView.bottomAnchor.constraint(equalTo: button.topAnchor).isActive = true
}
}
Add them to a stack then add stack to the viewController. at the end try to hide it easily without changing the height.
class ViewController: UIViewController {
private lazy var stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fill
stackView.alignment = .center
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
private lazy var myTextField: UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.text = "Hello world"
textField.backgroundColor = .green
return textField
}()
private lazy var testView: UIView = {
let view = UIView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .systemPink
return view
}()
private lazy var button: UIButton = {
let view = UIButton()
view.backgroundColor = .blue
view.setTitle("hide it", for: .normal)
view.translatesAutoresizingMaskIntoConstraints = false
view.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
return view
}()
#objc func buttonTapped() {
myTextField.isHidden = !myTextField.isHidden
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
stackView.addArrangedSubview(myTextField)
stackView.addArrangedSubview(testView)
stackView.addArrangedSubview(button)
view.addSubview(stackView)
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -64.0).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
myTextField.heightAnchor.constraint(equalToConstant: 32.0).isActive = true
myTextField.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -64.0).isActive = true
testView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
button.heightAnchor.constraint(equalToConstant: 32.0).isActive = true
button.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -64.0).isActive = true
}
}

Custom view with textField and button not responding

I have custom view with some labels, a text field and a button. When I add it to a view in a VC, the custom view appears nicely, but I can't tap on the text field or on the button, they are not responding. Can someone tell my what is the problem with the code? Here is the simplified version:
import UIKit
class CustomView: UIView {
let title: UILabel = {
let title = UILabel()
title.font = UIFont.systemFont(ofSize: 24)
title.text = "Title"
title.numberOfLines = 0
title.textAlignment = .center
title.translatesAutoresizingMaskIntoConstraints = false
return title
}()
let textView: UITextField = {
let textView = UITextField()
textView.translatesAutoresizingMaskIntoConstraints = false
textView.placeholder = "Placeholder text"
textView.backgroundColor = UIColor.lightGray
textView.layer.cornerRadius = 10
return textView
}()
let searchButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = UIColor.blue
button.setTitle("Tap me", for: UIControl.State.normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
return button
}()
private func setupView() {
addSubview(title)
addSubview(textView)
addSubview(searchButton)
NSLayoutConstraint.activate([
title.topAnchor.constraint(equalTo: self.topAnchor, constant: 100),
title.rightAnchor.constraint(equalTo: self.rightAnchor),
title.leftAnchor.constraint(equalTo: self.leftAnchor),
])
NSLayoutConstraint.activate([
textView.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 20),
textView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
textView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),
textView.heightAnchor.constraint(equalToConstant: 60)
])
NSLayoutConstraint.activate([
searchButton.topAnchor.constraint(equalTo: textView.bottomAnchor, constant: 20),
searchButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
searchButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),
searchButton.heightAnchor.constraint(equalToConstant: 60)
])
}
#objc func buttonAction(_ sender:UIButton!)
{
print("Button tapped")
}
override init(frame: CGRect) {
super.init(frame: frame)
self.setupView()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
import UIKit
class ViewController: UIViewController {
private let customView = CustomView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(customView)
customView.translatesAutoresizingMaskIntoConstraints = false
customView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
customView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
customView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
}
}
Basically I want to have a seperate file with the view that has all the design and I just want to drop that into the VC without doing anything special with it. Is this even a good approach? Where should I set up the button action? I mean once its tappable...
Thanks!
Try this first - at the end of viewDidLoad(), add this line:
customView.backgroundColor = .red
When you run the app, you'll notice there is no red box.
Now, add this line after that one:
customView.clipsToBounds = true
Run it again, and... we see nothing!
The problem is, you haven't given your customView any height.
To fix it, constrain the bottom of searchButton in your custom view class:
NSLayoutConstraint.activate([
searchButton.topAnchor.constraint(equalTo: textView.bottomAnchor, constant: 20),
searchButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
searchButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),
searchButton.heightAnchor.constraint(equalToConstant: 60),
// add this line!
searchButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20),
])
You need to set userInteractionEnabled to true on your CustomView instance so that it passes through taps.
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(customView)
customView.translatesAutoresizingMaskIntoConstraints = false
customView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
customView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
customView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
customView.userInteractionEnabled = true
}
You can use a delegation pattern or a closure property to pass the button tap event back to the containing view controller.
For example,
class CustomView: UIView {
var searchTappedHandler: ((CustomView)->Void)?
#objc func buttonAction(_ sender:UIButton!)
{
print("Button tapped")
self.searchTappedHandler?(self)
}
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(customView)
customView.translatesAutoresizingMaskIntoConstraints = false
customView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
customView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
customView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
customView.userInteractionEnabled = true
customView.searchTappedHandler = { _ in
print("Search button was tapped")
}
}
Or, if you want to use a function rather than an inline closure
func handleTap(_ customView: CustomView) {
print("tap")
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(customView)
customView.translatesAutoresizingMaskIntoConstraints = false
customView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
customView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
customView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
customView.userInteractionEnabled = true
customView.searchTappedHandler = handleTap
}

addSubview doesn't work in UIView - Swift Programmatically

I need to add a UIButton as a subview on a UIView but it actually doesn't appear at runtime.
This is my code:
let moreButton : UIButton = {
let button = UIButton()
button.setImage(#imageLiteral(resourceName: "more"), for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(moreButton)
moreButton.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
moreButton.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
moreButton.heightAnchor.constraint(equalToConstant: 30).isActive = true
moreButton.widthAnchor.constraint(equalToConstant: 20).isActive = true
}
The button isn't added eventually to the view. I'm sure this is an easy fix but I can't wrap my head around it.
First of all, make sure you the viewController is showing anything since you're doing it without storyboards, check out this simple tutorial:
https://medium.com/better-programming/creating-a-project-without-storyboard-in-2020-and-without-swifui-82080eb6d13b
If the problem is with that UIButton, try to set up the subviews in viewDidLoad:
final class ViewController: UIViewController {
let cardView = CardView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(cardView)
/// Constraints
let margins = view.layoutMarginsGuide
cardView.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
cardView.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
cardView.topAnchor.constraint(equalTo: margins.topAnchor).isActive = true
cardView.heightAnchor.constraint(equalToConstant: 30).isActive = true
cardView.widthAnchor.constraint(equalToConstant: 20).isActive = true
}
}
final class CardView: UIView {
let moreButton : UIButton = {
let button = UIButton()
button.setTitle("Button title", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
override init(frame: CGRect) {
super.init(frame:frame)
self.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(moreButton)
/// Constraints
let margins = self.layoutMarginsGuide
moreButton.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
moreButton.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
moreButton.topAnchor.constraint(equalTo: margins.topAnchor).isActive = true
moreButton.heightAnchor.constraint(equalToConstant: 30).isActive = true
moreButton.widthAnchor.constraint(equalToConstant: 20).isActive = true
}
}
I've set up margins for the constraints and also added a leadingAnchor constraint, that might have been the issue as well.

UILabel TapGesture not firing

Any idea why the Tap gesture on a UI Label will not fire?
I've tried using a delegate also but in its simplest form, for some reason it will just not hit the action method.
Is the UIView layer restricting this interaction?
class TestTextViewLabel : UIView {
weak var testTextView: UITextView!
weak var testLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(testLabelTapped(_:)))
let testUITextView: UITextView = {
let textView = UITextView()
textView.textColor = UIColor(hex: "#000")
textView.translatesAutoresizingMaskIntoConstraints = false
return textView
}()
let testUILabel: UILabel = {
let label = UILabel()
label.textColor = UIColor(hex: "#666666")!
label.translatesAutoresizingMaskIntoConstraints = false
label.addGestureRecognizer(tapGesture)
label.isUserInteractionEnabled = true
return label
}()
self.addSubview(testUITextView)
self.addSubview(testUILabel)
self.testTextView = testUITextView
self.testLabel = testUILabel
testUITextView.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
testUITextView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
testUITextView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
testUILabel.topAnchor.constraint(equalTo: testUITextView.bottomAnchor, constant: 50).isActive = true
testUILabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
testUILabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func testLabelTapped(_ sender: UITapGestureRecognizer) {
print("testLabelTapped")
}
}
I tried running your class and I was able to get the UILabel to fire after putting text into it. The tap gesture is only recognized within the view's bounds and since you didn't have any text in the UILabel, the bounds were zero giving you no place to click it. By default, if you put in text, the UILabel will automatically match those bounds. Here is my working code below:
class TestTextViewLabel : UIView {
weak var testTextView: UITextView!
weak var testLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(testLabelTapped(_:)))
let testUITextView: UITextView = {
let textView = UITextView()
textView.textColor = UIColor.black
textView.translatesAutoresizingMaskIntoConstraints = false
textView.text = "This is a text view"
textView.backgroundColor = .clear
return textView
}()
let testUILabel: UILabel = {
let label = UILabel()
label.textColor = UIColor(red:0.40, green:0.40, blue:0.40, alpha:1.0)
label.translatesAutoresizingMaskIntoConstraints = false
label.addGestureRecognizer(tapGesture)
label.isUserInteractionEnabled = true
label.text = "This is a UILabel view"
return label
}()
self.addSubview(testUITextView)
self.addSubview(testUILabel)
self.testTextView = testUITextView
self.testLabel = testUILabel
testUITextView.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
testUITextView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
testUITextView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
testUILabel.topAnchor.constraint(equalTo: testUITextView.bottomAnchor, constant: 50).isActive = true
testUILabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
testUILabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func testLabelTapped(_ sender: UITapGestureRecognizer) {
print("testLabelTapped")
}
}

How can I center UIView in a custom control?

I'm working on a exercise to create a custom control of anything. My idea is to have a UIView in the middle of the screen and a UILabel below it. When you tap on the view a random color will appear with the label changing to its hex value. When trying to create this custom control I'm having a problem trying to center the UIView programmatically. I get to an issue at `colorBox.center~
import UIKit
#IBDesignable
class Color: UIView {
private lazy var label : UILabel = {
let label = UILabel()
label.backgroundColor = UIColor.clear
label.translatesAutoresizingMaskIntoConstraints = false
label.heightAnchor.constraint(equalToConstant: 25.0).isActive = true
label.widthAnchor.constraint(equalToConstant: 100.0).isActive = true
label.font = .systemFont(ofSize: 15.0, weight: UIFontWeightRegular)
return label
}()
private lazy var colorGen : UIView = {
let colorBox = UIView()
colorBox.backgroundColor = UIColor.black
colorBox.heightAnchor.constraint(equalToConstant: 100.0).isActive = true
colorBox.widthAnchor.constraint(equalToConstant: 100.0).isActive = true
colorBox.centerXAnchor.constraint(equalTo: colorBox.frame.size.width /2)
}()
override init (frame: CGRect) {
super.init(frame:frame)
setUpLabel()
setUpView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setUpLabel()
setUpView()
}
I've tried the answers about using self.view but it doesn't work for me so I'm a bit lost.
You're close, but you need to add the label and the view so you can then constrain them relative to the superview...
#IBDesignable
class ColorView: UIView {
private lazy var colorLabel : UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = UIColor.clear
label.heightAnchor.constraint(equalToConstant: 25.0).isActive = true
label.widthAnchor.constraint(equalToConstant: 100.0).isActive = true
label.font = .systemFont(ofSize: 15.0, weight: UIFontWeightRegular)
return label
}()
private lazy var colorGen : UIView = {
let colorBox = UIView()
colorBox.translatesAutoresizingMaskIntoConstraints = false
colorBox.backgroundColor = UIColor.cyan
colorBox.heightAnchor.constraint(equalToConstant: 100.0).isActive = true
colorBox.widthAnchor.constraint(equalToConstant: 100.0).isActive = true
return colorBox
}()
override init (frame: CGRect) {
super.init(frame:frame)
commonSetup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonSetup()
}
func commonSetup() -> Void {
self.addSubview(colorGen)
self.addSubview(colorLabel)
colorGen.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0.0).isActive = true
colorGen.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0.0).isActive = true
colorGen.topAnchor.constraint(equalTo: self.topAnchor, constant: 0.0).isActive = true
colorLabel.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0.0).isActive = true
colorLabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0.0).isActive = true
colorLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0.0).isActive = true
colorLabel.topAnchor.constraint(equalTo: colorGen.bottomAnchor, constant: 0.0).isActive = true
colorLabel.text = "the label"
}
}

Resources