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 :)
Related
I'm using BulletinBoard (BLTNBoard) to create dialogs in my iOS app. There's an option to embed image inside it. I would like to extend it's functionality and allow user to manipulate this image using tap gesture. But eventually when I assign a gesture to it's imageView using addGestureRecognizer nothing happens.
Here's how I initiliaze bulletin and add gesture to the image:
class ViewController: UIViewController {
lazy var bulletinManager: BLTNItemManager = {
let rootItem: BLTNPageItem = BLTNPageItem(title: "")
return BLTNItemManager(rootItem: rootItem)
}()
override func viewDidLoad() {
//etc code
let bulletinManager: BLTNItemManager = {
let item = BLTNPageItem(title: "Welcome")
item.descriptionText = "Pleas welcome to my app"
item.actionButtonTitle = "Go"
item.alternativeButtonTitle = "Try to tap here"
item.requiresCloseButton = false
item.isDismissable = false
item.actionHandler = { item in
self.bulletinManager.dismissBulletin()
}
item.alternativeHandler = { item in
//do nothing by now
}
//
item.image = UIImage(named: "welcome")
//adding gesture to its imageView
item.imageView?.isUserInteractionEnabled=true
let tap = UITapGestureRecognizer(target: self, action: Selector("tapTap:"))
item.imageView?.addGestureRecognizer(tap)
return BLTNItemManager(rootItem: item)
}()
}
#objc func tapTap(gestureRecognizer: UITapGestureRecognizer) {
print("TAPTAP!!!!!!")
}
}
and nothing happens at all (no message printed in console).
However if I assign action inside alternative button it works as expected:
item.alternativeHandler = { item in
item.imageView?.isUserInteractionEnabled=true
let tap = UITapGestureRecognizer(target: self, action: Selector("tapTap:"))
item.imageView?.addGestureRecognizer(tap)
}
I guess the only thing which can prevent me to assign the tap event to it properly is that imageView becomes available much later than the bulletin is created (for example only when it is shown on the screen).
Could you please help and correct my code. Thanks
upd.
Ok, based on Philipp's answer I have the following solution:
class myPageItem: BLTNPageItem {
override func makeContentViews(with interfaceBuilder: BLTNInterfaceBuilder) -> [UIView] {
let contentViews = super.makeContentViews(with: interfaceBuilder)
let imageView=super.imageView
imageView?.isUserInteractionEnabled=true
let tap = UITapGestureRecognizer(target: self, action: #selector(tapTap))
imageView?.addGestureRecognizer(tap)
return contentViews
}
#objc func tapTap(gestureRecognizer: UITapGestureRecognizer) {
print("TAPTAP!!!!!!")
}
}
When you're working with an open source library, it's easy to check out the source code to find the answer.
As you can see here, image setter doesn't initiate the image view.
Both makeContentViews makeArrangedSubviews (which are responsible for views initializing) doesn't have any finish notification callbacks.
Usually in such cases I had to fork the repo and add functionality by myself - then I'll make a pull request if I think this functionality may be needed by someone else.
But luckily for you the BLTNPageItem is marked open, so you can just subclass it. Override makeContentViews and add your logic there, something like this:
class YourOwnPageItem: BLTNPageItem {
override func makeContentViews(with interfaceBuilder: BLTNInterfaceBuilder) -> [UIView] {
let contentViews = super.makeContentViews(with: interfaceBuilder)
// configure the imageView here
return contentViews
}
}
I have a viewController EditMessage which has two UITextFields (UITextView) which use the keyboard and they work great. This part is basic standard stuff. When the keyboard is displayed, I register a tag gesture for the entire view, so that if the user clicks anywhere else, I dismiss the keyboard:
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self,
action: #selector(dismissKeyboard)))
In dismissKeyboard, this all works fine:
#objc func dismissKeyboard(sender: Any) {
self.view.endEditing(true)
}
However, I have a menu button(thumbnail image) implemented as a child view controller (UIViewController) on the same EditMessage view, which hijacks the screen via UIApplication.shared.keyWindow() to display an overlay and menu on the bottom of the screen. Built using the model/code from Brian Voong's YouTube channel to replicate a YouTube style slide in menu from the bottom. However, the keyboard is in the way. Since the child is a different view controller "endEditing" doesn't work (or maybe I am referencing the wrong view?).
class ButtonPickerController : UIViewController,
UIGestureRecognizerDelegate, UINavigationControllerDelegate {
var maxSize = CGFloat(60)
let thumbnail: UIImageView = {
let thumbnail = UIImageView()
thumbnail.backgroundColor = UIColor.lightGray
return thumbnail
}()
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(self.buttonTapped(sender:)))
tap.delegate = self
view.addGestureRecognizer(tap)
//view.backgroundColor = .yellow
view.contentMode = .scaleAspectFit
thumbnail.frame = CGRect(x: 0, y: 0, width: self.maxSize, height: self.maxSize)
setupSubviews()
}
Can someone point me in a good direction? This is my first question so hopefully I am asking properly.
I figured it out in the end. Thank you for the help. In my child view controller I did used the following statement when the button was tapped:
#objc func buttonTapped(sender: UITapGestureRecognizer? = nil) {
self.parent!.view.endEditing(true)
}
First, its not a good way to present overlays as UIViewController.
But a solution good be, to give the second viewcontroller a reference to the first one before viewDidLoad is called. Do you use Segues ? So in prepare would be the right place. In the second viewcontroller you create a property for the first one and then use this property as target when you create the UITapGestureRecognizer.
Another way is using a protocol and delegation.
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.
I have two view controllers (recorder and player) with tapGuestureRecognizers inside. I'm trying to use both of them in one place, lets call it container view controller, and replacing one with another with methods:
private func displayContentController(content: UIViewController) {
addChildViewController(content)
content.view.frame = view.frame
view.addSubview(content.view)
content.didMoveToParentViewController(self)
}
private func hideContentController(content: UIViewController) {
content.willMoveToParentViewController(nil)
content.view.removeFromSuperview()
content.removeFromParentViewController()
}
This is container view controller code:
private let recorder = CMRecorder()
private let player = CMPlayer()
override func viewDidLoad() {
super.viewDidLoad()
displayContentController(recorder)
recorder.finishRecordingCallback = { url in
self.hideContentController(self.recorder)
self.displayContentController(self.player)
}
}
Everything is ok with taping on recorder, but player don't want to recognize my taps. If I swap recorder with player (so player is loaded first), player has no problem with guesture recognizer. What did I miss?
I found a problem.
In my player view controller I have added imageView and forgot to set:
thumbnailView.userInteractionEnabled = true
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.