UISearchBar Sometimes Resigns When Tapping On Views - ios

My view controller setup is as follows: UISearchBar on top of screen, keyboard on the bottom, and other views between them: custom UIView, UIView, UITableView. When I tap on the table view cells and UIButtons inside UIViews, I can trigger my actions successfully while keeping UISearchBar still visible on screen. But when I tap on UIViews that do not have buttons, this triggers searchBarShouldEndEditing as if search bar loses the focus. I want to disable this behavior and let only Cancel button and keyboard's Done button to trigger UISearchBar closure.
I thought this has to do with bubbling effect and tried to implement this fix:
extension UIViewController : UIGestureRecognizerDelegate{
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
return touch.view == gestureRecognizer.view
}
#IBAction func didTap(_ gestureRecognizer : UITapGestureRecognizer ) {
//
}
but this did not address the issue and UISearchBar still calls searchBarShouldEndEditing. Of course, I could add logic to searchBarShouldEndEditing to return false but not sure this is effective enough.
How can I ensure UISearchBar remains in place while tapping on other elements like UIViews?

Related

iOS / Swift : How can I scroll a view up or down when touch event starts on a UIButton?

Using UIKit:
I've got a view that has multiple UIButton, but I can only scroll the view up or down if I began my scroll touch event outside the UIButton.
So how can I make it so that I can scroll my view, regardless if I start my touch event on a UIButton?
If your view behaves like a scroll view, try solution as below
It has been proposed here: https://stackoverflow.com/a/26292829/9191054 and it works!
class UIButtonScrollView: UIScrollView {
override func touchesShouldCancel(in view: UIView) -> Bool {
if view.isKind(of: UIButton.self) {
return true
}
return super.touchesShouldCancel(in: view)
}
}

Avoid Passing Touch Event to the Background View Behind

View
StackView (vertical)
Purple (View)
Green (View)
Orange (View)
View has a tap gesture recognizer. It prints "this" plus a random int.
If I tap Purple, Green or Orange, it still prints "this".
How can I disable taps on the colored boxes? I tried turning off the 'User Interaction Enabled' setting on the boxes.
Because the stack view is a sub-view of View and not added over the top (like a presented view controller), its going to receive the touch events like you're experiencing. Your view controller needs to adopt the UIGestureRecognizerDelegate protocol, then you need to set the gesture's delegate to the view controller (most likely you just need to set it to "self") and then implement the following function:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
//the below will return false if the touch's view is not the gesture recognizer's view
return touch.view == gestureRecognizer.view
}
The code example was referenced from: UITapGestureRecognizer tap on self.view but ignore subviews

Change dragging one view to another in same gesture

I'm trying to reproduce the buttom sheet in Apple's iOS 10 Maps app. Most of it is working. I've been looking at this SO post and Pulley on GitHub, but none of them solves my issue.
When the sheets is fully opened, it is possible to scroll the content of the sheet as a UITableView, but when the user tries to scroll down (where the UITableView's contentOffset would be negative), the gesture is dragging in the sheet instead of the UITableView. The gesture seamlessly changes from dragging the UITableView to dragging the sheet.
It is possiple disable the scrolling of the UITableView in the gesture delegate's shouldRecognizeSimultaneouslyWith, but this is only called when a gesture begins.
I can't control the panGestureRecognizer of the UITableView, so I can't just capture the gesture and determine what view it should move.
How can I change what UIGestureRecognizer should recognize touches, in the middle of a gesture?
Try
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer == myCustomPanGesture {
return self.tableView.contentOffset == 0
}
return true
}

Is it possible to make Tap Gesture Recognizer work if UIImageView it's connected to is under UIScrollView?

Please take a look on a screenshot
I have a UIImageView with Tap Gesture Recognizer connected to it ("Mans body image view"). When I tap this UIImage I want it to call a keyboard with colors so I can change skin color of this mans figure.
For now it does not work because on top of Mans body image view I have 4 scroll views which are responsible for hair, facial hair and clothing (tops and bottoms).
Is there any way I can make Tap Gesture Recognizer (under scroll views) to react on my taps?
Thank you
Make a UIScrollView subclass and implement
func gesture​Recognizer(UIGesture​Recognizer, should​Recognize​Simultaneously​With:​ UIGesture​Recognizer)
Asks the delegate if two gesture recognizers should be allowed to recognize gestures simultaneously.
func gestureRecognizer(_ gestureRecognizer: UIPanGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UISwipeGestureRecognizer) -> Bool {
return true
}
or use horizontal UICollectionView instead and implement delegate method didSelectItemAtIndexPath.

Disabling user interaction for subview interferes with event handling for superview?

CustomView contains a subview, SubView, which implements a tap handler. However, for this implementation, SubView should ignore taps and let CustomView handle them.
The code below is supposed to achieve this, but setting userInteractionEnabled to false prevents taps on SubView from cascading to CustomView. Shouldn't CustomView still receive tap events if SubView has userInteractionEnabled set to false?
The Apple documentation says setting userInteractionEnabled to false causes events to get ignored, not that the view will swallow them so superviews don't receive them.
// CustomView class, which is a subclass of UIView
// Handle taps
let singleTap = UITapGestureRecognizer(target: self, action: #selector(doTap))
addGestureRecognizer(singleTap)
// Add SubView
view.insertSubview(SubView, atIndex: 0)
SubView.userInteractionEnabled = false
Updated code (doInit is called since buttonTapped is invoked on taps) but still not working:
class CustomButton : UIButton, UIGestureRecognizerDelegate {
private func doInit() {
...
// Handle taps
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(buttonTapped))
tapRecognizer.delegate = self
addGestureRecognizer(tapRecognizer)
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
SubView will still receive the taps, but it will ignore them and it will stop there. If a view has userInteractionEnabled set to false, it will block touches that are meant for parent views.
My guess is you want to add this gesture recognizer delegate:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool
This will allow your parent view to receive touches at the same time as your SubView.
Building on Laynemoseley's answer, this is the relevant bit from the Apple docs:
In iOS 6.0 and later,
default control actions prevent overlapping gesture recognizer
behavior. For example, the default action for a button is a single
tap. If you have a single tap gesture recognizer attached to a
button’s parent view, and the user taps the button, then the button’s
action method receives the touch event instead of the gesture
recognizer. This applies only to gesture recognition that overlaps the
default action for a control, which includes:
A single finger single tap on a UIButton, UISwitch, UIStepper,
UISegmentedControl, and UIPageControl. A single finger swipe on the
knob of a UISlider, in a direction parallel to the slider. A single
finger pan gesture on the knob of a UISwitch, in a direction parallel
to the switch. If you have a custom subclass of one of these controls
and you want to change the default action, attach a gesture recognizer
directly to the control instead of to the parent view. Then, the
gesture recognizer receives the touch event first. As always, be sure
to read the iOS Human Interface Guidelines to ensure that your app
offers an intuitive user experience, especially when overriding the
default behavior of a standard control.
It seems the only solution is to override the gesture recognition and allow simultaneous gestures to get recognized.

Resources