How can I make my UIView (which has no access to the ViewController) perform some task after an orientation change has taken place? That is, no task should take place until the animation has completely finished and the frame is no longer changing due to animation.
I've tried registering an observer on the UIView to respond to UIDeviceOrientationDidChangeNotifications, but a breakpoint I've placed in the handler is triggered before the animation even starts.
You would need to involve the view controller. Only the view controller can get a reference to the transition coordinator, so that it can call animate(alongsideTransition:completion:) and do something in the completion handler. That is the moment when the animation has finished.
So the view controller could then talk directly to the view, or, if you want the view to be completely agnostic, the view controller could post a custom notification for which the view has registered.
Related
I am using Xcode 10 Beta 6, so this might just be a bug.
I am trying to present a view controller (colorPickerController) as a popover. Within that view controller I will be able to set some properties, which I want to read once the popover is dismissed.
Here's the code:
In lines 93...97 I define a completion handler.
In line 99 I present the colorPickerController modally, including the completion handler.
When running the code the color picker controller was successfully shown in a popover. But when I tapped outside of the popover (to dismiss it), the callback was not called.
I thought maybe a UIPopoverPresentationController does not dismiss "normally", so I tried manually dismissing the popover before it would do so itself, by calling dismiss in popoverPresentationControllerShouldDismissPopover (line 110).
Now this still didn't work, so I set a breakpoint as seen in the picture, to check if the delegate method is even being called.
That's when I noticed, that when running the app, the completion handler is called right when the popover appears, not when it dismisses.
I was logging Completion handler was called. in the console, before even reaching the breakpoint.
How is this possible?
The way your code is in your question, your updateColor closure is only getting called when the presentation animation finishes, not when the view controller you are presenting is done with whatever it needs to do.
See the docs for UIViewController.present(_:animated:completion:):
https://developer.apple.com/documentation/uikit/uiviewcontroller/1621380-present
completion
The block to execute after the presentation finishes. This block has no return value and takes no parameters. You may specify nil for this parameter.
Note the "after the presentation finishes" (emphasis mine). This means the closure will be executed literally right after the 0.2 seconds of time it takes to animate the presentation of the new view controller up from the bottom of the screen (or however long it takes and in whatever fashion if you're doing some fancy custom presentation animation).
To get a callback for when your new view controller is done doing whatever it needs to do, subclass UIViewController (call it, say, ColorPickerViewController), and use delegation of some sort to notify your current view controller to dismiss the color picker view controller (and, presumably, to tell it what color was picked).
#TylerTheCompiler is correct, the completion you pass runs after the animation completes. In order to accomplish what you want here's what I recommend:
1) Subclass the UIViewController and make a custom type.
2) Add a property to the new class of type:
var functionToFinish: (() -> Void)
3) Change update color's definition to be:
let updateColor: (() -> Void) = ...
3a) Insert the rest of your function for the ...
4) On line 98 write:
colorPickerController.functionToFinish = updateColor
5) Now, within the subclassed controller, you can call that function from the viewDidDisappear event
I have animations playing in a view controller (via the UIView.animateWithDuration method). When I transition from the view controller to another using transitionFromViewController, the animations stop while the transition to the new view is taking place.
Is there a way to stop this happening?
Ive tried setting
UIViewAnimationOptions.AllowUserInteraction and UIViewAnimationOptions.AllowAnimatedContent in the options, but as soon as the transition started all animations in the current view controller stop.
Edit
Also the fromViewController seems to be being removed from the container as soon as the method is called unless you specify an animation transition, where as I was the animated it myself.
I'm currently working on an app with a viewcontroller that has a number of subviews. Some of these subviews make requests (as in urlRequests) for data to display within the subview.
Now if someone navigates away from the main viewController I want to cancel any unfinished requests in the subviews. Is it enough to cancel these requests in the "dealloc" method of a subview as obviously they don't have a viewwilldisappear method. I'm using ARC and iOS 6 for what it's worth. Otherwise should I create my own cancel method in the subviews, and then loop through them in the viewWillDisappear of the main viewcontroller to call this method?
Or is the approach of the subviews making the request wrong to begin with?
The closest thing I could find to my question was view will disappear is not firing which seems to suggest dealloc should work.
Thanks
From my point of view, your views (the subviews) should only take care of displaying a content.
The requests should be done by a dataManager singleton for example or from the view controller (You could use an NSOperationQueue).
I would then create a cancel method that stops the current request and remove the next ones from the queue.
Putting the cancel code in the dealloc or the viewDidDisappear is up to you. If you think you're view will only disappear for a short amount of time then put it in the dealloc.
You could also listen to the UIApplicationDidEnterBackgroundNotification notification to cancel the requests too.
I have a pretty simple application with a couple of UIViewControllers (say VC_A and VC_B). Every screen has a button that allows to switch to another screen (no UINavigation is used).
App schedules a local notification, which, when expired, should present another view controller (VC_N - no matter what screen is active at the moment).
The problem is that sometimes application throws:
'NSInternalInconsistencyException', reason: 'Attempting to begin a modal transition from
<VC_A: 0x2021e0> to <VC_N: 0xf84b970> while a transition is already in progress.
Wait for viewDidAppear/viewDidDisappear to know the current transition has completed'
What is the proper way to implement such behaviour?
A. Use UIViewController's new presentViewController:animated:completion to present all three controllers instead of the old modal method.
B. Create a Boolean flag and initialize it to NO.
C. Before any view controller presentation, check for this flag. If YES, set to NO. And then present the VC. In the completion block, set the flag to YES again.
D. The app should ignore this flag when present the initial view controller whether VC_A or VC_B.
One pitfall to this is that if a button got pressed or the local notification expired while already a view controller was in a transition state, then the new VC won't get presented. One can improve upon this logic to present it after if needed.
When the timer expires don't call the view presentation in that timer handler method. Instead try putting the view presentation call in a separate method and use [self performSelector: withObject: afterDelay:] to call that method (delay could be 0.1]. This should get the view presentation done when current transitions complete.
We have a container view controller and want to be able to call "PerformSelector" on one of the "sub" view controllers in that container, right after starting a transition, i.e.
[self navigateSubViewControllerTo:newSubViewController];
... some time later, elsewhere in the stack, a selector will be performed on the top visible VC
[subViewController performSelector:#selector(foo)];
The call to transitionFromViewController happens in navigateSubViewController.Unfortunately, since transitionFromViewController happens asynchronously, we are finding that the performSelector call gets applied to the "before" sub view controller, not newSubViewController. I.e. it is happening before the transition happens.
Any thoughts on how to have performSelect not happen until the sub view controller transition happens?
You can just call performSelector in the completion block of
transitionFromViewController:toViewController:duration:options:animations:completion:
UIViewController provides a callback beginAppearanceTransition:animated: which is exactly for this purpose. Simply implement it in your subviewcontrollers and your good to go ;)