TableHeaderView not responding to touches - ios

I have a custom subclass of UIView, designed in IB that contains a few labels and a button.
There is an action for touchUpInside event on the button and the target is the custom view.
I am attaching this custom view to a self.tableView.tableHeaderView for a tableview in my UI.
The strange thing is this custom view is not responding to touches. I can see it nicely with all the labels and the button in side the table view, that means the table view handles and shows it correctly, however it is not responding to touches.
I checked the whole view hierarchy and all the views involved have userInteractionEnabled as YES.
If i drag some other controls into that custom view for example a switch, segmented control..they do not respond as well. It is like these controls in custom view are not registering touches.
It doesn't make any sense. I am out of ideas. Can you help to allow the touch event on the button to arrive to its parent view?

What is a "headerTableView"? Do you mean a UITableViewHeaderFooterView? Have you tried setting userInteractionEnabled on the root UITableViewHeaderFooterView?

This is a hack that will detect a button touch and trigger touchUpInside programmatically:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
super.touchesBegan(touches, with: event)
if let touch = touches.first, button.bounds.contains(touch.location(in: button)) {
button.sendActions(for: UIControl.Event.touchUpInside)
}
}
I don’t know what your real problem is because that button should work without this.

Related

Prevent button to trigger when it's behind a view with UITapGestureRecognizer

I have the following setup :
Blue background at zPosition = 0
Yellow button at zPosition = 0 with an action to print "Button tapped"
UIView with grey background and 0.8 alpha at zPosition = 1 with a UITapGestureRecognizer with an action to print "Grey view tapped"
When I tapped at the grey area, "Grey view tapped" is printed.
But if I tapped at the location of the yellow button, "Button tapped" is printed.
I expected to always have printed "Grey view tapped" because that view is in front of the button.
How can I prevent the button that is behind a view to be triggered ?
I know that I can set the button to .isEnabled = false but that is not a suitable solution since the grey background is created in a parent view controller from witch all my views are inheriting.
I set the grey view to .isUserInteractionEnabled = true (even if it's the default value) as stated here : In Stackoverflow
I thought using Apple Documentation but the problem is that there are a button and a gesture recognizer instead of multiple gesture recognizers.
Any idea on how to do this properly?
#Daljeet lead me to the solution. Using the Debug View Hierarchy I realised that my button was on top of the the view. My mistake was because I didn't realised the difference between UIView.layer.zPosition and the order of subviews in the hierarchy. A view can be drawn behind another view but nonetheless be in front in the View Hierarchy.
The solution was to use view.bringSubviewToFront(greyView)
Be aware that if you add the button after calling that last line of code, the button will be placed on top of the View Hierarchy
you can use this method to check if the touch was on the button and then trigger your action.
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first {
let hitView = self.view.hitTest(firstTouch.location(in: yourBtn), with: event)
if hitView === yourBtn {
print("touch is inside")
} else {
print("touch is outside")
}
}
}

UICollectionView: Measure force on tap of cell

I am working on a tiny project where I want to use the 3D touch screen pressure capabilities (e.g. touch.force).
Right now I can measure force in my ViewController and it is behaving like I want it:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
if traitCollection.forceTouchCapability == UIForceTouchCapability.available {
// 3D Touch capable
let force = touch.force
print("Force: " + force.description)
}
}
}
I have one UIImageView that presents a picture. I also use an custom UICollectionView as overlay, which contains transparent cells and colored cells. What I am trying to achieve is to measure both force, and be able to remove the color from the cells when they are pressed, but I am failing to do both. I can either measure force, or change the color of a cell in the UICollectionView, but not both.
The solution as I see it is to forward touches from the UICollectionView to the next view in the hierarchy. But another solution could be to measure both force and register taps on cells from within the custom UICollectionView.
My question is, is either of my proposed solutions any good? And if so, how do I achieve them?
Apparently the override func touches... methods are part of UIView, not neccesarily UIViewControllers. This means that implementing the touchesMoved and other methods into my custom UICollectionView is perfectly possible and it solved my problem of handling touches and taps at the same time.

Button in table view cell

I have a custom button and it's added to a static cell. Somehow whenever I tap the button, it doesn't react immediately like when the button is added to a plain normal view.
When tapped, it registers the tap right away but the background color has a slight delay before changing.
To change the color I have the following code in my subclass of UIButton:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesBegan(touches, withEvent: event)
border.fillColor = .redColor() // border is a CAShapeLayer
print("began") // This prints right when the button is clicked
}
This button is added in a subclass of UITableViewCell and the button is reacting, except changing the color has a delay. How can I fix this?
Answer (by Jelly):
The answer of Jelly helped me a lot and made the button react a bit better. Knowing what the problem is also made it easier to research the problem and found out to make the button react just as it would in a normal view I have to disable both delaysContentTouches in the table view and it's subviews:
tableView.delaysContentTouches = false
tableView.subviews.forEach { ($0 as? UIScrollView)?.delaysContentTouches = false }
Set delaysContentTouches = false on your tableView.

Dismiss keyboard with swipe gesture

In Messages.app you can dismiss the keyboard down by scrolling the list view. To be clear, it isn't simply responding to a scrollViewDidScroll event. The keyboard tracks with your finger as you swipe down. Any idea how this is done?
Since iOS 7, you can use
scrollView.keyboardDismissMode = .Interactive
From the documentation:
UIScrollViewKeyboardDismissModeInteractive
The keyboard follows the
dragging touch offscreen, and can be pulled upward again to cancel the
dismiss.
In the XCode, attributes inspector, the scrollView has a Keyboard attribute. It has 3 options.
Do not dismiss
Dismiss on drag
Dismiss interactive.
If you're using a tableView and Swift 3 or Swift 4, it works by using:
tableView.keyboardDismissMode = .onDrag
Since iOS7, UIScroll​View and all classes that inherit from it (including UITableView) have a keyboard​Dismiss​Mode property. With Swift 5 and iOS 12, keyboard​Dismiss​Mode has the following declaration:
var keyboardDismissMode: UIScrollView.KeyboardDismissMode { get set }
The manner in which the keyboard is dismissed when a drag begins in the scroll view.
Note that UIScrollView.KeyboardDismissMode is an enum that has none, interactive and onDrag cases.
#1. Set keyboard​Dismiss​Mode programmatically
The code snippet below shows a possible implementation of keyboardDismissMode:
override func viewDidLoad() {
super.viewDidLoad()
// Dismiss keyboard when scrolling the tableView
tableView.keyboardDismissMode = UIScrollView.KeyboardDismissMode.interactive
/* ... */
}
#2. Set keyboard​Dismiss​Mode in storyboard
As an alternative to the programmatic approach above, you can set the keyboard​Dismiss​Mode value for your UIScrollView/UITableView in the storyboard.
Select your UIScrollView / UITableView instance,
Select the Attributes Inspector,
Set the correct value for Keyboard.
Without tableview - yes it not a swipe but it doesn't the trick
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
}

Detect touch that originated from superview

I'm creating a subclass of UIView to use in a project, and will handle touches on the main view. I'd like so that when the touch (on the main view) is dragged and contacts the special UIViews, they change their background color. Using UIView's default "touchesMoved" function only detects touches that originate on the specific view. I could set up the main view to check each instance of the custom UIView, but that would be contrary to encapsulation and lead to messy code.
Any ideas?
I'm not sure this is what your after, but if I put the following code in my view controller, whose view has several RDView views in it, the color of those views will change color when I drag from the main view into the RDView.
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
var touched = self.view.hitTest((touches.anyObject() as UITouch).locationInView(self.view), withEvent: event)
if touched is RDView {
touched?.backgroundColor = UIColor.redColor()
}
}

Resources