I have created a custom view that is to be used as a radio button with images and text. I need to be able to load the saved selection when the controller loads. I set my listeners this way:
for button in genderButtons {
button.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(genderTapped(_:))))
}
#objc private func genderTapped(_ sender: UITapGestureRecognizer) {
for button in genderButtons {
button.select(sender.view! == button) // Toggles the button to display selected/deslected state.
...
}
}
The problem is that I can't find a way to tell the view to select. I tried making the gesture recognizer and object, but it doesn't have any methods I can use to trigger it. The 'buttons' aren't actually buttons, they're views, so I can't send an action event.
How can I select the correct button with code?
Just call genderTapped directly, handing it the gesture recognizer already attached to the desired "button".
For example, if thisGenderButton is the one you want to "tap", say:
if let tap = thisGenderButton.gestureRecognizers?[0] as? UITapGestureRecognizer {
genderTapped(tap)
}
You can add this method in your customView like this,
Class CustomView: UIView {
public func select(_ value: Bool) {
self.backgroundColor = value ? .green: .red
}
}
and then in below method you can call select for the tapped view.
#objc private func genderTapped(_ sender: UITapGestureRecognizer) {
(sender.view as? CustomView)?.select(true)
}
Related
We want Lyft button touch event because I am working in analytics, so, I need how many people choose Lyft but I can't put UIView click event. I try below code.
let gesture = UITapGestureRecognizer(target: self, action: #selector(self.checkAction))
cell.lyftButton.addGestureRecognizer(gesture)
How can i achieve this?
You can directly assign a selector method to lyftButton e.g
lyftButton.addTarget(self, action: #selector(lyftButtonAction(_:)), for: .touchUpInside)
#objc
func lyftButtonAction(_sender: UIButton) {
//Do your action
}
To retrieve the LyftButton, you'll need to fetch the button inside the Lyft view, after retrieving it, I tried to add another target to it which was your 'checkAction' method, but for some reason it is not being called. One workaround solution is:
On Auto Layout, created a transparent button on top of the Lyft Button View, let's callet it 'Transparent Lyft Button': Example (I've embeded in another view because it was on a stackView);
On the code, retrieved the button with the above method, held it in a variable, let's call it 'requestLyftButton' and disabled it.
Created an IBAction for the 'Transparent Lyft Button' that triggers the method 'self.checkAction' that you've created and also calls requestLyftButton.sendActions(for: .touchUpInside), which triggers the original Lyft SDK action.
To Retrieve Lyft UIButton:
#IBOutlet weak var lyftButton: LyftButton!
#IBOutlet weak var transparentLyftButton: UIButton!
var requestLyftButton: UIButton?
func retrieveLyftButton(in view: UIView) {
for view in view.subviews {
if let lyftBtn = view as? UIButton {
lyftBtn.isEnabled = false
requestLyftButton = lyftBtn
} else {
retrieveLyftBtn(in: view)
}
}
}
transparentLyftButton IBAction to trigger your method + lyft sdk original action:
#IBAction func requestLyft(_ sender: UIButton) {
if let lyftBtn = requestLyftButton {
checkAction() // Your method
lyftBtn.sendActions(for: .touchUpInside)
}
}
I hope that you can understand what was done, if you have any questions, just let me know.
I have 2 groups of UIButtons with Pass or Fail. I need to check if in both groups was clicked any of the UIButtons (Pass or Fail) to scroll down to next question.
I'm using a delegate with 4 parameters (my 4 buttons) and I'm doing a if check, but is never entering in my if condition. Here's how my group of buttons looks:
Here is my code:
func tappedOnVehicleOrTrailerButtons(vehiclePassBtn: UIButton, vehicleFailBtn: UIButton, trailerPassBtn: UIButton, trailerFailBtn: UIButton) {
if (vehiclePassBtn.isTouchInside || vehicleFailBtn.isTouchInside) && (trailerPassBtn.isTouchInside || trailerFailBtn.isTouchInside) {
scrollDown()
}
}
Here is the delegate method:
// Check if the user press Pass or Fail btn for Vehicle or Trailer
#IBAction func passOrFailBtnTapped(_ sender: UIButton) {
delegate?.tappedOnVehicleOrTrailerButtons(vehiclePassBtn: vehiclePassButton, vehicleFailBtn: vehicleFailButton, trailerPassBtn: trailerPassButton, trailerFailBtn: trailerFailButton)
}
Is any other way to do it instead of if statement?
The code inside your if statement will never be reached. According to Apple's documentation
isTouchInside is a Boolean value indicating whether a tracked touch event is currently inside the control’s bounds.
Emphasis on the currently. So no two buttons will be simultaneously touched at the same time.
To achieve your goal, your buttons need some kind of state with wich you can check if the button was touched. You could simply subclass a button and add a wasTouched attribute, e. g.
class StateButton: UIButton {
override func awakeFromNib() {
super.awakeFromNib()
self.addTarget(self, action: #selector(self.buttonWasTouched), for: .touchUpInside)
}
#objc private func buttonWasTouched() {
self.isSelected = true
}
}
Your delegate method then would look like this:
func tappedOnVehicleOrTrailerButtons(vehiclePassBtn: StateButton, vehicleFailBtn: StateButton, trailerPassBtn: StateButton, trailerFailBtn: StateButton) {
if (vehiclePassBtn.isSelected || vehicleFailBtn.isSelected) && (trailerPassBtn.isSelected || trailerFailBtn.isSelected) {
scrollDown()
}
}
I want to remove UIView from screen after user tap something except that view. (to visualize it for you I will upload sketch of my view)
And I want to remove blue UIView after user tap on something except buttons in this view. What should I use?
EDIT:
In blue UIView are two buttons and I want to remove that view when user tap on background image
I did what #yerpy told me to do but it isn't working.
func test(gestureRecognizer: UITapGestureRecognizer) {
print("test")
}
func setUpBackgroundImageView() {
self.view.addSubview(backgroundImageView)
backgroundImageView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
backgroundImageView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
backgroundImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
backgroundImageView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
let tap = UITapGestureRecognizer(target: self, action: #selector(test(gestureRecognizer:)))
backgroundImageView.addGestureRecognizer(tap)
tap.delegate = self
}
And I also add shouldReceiveTouch function to UIGestureRecognizerDelegate. What am I doing wrong?
Add UIGestureRecognizer to the super view :
As you said you have image view as a background.
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapped(gestureRecognizer:)))
imageView.addGestureRecognizer(tapRecognizer)
tapRecognizer.delegate = self
Adding target action :
func tapped(gestureRecognizer: UITapGestureRecognizer) {
// Remove the blue view.
}
And then inside UITapGestureRecognizerDelegate :
extension ViewController : UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if touch.view!.superview!.superclass! .isSubclass(of: UIButton.self) {
return false
}
return true
}
}
Hope it helps !
EDIT
Make sure that user can touch on the view by enabling : self.view.userInteractionEnabled = true
1- Add a view below your view, let call it overlay (gray one)
2- Add your container view with all your buttons inside (green one)
3- Add a tap gesture to the overlay (drag tap to overlay view)
4- Create a #IBAction of the tap to the viewcontroller
5- Write code to hide your green view inside the #IBAction
Image
Image
you can use UITapGestureRecognizer for that view
override func viewDidLoad() {
super.viewDidLoad()
// Add "tap" press gesture recognizer
let tap = UITapGestureRecognizer(target: self, action: #selector(tapHandler))
tap.numberOfTapsRequired = 1
backgroundimage.addGestureRecognizer(tap)
}
// called by gesture recognizer
func tapHandler(gesture: UITapGestureRecognizer) {
// handle touch down and touch up events separately
if gesture.state == .began {
} else if gesture.state == .ended {
}
}
You can
subclass with that background view, and implement an inherited method -beganTouches:(there is another para but I forget what it is)
Add a UITapGestureRecognizer to that view
Add a giant button that covers the entire screen under all the buttons. Anytime the user presses on the giant button underneath the smaller buttons, do what you want it to do.
Not the most elegant but it works :P
I see that there are a ton of these questions, and I think I'm following the accepted Swift 3 methodology, but I'm still getting nothing. I can see that the UITapGestureRecognizer has been attached. Here's my code:
let tileClick = UITapGestureRecognizer(target: self, action: #selector(GameManagement.initiateTileClick(_:)))
newView.addGestureRecognizer(tileClick)
newView.isUserInteractionEnabled = true
func initiateTileClick(_ sender: UITapGestureRecognizer) {
print("initiate tile click")
}
A few things to note:
1) The view that I'm attaching the gesture recognizer to has a two views and a label within it that each cover the entire frame of the view, however, I tried attaching the recognizer to the label, which is the topmost child item and it still doesn't work.
2) Both the function that adds the recognizer and the function that is called on the tap are contained in an NSObject file. I have a variety of interconnected functions that I want to be able to call from multiple view controllers and would prefer to keep this in the separate NSObject file. The process worked when I had everything in a UIViewController file and stopped working when I moved the functions to the NSObject file.
3) I've tried changing GameManagement.initiateTileClick to self.initiateTileClick or just initiateTileClick and none of those worked.
If you are putting your views inside NSObject subclass then these views will lose their behaviors for UIResponder which manages the UI interactions as I am not able to see how you are adding these views to interface.
As you said, it was working inside ViewController because it manages view hierarchy and responder chain.
The solution would be to write extensions to separate code or better abstractions.
extension YourViewController {
newView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(GameManagement.initiateTileClick(_:))))
newView.isUserInteractionEnabled = true
func initiateTileClick(_ sender: UITapGestureRecognizer) {
print("initiate tile click")
}
}
Giving you an idea how the tap recogniser works.
Firstly add Tap gesture recogniser to your view controller. You have to put the object here as shown in the image.
Then control+drag the tap gesture object to your view and select delegate.
Then control+drag the recogniser to your swift file and action will be like this.
#IBAction func tapGesture(_ sender: UITapGestureRecognizer) {
}
Now you must have seen when you give some input to a text field, the keyboard appears. But if you press outside the text field, that is anywhere in the view, the keyboard hides. This is because of the tap gesture recogniser.
Consider you have a text field such that if you click in that text field, keyboard is appeared. But when you tap outside the textfield, the keyboard must hide.
Add this delegate
UITextFieldDelegate
Implement this:
#IBOutlet var phoneText: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
exampleText.delegate = self
}
#IBAction func tapGesture(_ sender: UITapGestureRecognizer) {
exampleText.endEditing(true)
}
Obviously,this function is instance method.
func initiateTileClick(_ sender: UITapGestureRecognizer) {
print("initiate tile click")
}
-
UITapGestureRecognizer(target: self, action:#selector(GameManagement.initiateTileClick(_:)))
but thisGameManagement.initiateTileClick(_:) looks like a class is calling a class method!The target should be the caller of method.self can't call GameManagement.initiateTileClick(_:).
I created a UISwitch on storyboard and tried to drag it to controller to create an action but there were only Insert Outlet / Insert Outlet Collection 2 options available? Why did this happen and how can I create action for UISwitch? I am using Xcode8.1
PS: The UISwitch was added on a container view
When "ctrl" + drag, you should see something like:
You should choose "Action" and let the event -which is by default- "Value Changed".
However, if you can't see this (and I assume you should, or there is somehow a problem), you can add an event programmatically to the switch outlet:
In viewDidLoad() method, you need to add:
override func viewDidLoad() {
// ...
mySwitch.addTarget(self, action: #selector(mySwitchTapped), for: .valueChanged)
// ...
}
mySwitchTapped(mySwitch: UISwitch) method:
func mySwitchTapped(mySwitch: UISwitch) {
if mySwitch.isOn {
} else {
}
}