Using iOS pan gesture to highlight buttons in grid - ios

So far I have a grid of buttons and have attached a pan gesture recognizer to the view. I can track the events and get the location of the finger as it moves but there doesn't seem to be the equivalent of a "mouseEnter" message to use to get info about or control properties (such as the highlighting) of the other buttons I pass over.
Am I missing something? How can I accomplish, say, highlighting the buttons under the users' fingers as they pan over them? Does cocoa touch support this or must something else be done?
Thanks.

You are right, there is no such event. Also UIButton events won't help you either, because those require to actually start gesture inside. What you can do instead is to get location of the point you are currently dragging:
func panDetected(sender : MoreInformativeGestureRecognizer) {
let touchPoint = sender.locationInView(self.view)
}
And now, when you have the point, you can iterate on all the buttons you have and check if the point is inside the button:
let buttons = [UIButton]
let lastActiveButton = UIButton?
...
// Iterate through all the buttons
for button in buttons {
// Check area of the button against your touch
if CGRectContainsPoint(button.frame, touchPoint) {
// You are inside the touch area of the button
// Here, you can for example perform some action if you want, store information
// about the button so you don't do it multiple times etc.. your call :)
self.lastActiveButton = button
}
}
This way you can detect then you go in and out and do whatever you want with events. Hope it helps!

UIButton inherits from UIControl which has a number of events
Have you tried that? You can add a listener to those events, either through nib/storyboard or through code (look under discussion to see how to do this)

Related

Disable DoubleTapGesture Zoom out on MKMapView

I have observed MKMapView zoom-out on Double tap gesture, I didn't find any way to disable it. I tried to add own Double Tap Gesture to catch Double tap action but it still zoom out. Any thought?
There is no API to disable or change double-tap behavior with MKMapView.
But, if you really want to do so, one approach would be to find and remove the double-tap gesture recognizer from the MKMapView object.
In the project you shared, you could do that in makeUIView in your UIMapView class:
func makeUIView(context: UIViewRepresentableContext<UIMapView>) -> UIViewType {
self.configureView(mapView, context: context)
setRegion(to: palce)
if let v = mapView.subviews.first,
let ga1 = v.gestureRecognizers
{
let ga2: [UITapGestureRecognizer] = ga1.compactMap { $0 as? UITapGestureRecognizer } .filter { ($0.numberOfTapsRequired == 2) }
for g in ga2 {
v.removeGestureRecognizer(g)
}
}
return mapView
}
I wouldn't necessarily suggest that you do so, however.
Apple may change the MKMapView object in the future, which could then break this.
User's tend to prefer that common UI elements behave in expected ways.
Personally, I get rather annoyed when using an app and the developer has changed standard functionality of UI elements. For example, if I see a table view row with a disclosure indicator (the right-arrow / chevron), I expect that tapping the row will "push" to another screen related to that row. I've seen apps that do not follow that pattern, and it just gets confusing.

How to know when focused element is refocused in voice over (iOS)

I want to do something when the focused voice-over element is being tapped again.
Function accessibilityElementDidBecomeFocused() will only get called when the element is focused for first time.
When we are again single tapping on the same focused element then this function won't get called. Can anybody suggest some solution?
Can anybody suggest some solution?
Here are some ideas on the fly to detect a single-tap on the same focused element:
Create a variable nbSelections that will count the number of single-taps.
Create a tap gesture recognizer on your element to increment the number of taps for instance:
let tap = UITapGestureRecognizer(target: self,
action: #selector(addTapCounter(info:)))
tap.numberOfTapsRequired = 1
self.addGestureRecognizer(tap)
Add the trait that will allow to catch the simple tap directly on the element:
override var accessibilityTraits: UIAccessibilityTraits {
get { return .allowsDirectInteraction }
set { }
}
Set nbSelections = 0 when the element loses the focus:
override open func accessibilityElementDidLoseFocus() { nbSelections = 0 }
Combining these ideas with the UIAccessibilityFocus informal protocol could be a good line of research to reach your goal.
However, this technical solution assumes that the single-tap is made directly on the element itself (-trait specific) and not anywhere else (I don't see how to catch this event the top off my head).

Implementing long press gesture from object library no response- swift 3

I have a google maps in which I want it to recognise long press gestures on markers. For the mean time I just want to map to respond to a long press gesture. I think I have followed the steps to implementing it correctly but don't seem to be getting a response from the long press and not sure why- could it be because I am testing on simulator that it doesn't recognise click as long press? Anyway here is the method I used below so if anybody can see if there is anything I've missed, please let me know.
1.) dragged long tap gesture recogniser from object library into my maps view in the main storyboard.
2.) this put the gestureRecognizers --> map View into my referencing outlet connection
3.) set the min duration to 0.5sec and enabled both touch and tap recognizers to 1.
4.) in the viewcontroller which contains my mapView typed in:
#IBAction func handleLongtap(recognizer: UILongPressGestureRecognizer) {
print("PRESSED")
}
5.) then back on the main storyboard did control drag Long Press Gesture recognizer to the view controller and selected "handleLongtap:" which put 'handleLongTap --> longPressGesture" into my view controller 'received actions'
Despite there being no error- i do not get "PRESSED" in my terminal when i do a long click on simulator. Any idea what is going wrong?
You could try adding the gesture recognizer in code:
let longPress = UILongPressGestureRecognizer()
longPress.addTarget(self, action: ViewController.handleTap)
yourPin.addGestureRecognizer(longPress)
func handleTap() {
print("Tapped")
}
And just tweak it as you need.

Is TouchUpOutside the correct control event for events outside of an element?

I'm three days new into swift development, even Xcode for that matter. I've created a UIView called EncounterMenu that appears over my app when i click the menu button. My goal is to have the EncounterMenu UIView close when I do anything outside of it.. This is what I have so far
override func viewDidLoad() {
super.viewDidLoad()
//create a button that spans the width & height of the encounter menu
let btnEncounterMenu = UIButton(type: UIButtonType.System) as UIButton
btnEncounterMenu.frame = CGRectMake(0, 0, EncounterMenu.frame.width, EncounterMenu.frame.height)
//(this is whats not working) If user clicks outside of button, call function "closeEncounter:"
btnEncounterMenu.addTarget(self, action: "closeEncounter:", forControlEvents: .TouchUpOutside)
//Add the button to the EncounterMenu
EncounterMenu.addSubview(btnEncounterMenu)
}
func closeEncounter(sender: UIButton!) {
EncounterMenu.hidden = true;
}
I tried changing it to TouchUpInside and it worked when i clicked inside the EncounterMenu UIView, so I figured it should be as easy as TouchUpOutside?
Any direction to what I'm doing wrong or how I can accomplish what I'm trying to do?
Touch up outside will not work because you need to be pressing on the menu first, then drag your finger outside for that to register. instead, the easiest option for you to do is to create a button that is the entire size of your view, and on that buttons touch down event, fire off close menu. just make sure that this big button is at the very back of the view, and everything else is built on top of it.

What's the simplest way to receive tap events on a disabled UIButton?

I have a UIButton on a form, and want to put it in a disabled state when the form is incomplete. However, I still want to be able to detect if a user attempts to press the button even in its disabled state so that the interface can let the user know that certain required fields on the form are not filled-in yet (and perhaps scroll to that field and point it out, etc.).
There doesn't seem to be any straightforward way to do this. I tried simply attaching a UITapGestureRecognizer to the UIButton but it doesn't respond when the button is in a disabled state.
I'd like to avoid subclassing UIButton if possible, unless it's the only way.
Create a fallback button. Put it behind the main button. Set its background and text colors to [UIColor clearColor] to ensure it won't show up. (You can't just set its alpha to 0 because that makes it ignore touches.) In Interface Builder, the fallback button should be above the main button in the list of subviews, like this:
Give it the same frame as the main button. If you're using autolayout, select both the main and fallback buttons and create constraints to keep all four edges equal.
When the main button is disabled, touches will pass through to the fallback button. When the main button is enabled, it will catch all the touches and the fallback button won't receive any.
Connect the fallback button to an action so you can detect when it's tapped.
Based on #rob idea, I sub-class a UIButton, and add a transparent button before someone addSubview on this button.
This custom UIButton will save many time about adjusting the UI components on the storyboard.
Update 2018/08
It works well, and add some enhanced detail to this sub-class. I have used it for 2 years.
class TTButton : UIButton {
// MARK: -
private lazy var fakeButton : UIButton! = self.initFakeButton()
private func initFakeButton() -> UIButton {
let btn = UIButton(frame: self.frame)
btn.backgroundColor = UIColor.clearColor()
btn.addTarget(self, action: #selector(self.handleDisabledTouchEvent), forControlEvents: UIControlEvents.TouchUpInside)
return btn
}
// Respect this property for `fakeButton` and `self` buttons
override var isUserInteractionEnabled: Bool {
didSet {
self.fakeButton.isUserInteractionEnabled = isUserInteractionEnabled
}
}
override func layoutSubviews() {
super.layoutSubviews()
// NOTE: `fakeButton` and `self` has the same `superView`.
self.fakeButton.frame = self.frame
}
override func willMoveToSuperview(newSuperview: UIView?) {
//1. newSuperView add `fakeButton` first.
if (newSuperview != nil) {
newSuperview!.addSubview(self.fakeButton)
} else {
self.fakeButton.removeFromSuperview()
}
//2. Then, newSuperView add `self` second.
super.willMoveToSuperview(newSuperview)
}
#objc private func handleDisabledTouchEvent() {
//NSLog("handle disabled touch event. Enabled: \(self.enabled)")
self.sendActionsForControlEvents(.TouchUpInside)
}
}
You have a great misunderstanding of user experience.
If a button is disabled, it is meant to be non-interactable.
You can not click on a disabled button, that is why it is disabled.
If you want to warn users about something when that button is clicked (e.g. form not filled correctly or completely), you need to make that button enabled. And just warn users when they click on it, instead of proceeding further with app logic.
Or you can keep that button disabled until form criteria are met, but show what is wrong with the form using another way, like putting exclamation marks near text fields, changing text field colors to red, or something like that...
But never try to add gesture recognizers, or hidden fallback buttons to a disabled button.
Check those and let me know if you see a disabled button:
https://airbnb.com/signup_login
https://spotify.com/us/signup/
https://netflix.com/signup/regform
https://reddit.com/register/
https://twitter.com/signup
https://facebook.com/r.php
https://appleid.apple.com/account
https://accounts.google.com/SignUp
https://login.yahoo.com/account/create
https://signup.live.com/signup
All the proceed buttons on these websites are always enabled, and you get feedback about what is wrong when you try to continue.
And here is really good answer: https://ux.stackexchange.com/a/76306
Long story short: disabled UI elements meant to be not-interactable.
Trying to make them interactable while they are disabled is the same to making them enabled in the first place.
So, for your question's case, it is just a styling issue. Just try styling your button, instead of making it disabled/enabled.

Resources