UIGestureRecognizer stops working after first use - ios

In my tvOS app I have a collection view with cells that represent movies.
I attached 2 gesture recognizers to the view:
Go to movie details on selection tap
Play movie directly (with an Apple TV Remote dedicated Play button)
let posterTap = UITapGestureRecognizer(target: self, action: #selector(ListViewController.movieSelect(_:)))
posterTap.allowedPressTypes = [NSNumber(integer: UIPressType.Select.rawValue)]
self.view.addGestureRecognizer(posterTap)
let posterPlay = UITapGestureRecognizer(target: self, action: #selector(ListViewController.moviePlay(_:)))
posterPlay.allowedPressTypes = [NSNumber(integer: UIPressType.PlayPause.rawValue)]
self.view.addGestureRecognizer(posterPlay)
And the respected methods
func movieSelect(gesture: UITapGestureRecognizer) {
if let cell = UIScreen.mainScreen().focusedView as? ItemCollectionViewCell {
let item = ItemViewController(nibName: "ItemViewController", bundle: nil)
item.item = cell.data
self.presentViewController(item, animated: true, completion: nil)
}
}
func moviePlay(gesture: UITapGestureRecognizer) {
if let cell = UIScreen.mainScreen().focusedView as? ItemCollectionViewCell {
let data = cell.data
// TLDR;
// Passing data to AVPlayerViewController and presenting it + start playing the movie.
}
}
Everything seem to work, apart from the fact that when I stop playing the movie and coming back to the list (by closing the AVPlayerViewController), my second gesture recognizer (Play button) no longer works. It is still there if I check with print(self.view.gestureRecognizers) but moviePlay() is never called again no matter what.
Is there a way to debug this? What may cause this issue? I'm thinking this is caused by UIGestureRecognizerState being still in "use"? Or maybe something like that. At this point I have no clue.

I've experience weirdness with gesture recognizers on tvOS. In my case, for some reason, the app behaved as if gesture recognizers were lingering on after container view had been dismissed. Oddly enough, I've observed this weirdness when launching and closing AVPlayerViewController a few times as well.
What I did to fix this was to remove use of gesture recognizers and overriding pressesEnded method instead.
I hope this helps.

Related

Delay on tap gestures and pop alert vibration

I'm getting a lot of delay in an app that needs to feel more instant.
I've got a simple app that toggles left and right. It's a seesaw and when one end is up, the other is down. Your supposed to use two fingers and tap on the screen like your just fidgeting with it. It supposed to be an aid to ADHD.
I've got two large images for left and right state. I've got a gesture recognized on the image and I check the coordinates of the tap to determine if you tapped the right side to go down or the left. I'm also using AudioServicesPlayAlertSound to cause a small pop vibrate on touch begin in an effort to give a bit of a feedback stimulus to the user.
In my tests, if I tap rapidly it seems I get a backlog of taps on the toggle. The vibrations happen way after the tap is over, so it feels useless. Sometimes the UI image gets backlogged just switching between images.
override func viewDidLoad() {
super.viewDidLoad()
let imageView = Seesaw
let tapGestureRecognizer = UILongPressGestureRecognizer(target:self, action: #selector(SeesawViewController.tapped));
tapGestureRecognizer.minimumPressDuration = 0
imageView?.addGestureRecognizer(tapGestureRecognizer)
imageView?.isUserInteractionEnabled = true
}
func tapped(touch: UITapGestureRecognizer) {
if touch.state == .began {
if(vibrateOn){
AudioServicesPlaySystemSound(1520)
}
let tapLocation = touch.location(in: Seesaw)
if(tapLocation.y > Seesaw.frame.height/2){
print("Go Down")
Seesaw.image = UIImage(named:"Down Seesaw");
seesawUp = false
} else if (tapLocation.y < Seesaw.frame.height/2){
print("Go Up");
Seesaw.image = UIImage(named:"Up Seesaw");
seesawUp = true
}
}
}
Another idea - would it be faster to implement this as a button? Are gesture recognizers just slow? Are the way I'm drawing the image states consuming the wrong type processing power?
Sems like you made mistake in your code. You want to create tap recognizer, but you created UILongPressGestureRecognizer
Please change line from
let tapGestureRecognizer = UILongPressGestureRecognizer(target:self, action: #selector(SeesawViewController.tapped))
to
let tapGestureRecognizer = UITapGestureRecognizer(target:self, action: #selector(SeesawViewController.tapped))
Or you may add transparent button and put your code to its handler
// onDown will fired when user touched button(not tapped)
button.addTarget(self, action: #selector(onDown), for: .touchDown)

Swift 3: UITapGestureRecognizer Not Firing

I'm not sure why but my UITapGestureRecognizer is not firing correctly. Well in fact it is not firing at all. I am trying to get a function to run on the tap of an image.
Info about the setup:
Not using storyboards, loading everything programmatically
The view that I am loading this on also has a UICollectionView on it
ok so for the code:
The UIImageView is declared like so:
let backButtonIcon: UIImageView = {
let bbi = UIImageView()
bbi.image = UIImage(named: "backIcon")
bbi.translatesAutoresizingMaskIntoConstraints = false
bbi.layer.zPosition = 2
return bbi
}()
I then added that to the view:
view.addSubview(backButtonIcon)
to add the tap functionality to the UIImageView I am using:
let tapBackButton = UITapGestureRecognizer(target: self, action: #selector(backButtonPressed))
backButtonIcon.isUserInteractionEnabled = true
backButtonIcon.addGestureRecognizer(tapBackButton)
and lastly, here is the function that I am trying to run, just a simple print at the moment:
func backButtonPressed(sender: UITapGestureRecognizer) {
print("Go Back Pressed")
}
Update:
I have already tried adding:
bbi.isUserInteractionEnabled = true
and also:
backButtonIcon.isUserInteractionEnabled = true
Update 2:
I created another blank view and used the code exactly as it is here and it worked.
Could it be something to do with the UICollectionView?
I made the UICollectionView zPosition -1 and the UIImageView zPosition at 1 but that alas that also did not work.
Image views ignore user events by default. Normally, you use image views only to present visual content in your interface. If you want an image view to handle user interactions as well, change the value of its isUserInteractionEnabled property to true. After doing that, you can attach gesture recognizers or use any other event handling techniques to respond to touch events or other user-initiated events.
source: https://developer.apple.com/reference/uikit/uiimageview
I think the issue is with the UICollectionView, which is composed of UICollectionViewCells that already have a tap enabled by definition.
I'm looking through a very old app (Swift 1.x) I wrote for my personal usage (as in it never saw the light of day) and here's what I seemed to do - you probably need to do some variation of this:
Subclassed UICollectionViewCell and added a UILabel and UIImageView as subviews to it, populating them (along with a NSURL for the tap action).
Declared (and adopted) a specific UIViewController to also be my UICollectionView delegate (also for my purposes it was a SFSafariViewController delegate).
In that VC's viewDidLoad() I did set up a long press (for removing) and double tap (for reloading) gestures, but did not set up a tap gesture - it isn't needed.
In that VC's viewDidLoad() I loaded (and populated) the cells.
Here's the thing I think you are getting stuck on, but without more code I'm not sure:
Coded against:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItemAtIndexPath(indexPath) as? BookmarkCell {
selectedBookmarkSection = indexPath.section
selectedBookmarkRow = indexPath.row
sfc = SFSafariViewController(URL: cell.targetUrl!, entersReaderIfAvailable:true)
sfc.delegate = self
presentViewController(sfc, animated: true, completion: nil)
} else {
// Error indexPath is not on screen: this should never happen.
}
}
Please be kind, as this was something I wrote back in July 2014! (I update the collectionView(didSelectItemAt:indexPath:) for Swift 3.x but that's it.)
TR;DR: I think - particularly if your tap is seen outside of a UICollectionView - that this may get you working.

Attempting to present <**> on <**> while a presentation is in progress. While performing a segue from one view controller to another using swift

override func viewDidAppear(animated: Bool) {
view.userInteractionEnabled = true
let pinchGesture:UIPinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: "pinchGesture")
view.addGestureRecognizer(pinchGesture)
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "dismissKeyboard")
view.addGestureRecognizer(tap)
}
func dismissKeyboard() {
view.endEditing(true)
}
func pinchGesture(){
self.performSegueWithIdentifier("trick1Segue", sender: self)
}
In my iOS app, i want to transition to a different view controller when a pinch gesture is performed on the screen.
The tap gesture is to dismiss the keyboard when tapped outside the keyboard area
While running the app, I get an error message:
"Attempting to present < ** > on < ** > while a presentation is in progress"
The new view controller appears but opens twice, with a very short time difference. Looked up a lot of blogs but couldn't find a solution, please help!!!
The problem is that pinchGesture can be called multiple times. You should add a property to your viewController to keep track of the fact that you already have acted upon the pinch gesture:
var segueInProcess = false
func pinchGesture() {
if !segueInProcess {
self.performSegueWithIdentifier("trick1Segue", sender: self)
segueInProcess = true
}
}
Gesture recognizers are called multiple times with different states. What you can do is check the state property of the UIPinchGestureRecognizer in pinchGesture().

Add single, double and triple tap gestures to all "buttons" in custom keyboard in swift

I'm trying to create a keyboard the allows single tap, double tap, and triple tap. So I want to add a UITapGestureRecognizer() to each button in my keyboard. I know how to do this manually from the xib file (add each letter it's own gesture, which would take ages) but not quite sure how to do it in the Controller.
I wrote this for double tap in the viewDidLoad() method:
let doubleTap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "doubleTapCharacter:")
doubleTap.numberOfTapsRequired = 2
for button in self.view.subviews{
button.addGestureRecognizer(doubleTap)
}
and created a doubleTapCharacter() method but it's still not working. I also want to be able to send information to the doubleTapCharacter method.
Any help would be much appreciated. Also, I'm very new to swift so if the instructions are complicated, I'd highly appreciate it if you can break it down a little.
create and add the gesture recognizers:
for button in view.subviews {
// create the gesture recognizer
let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: "doubleTapCharacter:")
doubleTapRecognizer.numberOfTapsRequired = 2
// add gesture recognizer to button
button.addGestureRecognizer(doubleTapRecognizer)
}
then implement the target method:
func doubleTapCharacter(doubleTapRecognizer: UITapGestureRecognizer) {
let tappedButton = doubleTapRecognizer.view as! UIButton
print(tappedButton.titleForState(UIControlState.Normal))
}

Hold down to view video like snapchat in swift

So I have a TableView that for each cell a user can hold down to view a video/image. The holding down part works but when I release it doesn't go back to the TableView.
Heres the code for presenting the view with the video/image:
func playVideo() {
// get path and url of movie
let path = NSBundle.mainBundle().pathForResource("static2", ofType:"mov")
println(path)
let url = NSURL.fileURLWithPath(path!)
var moviePlayer = MPViewController()//took out video handling right now so this is just basically a regular view controller
moviePlayer.navController = self.navigationController
// construct the views
moviePlayer.addGestRecognizer()
self.navigationController.pushViewController(moviePlayer, animated: true)
}
and then I have this gesture recognizer added to the view cell:
let singleTap = UILongPressGestureRecognizer(target: self, action: Selector("tapped:"))
self.addGestureRecognizer(singleTap)
and here is the "tapped:" function:
func tapped(gestureRecognizer:UIGestureRecognizer){
println("held down")
if gestureRecognizer.state == UIGestureRecognizerState.Began {
playVideo()
}
if gestureRecognizer.state == UIGestureRecognizerState.Ended {
println("hold ended")
self.navigationController.popViewControllerAnimated(true)
}
//playVideo()
}
for some reason once I push the view controller onto the main navigationController I no longer get the "hold ended". How can I set this up so it works exactly like snapchat?
This is not a solution for what you're asking but another idea, you might want to think about presenting the media files with a UIView instead of a UIViewController.
Hope that helps :)

Resources