close UIPopover on rotation with a fadeout animation - objective-c

The Apple Pages and Numbers apps have popovers (for "tools" etc) that close with a lovely fade out effect when you rotate the device. I'm trying to recreate this, but my popovers always seem to close instantly, so the animation of the rotation doesn't look quite as smooth. I'm currently using:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
{
[toolsPopoverController dismissPopoverAnimated:YES];
}
Does anyone know the best way to achieve the same effect seen in Pages/Numbers?
Thanks!

Based on the documentation for the UIPopoverController (emphasis added):
If the user rotates the device while a popover is visible, the popover controller hides the popover and then shows it again at the end of the rotation. The popover controller attempts to position the popover appropriately for you but you may have to present it again or hide it altogether in some cases. For example, when displayed from a bar button item, the popover controller automatically adjusts the position (and potentially the size) of the popover to account for changes to the position of the bar button item. However, if you remove the bar button item during the rotation, or if you presented the popover from a target rectangle in a view, the popover controller does not attempt to reposition the popover. In those cases, you must manually hide the popover or present it again from an appropriate new position. You can do this in the didRotateFromInterfaceOrientation: method of the view controller that you used to present the popover.
It would appear that by calling [toolsPopoverController dismissPopoverAnimated:YES] in the willAnimateRotationToInterfaceOrientation: method, you are dismissing with an animation while the popover is hidden during the rotation transition.
If you call the dismissPopoverAnimated:YES method in the didRotateFromInterfaceOrientation: method instead, the default behavior with the popover in the new position should present before the dismiss animation is invoked.
If the default animation is still not what you are looking for at this point, I would create a custom animation block and manage the fadeout or re-sizing explicitly to meet your desired needs.

This worked for me by calling dismissPopoverAnimated: from willRotateToInterfaceOrientation:duration:.

Related

Transition from UITableView

Could recommend me any third part library to animate custom transition to new view controller directly from UITableView, where cells slides out (up and down) and smoothly transmit with opacity to new controller.
I suppose you code put in some gesture recognizers and set them as IBActions to segue to a different view controller.
In general you could just set an animation when a button as a clicked, to play the animation and then open a new view controller.

iOS7 - Setting selectedIndex of UITabBarController breaks touch events along right-hand edge of screen?

I've hit a weird problem with UITabBarController on iOS7 and can't seem to find a workaround, so any help would be welcome!
Scenario:
Navigation-based app using landscape orientation on iPad.
App consists of a main view, and a second view which is a UITabBarController.
TabBarController has two tabs.
First view has two buttons - each button performs a segue to the tab bar controller and sets a different tab as selected. (i.e. button1 selects the first tab, and button2 selects the second tab).
Setting the tab is done in prepareForSegue by calling setSelectedIndex on the tab bar controller.
Outcome:
On iOS 7 I am finding that the view shown in the tab bar controller fails to register any touch events along the right-hand edge of the view! So in the storyboard shown above, the UISwitch on the right side of the screen cannot be tapped.
I've even attached a tap gesture recognizer to the views and used it to log the area of the screen that can be touched - it seems to register touch events up to about x=770 points across. The remaining 1/4 of the screen is 'untouchable'!
After the segue, if you manually switch to the other tab and switch back again, the touch events are 'fixed' and the full view responds to touches again.
This doesn't seem to be a problem on iOS 5 / 6.
Any help much appreciated as to:
What is causing this to happen in the first place (iOS7 bug / change?)
How else can I work around this? I've tried calling setSelectedViewController as well as using setSelectedIndex and this seems to be the same.
Thanks in advance.
I ended up raising this with Developer Tech Support, and it looks like a bug. This is the response I got back from Apple:
The container view that the tab bar controller sets up to contain your view controller is not being resized to account for the interface being in landscape orientation. It's dimensions at the time your view controller is displayed are 768 (width) x 1024 (height).
The view hierarchy looks like this when the selected tab's view is displayed:
UIWindow
/* Navigation Controller */
UILayoutContainerView
UINavigationTransitionView
UIViewControllerWrapperView
/* Tab bar controller */
UILayoutContainerView
UITransitionView
UIViewControllerWrapperView <-- Incorrectly sized.
/* MyViewController */
MyViewController.view
The incorrect size of UIViewControllerWrapperView does not cause a display problem because subviews are still displayed even if they are outside their superview's bounds. However, event routing is much more strict. Events on the right quarter of the screen are never routed to your view controller's view because the hit test fails at the wrongly-sized UIViewControllerWrapperView where the event falls outside UIViewControllerWrapperView's bounds.
As a workaround, I subclassed UITabBarController, and added the following in viewWillAppear:
#implementation FixedIOS7TabBarController
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Fix the frame of the UIViewControllerWrapperView
self.selectedViewController.view.superview.frame = self.view.bounds;
}
#end
Hope that helps someone else....
As explained in this answer,
The container view that the tab bar controller sets up to contain your
view controller is not being resized to account for the interface
being in landscape orientation. Its dimensions at the time your view
controller is displayed are 768 (width) x 1024 (height).
I was encountering this problem when the TabBarController was originally displayed in portrait mode. When the device was rotated into landscape mode, the view was unresponsive on the right hand side.
The solution proposed in that answer did not work for me, because viewWillAppear: is invoked only once. However, viewDidLayoutSubvews is invoked whenever the view changes, including rotations, so my solution was to subclass UITabBarController and perform the workaround in viewDidLayoutSubvews:
#implementation FixedIOS7TabBarController
- (void)viewDidLayoutSubviews
{
// fix for iOS7 bug in UITabBarController
self.selectedViewController.view.superview.frame = self.view.bounds;
}
#end
End up finding a workaround here:
self.view.autoresizesSubviews = YES;
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
Right answer don't worked for me, cause user can change orientation; And it still not touchable in some area when change orientation.
So I create my own solution, I don't sure that is normal solution.
#implementation FixedIOS7TabBarController
- (UIView*)findInSubview:(UIView*)view className:(NSString*)className
{
for(UIView* v in view.subviews){
if([NSStringFromClass(v.class) isEqualToString:className])
return v;
UIView* finded = [self findInSubview:v className:className];
if(finded)
return finded;
}
return nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
UIView* wraperView = [self findInSubview:self.view className:#"UIViewControllerWrapperView"];
wraperView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
#end
Works perfectly for me!
In the list of view controllers on the left hand side navigate to the views/view controllers affected, drag the view to underneath the first responder so that it is disassociated to the view controller's view.
Then go to the layout tab on the right hand side, select all 4 anchors and both sets of resizing arrows (horizontal + vertical).
Then drag the view back to where it was originally (just below the view controller).

How to store orginal frame of UIView subclass loaded from storyboard automatically

I have a subclass of UIView which is has a property called originalFrame.
I want this class to store the original it's original frame value after it loaded from the storyboard it is apart of.
I have overridden the awakeFromNib method:
-(void)awakeFromNib
{
self.originalFrame = self.frame;
}
This works if the view of the view controller it is apart of is not resized right after to fit the screen size.
However, the view controllers in my storyboard are all for the iPhone 4-inch screen, so when the app runs on an iPhone 3.5 inch screen, awakeFromNib is called before the view controller's view is resized to fit the 3.5 inch screen. If my subview is not anchored to the top, then the originalFrame property won't reflect its frame after the resize.
I was able to override layoutSubviews to get the frame property after the view controller's view was resized, but this method is called other times too and I have no way of knowing if it is for the initial resize to fit screen, or something else.
Is there any way to do this without having to set this property manually on a case by case basis in the viewDidLoad method of the view controller?
You can check if the value is already set if you set it in awakeFormNib to CGRectZero and check in layoutSubviews if it is CGRectZero. Only set the frame in layoutSubviews if it is CGRectSubviews.

View is clipped during presentModalViewController transition animation

I have initialized a UIViewController with a nib file which displays fine when I push it on my viewstack.
However when I try and present it as a modal viewcontroller, the view of that controller is clipped at the bottom during the transition animation only. After the transition is done, the view displays just fine.
Anyone have a clue what is going on here? Do I need to set additional properties on my viewcontroller that is presenting my modal viewcontroller?
I have tried re-setting the frame of my viewcontroller without success...

Dismissing a modal view controller using a different animation than it was presented with

I have an application that presents a view controller (for registering / logging in) with a container view, and two views as switched between eachother using horizontal flipping. The app itself can be used before registration. I'm looking to change the way this is handled with storyboarding.
So upon opening the app there's a login button. If the user taps login a view controller is presented using Cover Vertical animation. On the top left of this view controller is a button to Register. Tapping on that does modal segue with a Horizontal Flip animation. On the Register view controller there's Login and Cancel buttons. I want Login to return to the login screen, and Cancel to go back to the view controller that was displayed using the Cover Vertical animation. Getting it there is fine, but the animation used is the Horizontal Flip animation, and not a (un)Cover Vertical animation.
I'ved tried the following code:
self.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
before dismissing the view controller, but it's still flipping instead of uncovering.
Thanks for the help!
~James
I see no reason that shouldn't work, but as an example here's the code I would use:
[self setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self dismissModalViewControllerAnimated:YES];
I suggest trying this, it works well for me.
for Swift you can call this:
viewController.modalTransitionStyle = .coverVertical
viewController.dismiss(animated: true, completion: nil)