How to use applicationWillResignActive to call a function of a view controller - swift

I am trying to call my menu view inside my view controller when the home button is pressed, or for that matter when the user gets a phone call, etc...
My goal is to call the function: toggleMenu() that is inside the QuestionViewController. Here's the code:
class QuestionViewController: UIViewController, MFMailComposeViewControllerDelegate {
///////////
func toggleMenu() {
// Show or hide menu
if menuStackView.isHidden == true {
// Show the menu
print("Showing Menu")
// Update labels
questionNumberMenuLabel.text = questionNumberLabel.text
endTimer()
menuStackView.isHidden = false
animateInMenu()
} else {
// Hide the menu
animateOutMenu()
}
}
I do believe I should utilize the following method found in the AppDelegate.swift file:
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
If I'm on the right track I need help calling the toggleMenu() function of the QuestionViewController class in this method. How do I do that?
Thanks so much!

Use the NotificationCenter and listen for UIApplicationWillResignActiveNotification. The system broadcasts that notification as well as calling your app delegate's applicationWillResignActive method (assuming you have one.)
Listen for notifications (using the method addObserver(forName:object:queue:using:) in your viewDidLoad. If you don't need to support iOS versions < 9.0, you don't need to worry about calling removeObserver - the system takes care of it for you.

Related

How to trigger method in a view from other view

I have two different views. In first view, I am listening for an event, whenever I receive the event I want to trigger a method in different view. Is this achievable? How can this be achieved, do I need to use a stream for this? I am using provider to share data and usually I access it like
context.read<Item>().doSomething
class View1 {
final Location _location = Location();
_location.onLocationChanged.listen((event){
print(event); // After receiving the event, I want to trigger the method in view2
}
}
class View2 {
void method() {
print('I want to be called when event is received from view1')
}
}
I recommend you to explore the Observer programming pattern.

check if user clicked outside view controller

I am working with swift 4 for macOS and I would like to dismiss a view controller, if I clicked outside of this view controller.
With this code I can check, if the user has clicked into the view controller. but how can I check, if the user has clicked outside the view controller?
override func viewDidAppear() {
let gesture = NSClickGestureRecognizer(target: self, action: #selector(clicked))
gesture.buttonMask = 0x1 // left mouse
gesture.numberOfClicksRequired = 1
self.view.addGestureRecognizer(gesture)
}
#objc func clicked() {
print("Hello world")
}
NSEvent has a method called...
+ (id)addLocalMonitorForEventsMatchingMask:(NSEventMask)mask handler:(NSEvent * _Nullable (^)(NSEvent *))block;
...that captures events before they are dispatched.
Ask to monitor mouse up or mouse down events and use the block to compare the coordinates to your view's bounds.
Edit:
Except, in Swift, it's called...
class func addLocalMonitorForEvents(matching mask: NSEventMask, handler block: #escaping (NSEvent) -> NSEvent?) -> Any?
I would use touchesEnded: then use the event to grab the locationInWindow. If the location falls outside your bounds of your view then dismiss it. A similar question for iOS can be seen here.
IOS - How to hide a view by touching anywhere outside of it
I always prefer touchedEnded because if a user accidentally clicks outside they can still drag to the view to cancel the dismissal.

Swift: Long press cancelled by movie playing. How do I persist the long press?

I'm building some functionality similar to SnapChat. Press and hold on a view, it brings up a movie to play, then returns to the main view controller when the movie finishes (currently working), or when the user picks up their finger (not working. That's what this question is about).
My problem lies in the IBAction, when the video comes up, the UIGestureRecognizerState changes from Began to Cancelled.
I'd like for the state to not change until the user lifts their finger, which should register UIGestureRecognizerState as Ended
Any tips on how to do that would be awesome. Thanks!
class ViewController: UIViewController {
var moviePlayer: MPMoviePlayerViewController!
#IBAction func handleLongPress(recognizer: UILongPressGestureRecognizer) {
println("\(recognizer)")
if recognizer.state == UIGestureRecognizerState.Began {
playVideo()
}
if recognizer.state == UIGestureRecognizerState.Ended {
self.moviePlayer.moviePlayer.stop()
}
}
func videoHasFinishedPlaying(notification: NSNotification){
println("Video finished playing")
}
func playVideo() {
// get path and url of movie
let path = NSBundle.mainBundle().pathForResource("IMG_8602", ofType:"MOV")
let url = NSURL.fileURLWithPath(path!)
moviePlayer = MPMoviePlayerViewController(contentURL: url)
presentMoviePlayerViewControllerAnimated(moviePlayer)
// construct the views
moviePlayer.view.frame = self.view.bounds
self.view.addSubview(moviePlayer.view)
// remove controls at top and bottom of video
moviePlayer.moviePlayer.controlStyle = MPMovieControlStyle.None
NSNotificationCenter.defaultCenter().addObserver(self, selector: "videoHasFinishedPlaying:",
name: MPMoviePlayerPlaybackDidFinishNotification, object: nil)
}
}
Edit: One possible solution
So this was a total fluke, but I figured out how to do this in this context.
I simply removed the line presentMoviePlayerViewControllerAnimated(moviePlayer) from my playVideo function.
Not entirely sure why that worked but it worked for my context.
This is ONE possibility but I'm open to better suggestions.
Add a 'master view' on top of where you need the touch event
Add the gesture recognizer to that view
Handle the logic within the gesture as you are now, but the subview that needs to be added needs to be added below the view thats receiving the touch.
You can now get the .ended state when the user lifts their finger.
Word of caution. If your view has other touch events make sure this 'master view' is not over top of them because it will intercept those events and hog it for its gesture (unless you set it up to recognize them simultaneously). If thats the case, you can make this 'master view' only a small port of the screen where you need to listen for the touch event without interruption

iOS MKMapViewDelegate - Getting UserLocation the second time

I create a MKMapView and associate a MKMapViewDelegate with it.
The MKMapViewDelegate gets notified correctly that DidUpdateUserLocation and other life cycle events occurred.
When I create another MKMapView later on in the app, the MKMapViewDelegate doesn't receive any notifications about MapLoaded or the like. Just the constructor is fired.
How do I get a new map instance the 2nd time to update the MKMapViewDelegate with lifecycle events?
Using .NET for iPhones (MonoTouch)
In the Map Delegate handle this event ONCE only, it gets fired a lot. This way you never need to worry about how the map caches.
public override void RegionChanged (MKMapView mapView, bool animated)
{
if (!hasSetupMapBoolean)
{
localCopymap = mapView;
DoYourSetupMap ();
}
}

Why does my UIButton delegate have to execute entirely before changes are shown in view?

I'm doing some MonoTouch development, and I really can't figure out an problem I've run into
I'm having an ViewController containing a UIButton. I have added a delegate to the TouchDown event of this button. In this delegate I'm calling a WebService and trying to change the colour and title of the button. However nothing happens to the button before the entire delegate have been executed. The thing is that the webservice is rather slow, so I want to give the users a waiting message by changing the colour and title of the button.
The code:
public override void ViewDidLoad ()
{
View.BackgroundColor = UIColor.Black;
bookButton = new UIButton( new RectangleF(10,100,this.View.Frame.Width-10 ,40) );
bookButton.BackgroundColor = UIColor.Clear;
setButton();
bookButton.TouchDown += delegate {
gymClass.book();
setButton();
tableView.ReloadData();
NavigationController.PopViewControllerAnimated( true );
};
this.View.AddSubview( bookButton );
}
Anyone, please?
The delegate is executed on the main thread which is responsible for rendering, so you are blocking the renderer until you return.