I added a label and an image to the navigation item title view, like this - https://stackoverflow.com/a/38548905/1373592
And I added these three lines of code, to make the title clickable.
....
let recognizer = UITapGestureRecognizer(target: self, action: #selector(MyViewController.titleTapped(_:)))
navView.isUserInteractionEnabled = true
navView.addGestureRecognizer(recognizer)
And this titleTapped function.
#objc func titleTapped(_ tapGestureRecognizer: UITapGestureRecognizer) {
print("Tapped")
}
What am I doing wrong?
I tried adding gesture recognizer to the label, and to the image (separately). That didn't work either.
Thanks.
Your NavView has no frame, so there is nothing "there" to tap.
Add this line:
// Create a navView to add to the navigation bar
let navView = UIView()
// new line
navView.frame = CGRect(x: 0, y: 0, width: 200, height: 40)
// Create the label
let label = UILabel()
and you should be on your way.
100% sure working in my app and well tested.
var tapGesture = UITapGestureRecognizer()
take your view and set IBOutlet like:
#IBOutlet weak var viewTap: UIView!
Write pretty code on viewDidLoad() like:
tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.myviewTapped(_:)))
tapGesture.numberOfTapsRequired = 1
tapGesture.numberOfTouchesRequired = 1
viewTap.addGestureRecognizer(tapGesture)
viewTap.isUserInteractionEnabled = true
this method is calling when tap gesture recognized
#objc func myviewTapped(_ sender: UITapGestureRecognizer) {
if self.viewTap.backgroundColor == UIColor.yellow {
self.viewTap.backgroundColor = UIColor.green
}else{
self.viewTap.backgroundColor = UIColor.yellow
}
}
Note: In your case you have to sure for view which view infront like UILabel or UIView that is nav and then assign the gesture to it.If your label cover the whole view then let's try to give gesture to label only.
Related
I have a View Controller embedded in Navigation Controller. The view has 1 WKWebView, hence, I'm setting view = webView in loadView() override.
So, I'm adding a small little sub navigation bar underneath my navigation controller to allow a user to change their location.I can add the subview to the navigation controller, I'm just not able to make it clickable.
override func loadView() {
let config = WKWebViewConfiguration()
config.processPool = YourModelObject.sharedInstance.processPool
webView = WKWebView(frame: .zero, configuration: config)
webView.navigationDelegate = self
self.webView.scrollView.delegate = self
view = webView
..
if let navigationBar = self.navigationController?.navigationBar {
let secondFrame = CGRect(x: 50, y: 44.1, width: navigationBar.frame.width, height: 30)
let secondLabel = UILabel(frame: secondFrame)
secondLabel.textColor = .black
secondLabel.text = "Getting your location..."
secondLabel.isUserInteractionEnabled = true
let guestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(setLocation(_:)))
secondLabel.addGestureRecognizer(guestureRecognizer)
secondLabel.textAlignment = .left
secondLabel.font = secondLabel.font.withSize(14)
secondLabel.tag = 1002
navigationBar.addSubview(secondLabel)
}
}
And then the setLocation function
#objc func setLocation(_ sender: Any) {
print("location label tapped")
}
But when I tap the label, I'm not getting anything printed in console. I don't know if the use of target: self is wrong for the tapGestureRecognizer or what's going on here.
I too am new to Swift, so my answer is far from guaranteed. I just know what it's like to be in your position,
Perhaps try creating a subclass of navigationBar for the sub navigation bar, i.e. mySubNavigationBar. Then in the subclass's code do all the initialization that you need to do. Including the print line so you'll know if you're getting there.
p.s. I would have put this as a comment, but I don't have enough points to add comments.
I am trying to add tap gesture for a dynamically created UILabel in a function in swift 4, but it is not firing UITapGestureRecognizer function. it is working when i add tap gesture from viewDidLoad function, but i have to add tap gesture from other function.
here is the code
override func viewDidLoad() {
super.viewDidLoad()
createLabel()
}
func createLabel() {
let label = UILabel()
label.text = "abc"
label.numberOfLines = 0
label.frame.size.width = self.otherlinksStack.bounds.width
label.font = label.font.withSize(17) // my UIFont extension
label.sizeToFit()
label.tag = 1
self.otherlinksStack.addSubview(label)
let labelTapGesture = UITapGestureRecognizer(target:self,action:#selector(self.doSomethingOnTap))
label.isUserInteractionEnabled = true
label.addGestureRecognizer(labelTapGesture)
}
#objc func doSomethingOnTap() {
print("tapped")
}
You're doing a couple things wrong...
// your code
label.frame.size.width = self.otherlinksStack.bounds.width
label.sizeToFit()
If you're adding the label to a stackView, there is no need to set its frame -- let the stack view handle that. If your stackView's alignment is set to .fill it will stretch the label to its width anyway. If it's not set to fill, the label will expand horizontally as needed, based on its text. So, also, no need to call .sizeToFit().
// your code
self.otherlinksStack.addSubview(label)
When add a view to a stack view, use .addArrangedSubview, otherwise it will be added overlaid on top of another view in the stack view.
This should work fine (it does in my quick test):
func createLabel() {
let label = UILabel()
label.text = "abc"
label.numberOfLines = 0
label.font = label.font.withSize(17) // my UIFont extension
label.tag = 1
// give the label a background color so we can see it
label.backgroundColor = .cyan
// enable user interaction on the label
label.isUserInteractionEnabled = true
// add the label as an Arranged Subview to the stack view
self.otherlinksStack.addArrangedSubview(label)
// create the gesture recognizer
let labelTapGesture = UITapGestureRecognizer(target:self,action:#selector(self.doSomethingOnTap))
// add it to the label
label.addGestureRecognizer(labelTapGesture)
}
#objc func doSomethingOnTap() {
print("tapped")
}
Use this code for adding the Tap Gesture:
let tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapGestureMethod(_:)))
tapGesture.numberOfTapsRequired = 1
tapGesture.numberOfTouchesRequired = 1
yourLabel.isUserInteractionEnabled = true
yourLabel.addGestureRecognizer(tapGesture)
This is the tap gesture method:
#objc func tapGestureMethod(_ gesture: UITapGestureRecognizer) {
Do your code here
}
func addTapGesture() {
let labelTapGesture = UITapGestureRecognizer(target: self, action: #selector(doSomethingOnTap))
//Add this line to enable user interaction on your label
myLabel.isUserInteractionEnabled = true
myLabel.addGestureRecognizer(labelTapGesture)
}
#objc func doSomethingOnTap() {
print("tapped")
}
Then call addTapGesture() from viewDidLoad or from whichever function you wanna add tap from.
If your gesture works from viewDidLoad, then after adding from other function the gesture might override by other control's gestures..
Try adding gesture on control which is seperate from all other controls.
I have an image view on scroll view. I added more than one sub views on image view.
->UIScrollView
-->UIImageView
--->UIViews
I want to add gesture recognizer to subviews. But It doesn't work. Where is my mistake? function handleTap() not triggered.
func addSubViewOnImageView(mPoint:CGPoint, mSize: CGSize){
let rect = CGRect(origin: mPoint, size: mSize)
let sView = UIView(frame: rect)
sView.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
tap.delegate = self
sView.addGestureRecognizer(tap)
imageView.addSubview(sView)
}
#objc func handleTap(_ sender: UITapGestureRecognizer) {
print("tapped any sub view")
}
This property is inherited from the UIView parent class. This class changes the default value of this property to NO.
enable the user interaction for your imageview, by default its false , for more info you get the info from apple document
imageView.isUserInteractionEnabled = true
just check
yourImageView.isUserInteractionEnabled = true
is there or not?
An Alternate if you don't want to do
yourImageView.isUserInteractionEnabled = true
Just make your UIView class a subclass of UIControl from storyboard refer from screenshot.
it will act as an UIButton no need to write the code for adding an tap gesturejust add an action and likenormally we do for UIImageView`
I'm trying to get/keep a handle on elements in my UIStackView after I have moved it with a pan gesture.
For example, the elements I am trying to grab a handle on are things like the button tag or the text label text.
The code explained…
I am creating a UIStackView, via the function func createButtonStack(label: String, btnTag: Int) -> UIStackView
It contains a button and a text label.
When the button stack is created, I attach a pan gesture to it so I can move the button around the screen. The following 3 points work.
I get the button stack created
I can press the button and call the fun to print my message.
I can move the button stack.
The issue I have is…
Once I move the button stack the first time and the if statement gesture.type == .ended line is triggered, I lose control of the button stack.
That is, the button presses no longer work nor can I move it around any longer.
Can anyone please help? Thanks
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .lightGray
let ButtonStack = createButtonStack(label: “Button One”, btnTag: 1)
view.addSubview(ButtonStack)
ButtonStack.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
ButtonStack.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
let panGuesture = UIPanGestureRecognizer(target: self, action: #selector(pan(guesture:)))
ButtonStack.isUserInteractionEnabled = true
ButtonStack.addGestureRecognizer(panGuesture)
}
func createButtonStack(label: String, btnTag: Int) -> UIStackView {
let button = UIButton()
button.setImage( imageLiteral(resourceName: "star-in-circle"), for: .normal)
button.heightAnchor.constraint(equalToConstant: 100.0).isActive = true
button.widthAnchor.constraint(equalToConstant: 100.0).isActive = true
button.contentMode = .scaleAspectFit
button.tag = btnTag
switch btnTag {
case 1:
button.addTarget(self, action: #selector(printMessage), for: .touchUpInside)
case 2:
break
default:
break
}
//Text Label
let textLabel = UILabel()
textLabel.backgroundColor = UIColor.green
textLabel.widthAnchor.constraint(equalToConstant: 100.0).isActive = true
textLabel.heightAnchor.constraint(equalToConstant: 25.0).isActive = true
textLabel.font = textLabel.font.withSize(15)
textLabel.text = label
textLabel.textAlignment = .center
//Stack View
let buttonStack = UIStackView()
buttonStack.axis = UILayoutConstraintAxis.vertical
buttonStack.distribution = UIStackViewDistribution.equalSpacing
buttonStack.alignment = UIStackViewAlignment.center
buttonStack.spacing = 1.0
buttonStack.addArrangedSubview(button)
buttonStack.addArrangedSubview(textLabel)
buttonStack.translatesAutoresizingMaskIntoConstraints = false
return buttonStack
}
#objc func printMessage() {
print(“Button One was pressed”)
}
#objc func pan(guesture: UIPanGestureRecognizer) {
let translation = guesture.translation(in: self.view)
if let guestureView = guesture.view {
guestureView.center = CGPoint(x: guestureView.center.x + translation.x, y: guestureView.center.y + translation.y)
if guesture.state == .ended {
print("Guesture Center - Ended = \(guestureView.center)")
}
}
guesture.setTranslation(CGPoint.zero, in: self.view)
}
If you're using autolayout on the buttonStack you can't manipulate the guestureView.center centerX directly. You have to work with the constraints to achieve the drag effect.
So instead of ButtonStack.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true you should do something along the lines of:
let centerXConstraint = ButtonStack.centerXAnchor.constraint(equalTo: self.view.centerXAnchor)
centerXConstraint.isActive = true
ButtonStack.centerXConstraint = centerXConstraint
To do it like this you should declare a weak property of type NSLayoutConstraint on the ButtonStack class. You can do the same thing for the centerY constraint.
After that in the func pan(guesture: UIPanGestureRecognizer) method you can manipulate the centerXConstraint and centerYConstraint properties directly on the ButtonStack view.
Also, I see you are not setting the translatesAutoresizingMaskIntoConstraints property to false on the ButtonStack. You should do that whenever you are using autolayout programatically.
Thanks to PGDev and marosoaie for their input. Both provided insight for me to figure this one out.
My code worked with just the one button, but my project had three buttons inside a UIStackView.
Once I moved one button, it effectively broke the UIStackView and I lost control over the moved button.
The fix here was to take the three buttons out of the UIStackView and I can now move and control all three buttons without issues.
As for keeping a handle on the button / text field UIStackView, this was achieved by adding a .tag to the UIStackView.
Once I moved the element, the .ended action of the pan could access the .tag and therefore allow me to identify which button stack was moved.
Thanks again for all of the input.
I've the following code to add a gesture recognizer to a UILabel. User Interaction Enabled is ticked on for the label in the storyboard, but when I tap on the label the onUserClickingSendToken method is not being called.
class ViewController: UIViewController, MFMailComposeViewControllerDelegate {
#IBOutlet weak var tokenDisplay: UILabel!
var tapGestureRecognizer:UITapGestureRecognizer = UITapGestureRecognizer(target:self, action: #selector(onUserClickingSendToken(_:)))
override func viewDidLoad() {
super.viewDidLoad()
tapGestureRecognizer.numberOfTapsRequired = 1
tokenDisplay.addGestureRecognizer(tapGestureRecognizer)
}
func onUserClickingSendToken(_ sender: Any)
{
....
Initializing the tapRecognizer in viewDidLoad should do it, cause you were targeting self before the view was initialized
class ViewController: UIViewController, MFMailComposeViewControllerDelegate {
#IBOutlet weak var tokenDisplay: UILabel!
var tapGestureRecognizer:UITapGestureRecognizer!
override func viewDidLoad() {
super.viewDidLoad()
tapGestureRecognizer = UITapGestureRecognizer(target:self, action: #selector(onUserClickingSendToken(_:)))
tapGestureRecognizer.numberOfTapsRequired = 1
tokenDisplay.isUserInteractionEnabled = true
tokenDisplay.addGestureRecognizer(tapGestureRecognizer)
}
#objc func onUserClickingSendToken(_ sender: Any)
{
....
In general for UILabel clickable issue:
There are couple of reasons why your UILabel will not work as clickable
Make sure to mark UILabel UserInteraction true and if yourLabel is inside other view than mark that view UserInteraction true too.
yourLabel.isUserInteractionEnabled = true
Assigning self to target before the view initialisation, move your code inside viewDidLoad/awakeFromNib or after view Load
let tap = UITapGestureRecognizer(target: self, action: #selector(onClickLabel(_:)))
self.yourLabel.addGestureRecognizer(tap)
If you are adding UILabel programmatically, than you have to assign it's frame too.
yourLabel.frame = CGRect(x: 0, y: 0, width: 300, height: 20)
Super view in which your UILabel is should be large enough to fit/display label properly, if your label is inside some view than that view's height/weight should assign in a way that it cover the whole UILabel frame.
In my case this was the problem I had, I tried so many thing but at last it turns out that the view in which I had my label had fixed width, the label was displaying properly but onClick event was't working due to out of frame.
It was missing UILabel's trailing constraints, so half of the thing is not clickable.
Using same tab gesture for multiple view, you cannot use one tab gesture for more than one view or labels,use different for each UILabel.
try this:
let tap = UITapGestureRecognizer(target: self, action: #selector(onClickLabel(_:)))
self.yourLabel.isUserInteractionEnabled = true
self.yourLabel.addGestureRecognizer(tap)
let tap2 = UITapGestureRecognizer(target: self, action: #selector(onClickLabel(_:)))
self.someView.isUserInteractionEnabled = true
self.someView.addGestureRecognizer(tap2)
instead of this:
let tap = UITapGestureRecognizer(target: self, action: #selector(onClickLabel(_:)))
self.yourLabel.isUserInteractionEnabled = true
self.yourLabel.addGestureRecognizer(tap)
self.someView.isUserInteractionEnabled = true
self.someView.addGestureRecognizer(tap)
// tapGesture will work on only one element
Try changing your selector setup to:
#selector(ViewController .onUserClickingSendToken(_:).