how to ignore tap gesture action in voice over - ios

CollectionView Cell
ㄴ A
ㄴㄴ B
this is example of my collectionView cell View stack
the problem is
I put gestures and actions in B view.
If you turn on the accessibility, i think call collectionView's delegate = didSelectItemAt.. but my collectionView call tap Gesture action
I have never set up a gesture related to accessibility..
Is there a tap gesture relationship with voice over?
This phenomenon is only reproduced in certain views.
why this problem cause..?
private lazy var AContainer = UIView().then {
$0.addGestureRecognizer(UITapGestureRecognizer.init(target: self, action: #selector(actionGesture)))
}
AContainer.flex.define { flex in
flex.addItem(textLabel)
flex.addItem(testImageView)
.size(10)
}
contentView.flex.addItem().define { flex in
flex.addItem(AContainer)
}
#objc func actionGesture() {
print("testestest")
}

i catch the issue .
accessibilityActivationPosition's default position is mid,,
gesture action is in B view's center,,
so i solved this probelm by this code
B.accessibilityActivationPosition = CGPoint(x: 0, y: 0)

Related

Charts iOS: How do I implement tap recognizing for LineChart label

I have got a wide LineChart with many entries. I want to let user tap (or better longtap/3D touch) on an entry to show modal card where user could edit data entry. I tried implementing chartValueSelected but the problem is that it runs even when user taps to scroll (i e taps without releasing finger) which is not how a button should behave. Is there any way to implement tap recognizing for LineChart label?
It appears that overriding the tap gesture recognizer for the chart can work. This question has some answers based on someone who was looking for a similar solution.
You can attach your own gesture recognizer to LineChartView and use method getHighlightByTouchPoint to get information about the selected point.
override func viewDidLoad() {
// ...
let longTapRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(onLongTap))
lineChartView.addGestureRecognizer(longTapRecognizer)
// ...
}
#objc func onLongTap(recognizer: UILongPressGestureRecognizer) {
if recognizer.state == .ended {
let highlight = lineChartView.getHighlightByTouchPoint(recognizer.location(in: lineChartView))
print("\(highlight)")
}
}

Can't load gesture recogniser on multiple table cells

I have a tableView where I want each cell to respond to a long press gesture.
I attached the gesture recogniser to the prototype cell and connected it to an IBAction on the tableViewController's swift code.
When I run the app, the first cell loads fine, it responds to the long press gesture and no errors.
When I add another item to the table, I get this error:
2016-09-09 12:56:52.963 Day Care Register[1044:222155] WARNING: A Gesture
recognizer (<UILongPressGestureRecognizer: 0x7906a220; state =
Possible; view = <Day_Care_Register.DogTableViewCell 0x799c2c00>;
target= <(action=dogMarkedForBoarding:, target=
<Day_Care_Register.DogTableViewController 0x7906aa10>)>>) was setup in
a storyboard/xib to be added to more than one view (->
<Day_Care_Register.DogTableViewCell: 0x7a1bac00; baseClass =
UITableViewCell; frame = (0 0; 600 90); clipsToBounds = YES;
gestureRecognizers = <NSArray: 0x79180200>; layer = <CALayer:
0x7916cbe0>>) at a time, this was never allowed, and is now enforced.
Beginning with iOS 9.0 it will be put in the first view it is loaded
into.
OK, so if I understand this right, I can't use the same gesture recogniser over multiple cells?
How do I fix that?
I'm not sure what code you guys would want to see, so if you want to see anything, feel free to ask in a comment and I'll happily provide as soon as I can.
The way I've done this in the past is to create a gesture recognizer for the UITableViewController, assigning it a target selector of the form:
func handleLongPress(recognizer: UILongPressGestureRecognizer)
When you implement handleLongPress(_:) you can iterate over the visible cells in the table view, testing whether the visible cell's frame contains the point recognizer.locationInView(Self.tableView):
func handleLongPress(recognizer: UILongPressGestureRecognizer) -> Void {
guard let touchPoint : CGPoint = recognizer.locationInView(self.tableView) else { return }
let cellInFocus : UITableViewCell? = {
for cell in self.tableView.visibleCells {
if cell.frame.containsPoint(touchPoint) {
return cell
}
}
return nil
}()
guard let _ = cellInFocus else { return }
// Take desired action with cell...
}
Hope that helps!

Swipe to delete on a tableView that is inside a pageViewController

I've got a tableView inside of a pageViewController and when swiping on a cell to bring up the option to delete the cell the gesture is only recognized under certain circumstances, say you swiped very quickly and aggressively.
I imagine this is happening because it's not sure whether the swiping gesture is meant for the pageView or the tableView. Is there a way to specifically determine where the swipe gesture is happening to enable a nice smooth display of the delete button?
Theory:
Both UIPageViewController and UITableView are implemented using UIScrollView, where UIPageViewController embeds UIScrollView and UITableView is a subclass of UIScrollView
UITableView also uses a couple of UIPanGestureRecognizers to bring in all the magic. One of these is UISwipeActionPanGestureRecognizer which handles the swipe to delete actions.
This issue is caused because UIPageViewControllers UIPanGestureRecognizer wins in the conflict with the UITableViews UISwipeActionPanGestureRecognizers.
So we have to some how tell UIPageViewController to ignore gestures if UITableViews UISwipeActionPanGestureRecognizer are in action.
Luckily there is something already provided by UIGestureRecognizer.
UIGestureRecognizer's require(toFail otherGestureRecognizer: UIGestureRecognizer) creates a relation between the two gesture recognizers that will prevent the gesture's actions being called until the other gesture recognizer fails.
So all we had to do is fail UIPageViewControllers embedded UIScrollviews panGestureRecognizer when UITableViews UISwipeActionPanGestureRecognizer are triggered.
There are two ways you could achieve this.
Solution 1: Add a new gesture recognizer to table view and Mimic UISwipeActionPanGestureRecognizer. And make UIPageViewController panGesture require to fail this new gestureRecognizer
Solution 2 (A bit dirty): Make a string comparision to the UITableView's UISwipeActionPanGestureRecognizer and make UIPageViewController panGesture require to fail this new gestureRecognizer
Code:
Solution 1
A helpful utility to get UIPageViewControllers embedded UIScrollView
extension UIPageViewController {
var scrollView: UIScrollView? {
return view.subviews.first { $0 is UIScrollView } as? UIScrollView
}
}
Add the below code to UIViewController holding the UITableView and call it from viewDidLoad()
func handleSwipeDelete() {
if let pageController = parent?.parent as? UIPageViewController {
let gestureRecognizer = UIPanGestureRecognizer(target: self, action: nil)
gestureRecognizer.delaysTouchesBegan = true
gestureRecognizer.cancelsTouchesInView = false
gestureRecognizer.delegate = self
tableView.addGestureRecognizer(gestureRecognizer)
pageController.scrollView?.canCancelContentTouches = false
pageController.scrollView?.panGestureRecognizer.require(toFail: gestureRecognizer)
}
}
And finally delegate methods
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
guard let panGesture = gestureRecognizer as? UIPanGestureRecognizer else {
return false
}
let translation = panGesture.translation(in: tableView)
// In my case I have only trailing actions, so I used below condition.
return translation.x < 0
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return otherGestureRecognizer.view == tableView
}
Solution 2 (A bit dirty)
A helpful utility to get UIPageViewControllers embedded UIScrollView
extension UIPageViewController {
var scrollView: UIScrollView? {
return view.subviews.first { $0 is UIScrollView } as? UIScrollView
}
}
Add the below code to UIViewController holding the UITableView and call it from viewDidLoad()
func handleSwipeDelete() {
guard let pageController = parent as? UIPageViewController else {
return
}
pageController.scrollView?.canCancelContentTouches = false
tableView.gestureRecognizers?.forEach { recognizer in
let name = String(describing: type(of: recognizer))
guard name == "_UISwipeActionPanGestureRecognizer" else {
return
}
pageController.scrollView?
.panGestureRecognizer
.require(toFail: recognizer)
}
}
I had the same problem. I found a solution that works well.
Put this in your UIPageViewController's viewDidLoad func.
if let myView = view?.subviews.first as? UIScrollView {
myView.canCancelContentTouches = false
}
PageViewControllers have an auto-generated subview that handles the gestures. We can prevent these subviews from cancelling content touches. The tableview will be able to capture swipes for the delete button, while still interpreting swipes that fail the tableview's gesture requirements as page swipes. The delete button will show in cases where you hold and swipe or swipe "aggressively."
You can set delaysContentTouches to false on the tableView itself as well. This solution worked for my collection view's UISlider elements.
See Swift 4.0 code below:
yourTableView.delaysContentTouches = false
I have found a working solution by reassigning UIScrollView's panGestureRecognizer's delegate to my class and ignoring the original delegate when a right-to-left pan is detected. I used method swizzling for that.
Please check my sample project: https://github.com/kambala-decapitator/SwipeToDeleteInsidePageVC
In case you have a tableView in the first or the last viewController inside of your UIPageViewController maybe think of disabling the bouncing at the end of your UIPageViewController first and then implement this solution.
In case you don't know how to disable the bounce of the UIPageViewController, check out my answer here.

Gesture Recognizer in prototype cell doesn't work

I have a prototype cell with an UIImageView, when user tap this ImageView, app should display a Collection View where user can select an alternative icon for the cell. So, in UITableViewCell I added a Gesture Recognizer:
internal let iconTappedGR = UITapGestureRecognizer()
then I implemented it in table's cellForRowAtIndexPath:
cell.iconTappedGR.addTarget(self, action: #selector(changeIcon))
cell.iconView.gestureRecognizers = []
cell.iconView.gestureRecognizers!.append(cell.iconTappedGR)
and I added a changeIcon function
func changeIcon () {
print("imageView tapped!")
}
trouble is that it doesn't works; I tried even using storyboard but is the same...where am I wrong?
I solved using
cell.iconView.userInteractionEnabled = true
following this answer https://stackoverflow.com/a/36495864/2085352 to a question posted 30 minutes later than mine!

Remove custom UIView(s) dynamically on user selection/highlighed in ios swift

I am new to swift, please kindly advise the best way to achieve this.
Let say if there are 3 to 4 (custom) UIView(s) added under a parent view
when user select/highlighted a particular one (e.g. the 2nd UIView), and this will get removed and the whole layout will re-render immediately. Any idea?
Connect all views to one IBOutletCollection, add gesture recognizer for tap and in recognizer callback just get the touch point and check if the point is contained in one of the views from the outlet collection.
#IBOutlet var views: [UIView]!
override func viewDidLoad() {
super.viewDidLoad()
let tapGesture = UITapGestureRecognizer(target: self, action: Selector("viewTapped:"))
self.view.addGestureRecognizer(tapGesture)
}
func viewTapped(tapGesture: UITapGestureRecognizer) {
let locationInView = tapGesture.locationInView(view)
for v in views {
if CGRectContainsPoint(v.frame, locationInView) {
v.removeFromSuperview()
}
}
}
Make sure you have your autolayout setup for the state in which each of the view is not there.

Resources