UITapGestureRecognizer not working on UIView in the navigationBar titleView? - ios

I am trying to add tapGesture on navigationBar titleView but not getting any event. Please tell me how to resolve this issue.
let titleView = UIView()
titleView.frame = CGRect(x: 0, y: 0, width: 100, height: 60)
titleView.backgroundColor = UIColor.yellow
let profileImageView = UIImageView()
profileImageView.contentMode = .scaleAspectFill
profileImageView.layer.cornerRadius = 20
profileImageView.clipsToBounds = true
profileImageView.loadImageUsingCacheWithUrlString(urlString: user.image)
titleView.addSubview(profileImageView)
profileImageView.translatesAutoresizingMaskIntoConstraints = false
profileImageView.leftAnchor.constraint(equalTo: titleView.leftAnchor).isActive = true
profileImageView.centerYAnchor.constraint(equalTo: titleView.centerYAnchor).isActive = true
profileImageView.widthAnchor.constraint(equalToConstant: 40).isActive = true
profileImageView.heightAnchor.constraint(equalToConstant: 40).isActive = true
let nameLabel = UILabel()
nameLabel.text = user.name
nameLabel.font = UIFont(name: "HelveticaNeue-Medium", size: 17)
titleView.addSubview(nameLabel)
nameLabel.translatesAutoresizingMaskIntoConstraints = false
nameLabel.leftAnchor.constraint(equalTo: profileImageView.rightAnchor, constant: 8).isActive = true
nameLabel.centerYAnchor.constraint(equalTo: profileImageView.centerYAnchor).isActive = true
nameLabel.rightAnchor.constraint(equalTo: titleView.rightAnchor).isActive = true
nameLabel.heightAnchor.constraint(equalTo: profileImageView.heightAnchor).isActive = true
self.navigationItem.titleView = titleView
titleView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(showChatTableViewController)))

Make sure to set isUserInteractionEnabled to true. By default it true but if it's not working in your case then try to set true
Make sure to debug your code and check that after adding titleView are you able to print out self.navigationItem.titleView ?
let titleView = UIView()
titleView.frame = CGRect(x: 0, y: 0, width: 100, height: 60)
titleView.backgroundColor = UIColor.yellow
titleView.isUserInteractionEnabled = true
self.navigationItem.titleView = titleView
titleView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(showChatTableViewController)))
#objc func showChatTableViewController() {
print("tapped")
}

Related

Swift - UIView moves when changing tab controller tabs

Inside the viewDidAppear I have a function that contains this code, in order to make a UIView:
let contentView = UIView()
func addSleepingView() {
contentView.backgroundColor = .systemYellow.withAlphaComponent(0.25)
view.addSubview(contentView)
contentView.translatesAutoresizingMaskIntoConstraints = false
contentView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
contentView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
// Anchor your view right above the tabBar
contentView.bottomAnchor.constraint(equalTo: (tabBarController?.tabBar.topAnchor)!).isActive = true
contentView.heightAnchor.constraint(equalToConstant: 50).isActive = true
let label = UILabel()
label.text = "Test"
label.frame = CGRect(x: 0, y: 0, width: 25, height: 34.0)
label.lineBreakMode = .byWordWrapping
label.numberOfLines = 0
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(label)
label.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
let button = UIButton()
button.setImage(UIImage(systemName: "arrow.clockwise", withConfiguration: UIImage.SymbolConfiguration(scale: .large)), for: UIControl.State.normal)
button.tintColor = .systemGray
button.frame = CGRect(x: self.view.bounds.width-42, y: 8, width: 34, height: 34.0)
button.isUserInteractionEnabled = true
button.addTarget(self, action: #selector(wakeupFunction), for: .touchUpInside)
contentView.addSubview(button)
let button2 = UIButton()
button2.setImage(UIImage(systemName: "exclamationmark.triangle", withConfiguration: UIImage.SymbolConfiguration(scale: .large)), for: UIControl.State.normal)
button2.tintColor = .label
button2.frame = CGRect(x: 8, y: 8, width: 34, height: 34.0)
button2.isUserInteractionEnabled = false
contentView.addSubview(button2)
contentView.bringSubviewToFront(button)
}
This is what it looks like:
Now this is exactly how I want it. The problem comes when I change tab. For example go to the last tab, and back to the first tab again. Then it looks like this:
What am I doing wrong here?
You can just add your code of activating constraints inside the DispatchQueue.main block
Updated Line you can change and it will start working as you're expectation.
// Anchor your view right above the tabBar
DispatchQueue.main.async
{
self.contentView.bottomAnchor.constraint(equalTo: (self.tabBarController?.tabBar.topAnchor)!).isActive = true
self.contentView.heightAnchor.constraint(equalToConstant: 50).isActive = true
}

Why does the UITableView goes below the TabBar?

This is how it currently looks:
Image here
This is my current code:
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor(red: 4/255, green: 4/255, blue: 4/255, alpha: 1.0)
self.navigationController?.navigationBar.barStyle = .black
self.navigationController?.navigationBar.tintColor = UIColor.white
self.navigationItem.title = "Test"
self.navigationController?.navigationBar.prefersLargeTitles = true
// Get main screen bounds
let screenSize: CGRect = UIScreen.main.bounds
let screenWidth = screenSize.width
let screenHeight = screenSize.height
myView.frame = CGRect(x: 0, y: 0, width: screenWidth, height: 150)
myView.backgroundColor = .red
self.view.addSubview(myView)
myTableView.frame = CGRect(x: 0, y: myView.frame.size.height, width: screenWidth, height: screenHeight-myView.frame.size.height-(navigationController?.navigationBar.frame.size.height)!-(tabBarController?.tabBar.frame.size.height)!)
print("SCREEN: \(screenHeight)")
print("TABLEVIEW: \(myTableView.frame.size.height)")
myTableView.dataSource = self
myTableView.delegate = self
myTableView.backgroundColor = .blue
myTableView.layer.borderWidth = 3
myTableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
self.view.addSubview(myTableView)
}
It looks liked I have coded it correctly. Also in the Storyboard's Attribute Inspector, I have unchecked the Extend Edges: Under Bottom Bar. Any ideas?
Guessing here, but you're probably having an autoresizing mask translated to a constraint. Breaking your layout as a result. Try:
myView.autoresizingMask = []
myTableView.autoresizingMask = []
// or alternatively
myView.translatesAutoresizingMaskIntoConstraints = false
myTableView.translatesAutoresizingMaskIntoConstraints = false
But it does not matter whether things are set up correctly or not because you are calculating the actual layout by hand. Try to utilize Auto Layout instead:
myView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(myView)
myView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
myView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
myView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
myView.heightAnchor.constraint(equalToConstant: 150).isActive = true
myTableView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(myTableView)
myTableView.topAnchor.constraint(equalTo: myView.bottomAnchor).isActive = true // making myTableView to lie just below myView
myTableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
myTableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
myTableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true

Copy of UiView and all subviews..Copied UIButton cannot be pressed

Essentially I am using this code extension below to copy a view and all its subviews. The copy is successful and I am able to view the copied view. However, the button on each copied view cannot be pressed. The button can only be pressed within the first original (not copied) view.
How to get all copied buttons to be active? Is this even possible?
I have already tried .isUserInteractionEnabled on the button and its parent view.
override func viewDidLoad(){
super.viewDidLoad()
view.isUserInteractionEnabled = true
view.addSubview(containerScrollView)
containerScrollView.addSubview(contentView)
contentView.addSubview(stackMainView)
let button = UIButton(frame: CGRect(x: 270, y: 200, width: 80, height: 40))
let partLabel1 = UILabel(frame: CGRect(x:10, y: 10, width: 300, height: 50))
let partLabel2 = UILabel(frame: CGRect(x:10, y: 50, width: 300, height: 50))
partLabel1.text = "This should sit within part use :)"
partLabel1.textColor = .white
partLabel2.text = "This should also sit within part use :)"
partLabel2.textColor = .white
contentView.addSubview(button)
contentView.addSubview(partLabel1)
contentView.addSubview(partLabel2)
part.addSubview(button)
part.addSubview(partLabel1)
part.addSubview(partLabel2)
part.bringSubviewToFront(button)
part.bringSubviewToFront(partUse3Label1)
part.layer.zPosition = -1
button.setTitle("Issue", for: .normal)
button.backgroundColor = .orange
button.leadingAnchor.constraint(equalTo: part.leadingAnchor).isActive = true
button.trailingAnchor.constraint(equalTo: part.trailingAnchor).isActive = true
button.bottomAnchor.constraint(equalTo: part.bottomAnchor).isActive = true
button.topAnchor.constraint(equalTo: part.topAnchor).isActive = true
button.heightAnchor.constraint(equalToConstant: 40).isActive = true
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
button.isUserInteractionEnabled = true
part.bringSubviewToFront(button)
partLabel1.leadingAnchor.constraint(equalTo: part.leadingAnchor).isActive = true
partLabel1.trailingAnchor.constraint(equalTo: part.trailingAnchor).isActive = true
partLabel2.leadingAnchor.constraint(equalTo: part.leadingAnchor).isActive = true
partLabel2.trailingAnchor.constraint(equalTo: part.trailingAnchor).isActive = true
part.layoutIfNeeded()
let copiedView = self.part.copyView()
stackMainView.addArrangedSubview(part)
stackMainView.addArrangedSubview(copiedView)
containerScrollView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
containerScrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0).isActive = true
containerScrollView.trailingAnchor.constraint(equalTo:self.view.trailingAnchor, constant: 0).isActive = true
containerScrollView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
contentView.topAnchor.constraint(equalTo: self.containerScrollView.topAnchor, constant: 0).isActive = true
contentView.leadingAnchor.constraint(equalTo: self.containerScrollView.leadingAnchor, constant: 0).isActive = true
contentView.trailingAnchor.constraint(equalTo:self.containerScrollView.trailingAnchor, constant: 0).isActive = true
contentView.bottomAnchor.constraint(equalTo: self.containerScrollView.bottomAnchor, constant: 0).isActive = true
contentView.widthAnchor.constraint(equalTo:self.view.widthAnchor, constant: 0).isActive = true
stackMainView.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 50).isActive = true
stackMainView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 8).isActive = true
stackMainView.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -8).isActive = true
stackMainView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -30).isActive = true
}
#objc func buttonAction(sender: UIButton!) {
print("Button tapped")
}
var containerScrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.backgroundColor = .white
scrollView.isScrollEnabled = true
return scrollView
}()
var contentView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.white
return view
}()
let stackMainView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.backgroundColor = .random()
return stackView
}()
let part: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.layer.cornerRadius = 4
view.layer.masksToBounds = true
view.backgroundColor = .random()
return view
}()
Extension to copy the view.
extension UIView {
func copyView<T: UIView>() -> T {
return NSKeyedUnarchiver.unarchiveObject(with:
NSKeyedArchiver.archivedData(withRootObject: self)) as! T
}
}
I expect the output to print in the console "Button Tapped"
This happens only when I press the button on the non copied view.
Since this is all done in viewDidLoad, I assume that the view you want to copy is the same every time.
Your code does not work likely because NSKeyedArchiver does not archive the button's target and selector pairs.
You can create a method that gives a new UIView instead:
func createPart() -> UIView {
let part = UIView()
part.translatesAutoresizingMaskIntoConstraints = false
part.layer.cornerRadius = 4
part.layer.masksToBounds = true
part.backgroundColor = .random()
// The part below is copied from your viewDidLoad method
// Include only those lines that create the part view.
// I might have put more than you need. Check twice
let button = UIButton(frame: CGRect(x: 270, y: 200, width: 80, height: 40))
let partLabel1 = UILabel(frame: CGRect(x:10, y: 10, width: 300, height: 50))
let partLabel2 = UILabel(frame: CGRect(x:10, y: 50, width: 300, height: 50))
partLabel1.text = "This should sit within part use :)"
partLabel1.textColor = .white
partLabel2.text = "This should also sit within part use :)"
partLabel2.textColor = .white
part.addSubview(button)
part.addSubview(partLabel1)
part.addSubview(partLabel2)
part.bringSubviewToFront(button)
part.bringSubviewToFront(partUse3Label1)
part.layer.zPosition = -1
button.setTitle("Issue", for: .normal)
button.backgroundColor = .orange
button.leadingAnchor.constraint(equalTo: part.leadingAnchor).isActive = true
button.trailingAnchor.constraint(equalTo: part.trailingAnchor).isActive = true
button.bottomAnchor.constraint(equalTo: part.bottomAnchor).isActive = true
button.topAnchor.constraint(equalTo: part.topAnchor).isActive = true
button.heightAnchor.constraint(equalToConstant: 40).isActive = true
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
button.isUserInteractionEnabled = true
part.bringSubviewToFront(button)
partLabel1.leadingAnchor.constraint(equalTo: part.leadingAnchor).isActive = true
partLabel1.trailingAnchor.constraint(equalTo: part.trailingAnchor).isActive = true
partLabel2.leadingAnchor.constraint(equalTo: part.leadingAnchor).isActive = true
partLabel2.trailingAnchor.constraint(equalTo: part.trailingAnchor).isActive = true
part.layoutIfNeeded()
return part
}
And then in viewDidLoad, you should remove the lines of code that help create the part view, leaving only the code that creates the stack view and main content view. You should then call createPart twice, and there you have 2 copies!
let part = createPart()
let copyOfPart = createPart()

swift can't call function after clicking button

I have made a container view with a image view and a button inside for some reason when running the app in simulator i can't click button and call the function taking me to the next screen , i have encountered this kind of problem before and it was as simple as changing the button to a lazy var though that hasn't work on this occasion?
lazy var nameLabelButton = UIButton()
func setupNavBarWithUser() {
guard let displayName = user?.DisplayName else { return }
let titleView = UIView()
titleView.frame = CGRect(x: 0, y: 0, width: 100, height: 40)
titleView.backgroundColor = UIColor.red
let containerView = UIView()
containerView.translatesAutoresizingMaskIntoConstraints = false
titleView.addSubview(containerView)
let profileImageView = UIImageView()
profileImageView.translatesAutoresizingMaskIntoConstraints = false
profileImageView.contentMode = .scaleAspectFill
profileImageView.layer.cornerRadius = 20
profileImageView.clipsToBounds = true
if let profileImageUrl = user?.profileImageURL {
profileImageView.loadImageUsingCacheWithUrlString(urlString:profileImageUrl)
}
containerView.addSubview(profileImageView)
//ios 9 constraint anchors
//need x,y,width,height anchors
profileImageView.leftAnchor.constraint(equalTo: containerView.leftAnchor).isActive = true
profileImageView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
profileImageView.widthAnchor.constraint(equalToConstant: 40).isActive = true
profileImageView.heightAnchor.constraint(equalToConstant: 40).isActive = true
containerView.addSubview(nameLabelButton)
nameLabelButton.setTitle("\(displayName)", for: .normal)
nameLabelButton.setTitleColor(.black, for: .normal)
nameLabelButton.translatesAutoresizingMaskIntoConstraints = false
//need x,y,width,height anchors
nameLabelButton.leftAnchor.constraint(equalTo: profileImageView.rightAnchor, constant: 8).isActive = true
nameLabelButton.centerYAnchor.constraint(equalTo: profileImageView.centerYAnchor).isActive = true
nameLabelButton.rightAnchor.constraint(equalTo: containerView.rightAnchor).isActive = true
nameLabelButton.heightAnchor.constraint(equalTo: profileImageView.heightAnchor).isActive = true
containerView.centerXAnchor.constraint(equalTo: titleView.centerXAnchor).isActive = true
containerView.centerYAnchor.constraint(equalTo: titleView.centerYAnchor).isActive = true
nameLabelButton.addTarget(self, action: #selector(self.openUsersProfileController), for: .touchUpInside)
self.navigationItem.titleView = titleView
}
func openUsersProfileController(){
print("asdasdadsad")
let openUsersProfileController = UserProfileController(collectionViewLayout: UICollectionViewFlowLayout())
openUsersProfileController.user = self.user
navigationController?.pushViewController(openUsersProfileController, animated: true)
}
well, code looks fine you just need to add #objc
#objc func openUsersProfileController(){
print("asdasdadsad")
let openUsersProfileController = UserProfileController(collectionViewLayout: UICollectionViewFlowLayout())
openUsersProfileController.user = self.user
navigationController?.pushViewController(openUsersProfileController, animated: true)
}
Reason:
From Swift 4, we manually need to add #objc before function.

titleview dissapears when a subview is added?

This is new to me so forgive me if I'm not asking the right question.
I'm following a tutorial where we are creating a few subviews inside of a titleview for the navigation bar so that a picture and username displays. When I create the intial titleview with a red background, it shows up as expected. However, when I add a container subview to place the text and image, the red titleview disappears. I finished the tutorial and the text shows up in the right place, but it doesn't allow me to add a tap gesture, since the titleview isn't there anymore to tap?
I'll add my code for this function- hopefully there's a stupid mistake that I'm missing.
func setupNavBarWithUser(user: User) {
let titleView = UIView()
titleView.frame = CGRect(x: 0, y: 0, width: 130, height: 35)
titleView.backgroundColor = UIColor.red
let containerView = UIView()
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.backgroundColor = UIColor.blue
titleView.addSubview(containerView)
let profileImageView = UIImageView()
profileImageView.translatesAutoresizingMaskIntoConstraints = false
profileImageView.contentMode = .scaleAspectFill
profileImageView.layer.cornerRadius = 20
profileImageView.clipsToBounds = true
if let profileImageUrl = user.profileImageUrl {
profileImageView.loadImageUsingCacheWithUrlString(urlString: profileImageUrl)
}
containerView.addSubview(profileImageView)
profileImageView.leftAnchor.constraint(equalTo: containerView.leftAnchor).isActive = true
profileImageView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
profileImageView.widthAnchor.constraint(equalToConstant: 35).isActive = true
profileImageView.heightAnchor.constraint(equalToConstant: 35).isActive = true
let nameLabel = UILabel()
containerView.addSubview(nameLabel)
nameLabel.text = user.name
nameLabel.translatesAutoresizingMaskIntoConstraints = false
nameLabel.leftAnchor.constraint(equalTo: profileImageView.rightAnchor, constant: 8).isActive = true
nameLabel.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
nameLabel.rightAnchor.constraint(equalTo: containerView.rightAnchor).isActive = true
nameLabel.heightAnchor.constraint(equalToConstant: 40).isActive = true
containerView.centerXAnchor.constraint(equalTo: titleView.centerXAnchor).isActive = true
containerView.centerYAnchor.constraint(equalTo: titleView.centerYAnchor).isActive = true
self.navigationItem.titleView = titleView
titleView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(showChatController)))
titleView.isUserInteractionEnabled = true
}
Replace setupNavBarWithUser method with this:
func setupNavBarWithUser(user: User) {
let titleView = UIView()
titleView.frame = CGRect(x: 0, y: 0, width: 130, height: 45)
titleView.backgroundColor = UIColor.red
let containerView = UIView()
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.backgroundColor = UIColor.blue
self.navigationItem.titleView = titleView
titleView.addSubview(containerView)
containerView.topAnchor.constraint(equalTo: titleView.topAnchor, constant: 0).isActive = true
containerView.bottomAnchor.constraint(equalTo: titleView.bottomAnchor, constant: 0).isActive = true
containerView.leadingAnchor.constraint(equalTo: titleView.leadingAnchor, constant: 0).isActive = true
containerView.trailingAnchor.constraint(equalTo: titleView.trailingAnchor, constant: 0).isActive = true
let profileImageView = UIImageView()
profileImageView.translatesAutoresizingMaskIntoConstraints = false
profileImageView.contentMode = .scaleAspectFill
profileImageView.layer.cornerRadius = 20
profileImageView.clipsToBounds = true
if let profileImageUrl = user.profileImageUrl {
profileImageView.loadImageUsingCacheWithUrlString(urlString: profileImageUrl)
}
containerView.addSubview(profileImageView)
profileImageView.leftAnchor.constraint(equalTo: containerView.leftAnchor).isActive = true
profileImageView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
profileImageView.widthAnchor.constraint(equalToConstant: 35).isActive = true
profileImageView.heightAnchor.constraint(equalToConstant: 35).isActive = true
let nameLabel = UILabel()
containerView.addSubview(nameLabel)
nameLabel.text = user.name
nameLabel.translatesAutoresizingMaskIntoConstraints = false
nameLabel.leftAnchor.constraint(equalTo: profileImageView.rightAnchor, constant: 8).isActive = true
nameLabel.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
nameLabel.rightAnchor.constraint(equalTo: containerView.rightAnchor).isActive = true
nameLabel.heightAnchor.constraint(equalToConstant: 40).isActive = true
self.navigationItem.titleView = titleView
titleView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(showChatController)))
titleView.isUserInteractionEnabled = true
}
You can compare the codes. As you can see you have to add subview in a proper order to set your constraints.

Resources