I am using auto layout with Storyboard. I present a popoverPresentationController from a cell rect:
NumberController * viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"NumberController"];
UIPopoverPresentationController *pc = [viewController popoverPresentationController];
pc.delegate = self;
pc.permittedArrowDirections = UIPopoverArrowDirectionAny;
pc.sourceView = tableView;
pc.sourceRect = [tableView rectForRowAtIndexPath:indexPath];
[self.navigationController presentViewController:viewController animated:animated completion:nil];
The popover presents on an iPad in portrait mode with the arrow up.
I rotate the iPad to landscape mode. The popoverPresentationController keeps the same sourceView/sourceRect and properly points to the cell. It also keeps the up arrow.
But it is now at the bottom of the view, so the popover resizes to a shorter height. This is not desired behavior.
If the popover were simply to move to a new position and change the arrow direction, it would not need to resize at all. This is the desired behavior.
I thought the following method might permit me to make changes, but it is not called since the sourceView rect does not change:
- (void)popoverController:(UIPopoverController *)popoverController
willRepositionPopoverToRect:(inout CGRect *)rect
inView:(inout UIView **)view {
}
I have tried to reset the permittedArrowDirections (in preferredContentSize, because this seemed like the most logical place). This does not work (the popover still resizes):
- (CGSize) preferredContentSize {
[super preferredContentSize];
self.popoverPresentationController.permittedArrowDirections = UIPopoverArrowDirectionUnknown;
return CGSizeMake(DEFAULT_POPOVER_WIDTH,DEFAULT_POPOVER_HEIGHT);
}
I simply cannot find a way to force the popoverPresentationController to change arrow direction and reposition the popover instead of resizing the popover. I am beginning to think it is not even possible - but I still hold out hope that I am just missing something.
EDIT: In the meantime, it has occurred to me that maybe a popover is not the best way to present this view if I don't want it resized in iPad. I am going to try it with UIModalPresentationFormSheet presentation. But I would still like to find an answer to this question.
I just ran into the problem where
- (void)popoverController:(UIPopoverController *)popoverController
willRepositionPopoverToRect:(inout CGRect *)rect
inView:(inout UIView **)view {
was not being called because my view controller was detached. There may be a view in your view hierarchy whose view controller has not been added as a child view controller.
I thought the following method might permit me to make changes, but it is not called since the sourceView rect does not change
The sourceView rect does not have to change, just the interface orientation. From the UIPopoverControllerDelegate documentation:
For popovers that were presented using the presentPopoverFromRect:inView:permittedArrowDirections:animated: method, the popover controller calls this method when the interface orientation changes.
Related
I am creating a tableview where I will have an UIImageViewinside a UITableViewCell that I would like to allow the user to zoom into. I have been able to create this effect inside a UIScrollView but have struggled to get it right for zooming into a single UITableViewCell.
At the bottom I have included the code that has worked for me for the UIScrollView. The challenge starts with what to return for the viewForZoomingInScrollView. If I try to give this method the UIImageView from the cell It throws an errors as it appears the viewForZoomingInScrollView is called before the table is loaded thus it can not find the cell I am referencing. If I pass the entire view itself self.view I can scroll but the entire tableview is zoomed (where I would like to zoom just the UIImageView inside the cell) and when I release the zoom I get an error Terminating app due to uncaught exception 'CALayerInvalidGeometry', reason: 'CALayer bounds contains NaN: [nan nan; 375 667] which is clearly not the correct path to take.
Any help or guidance here would be appreciated.
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return self.imageView;
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
[self centerScrollViewContents];
}
- (void)centerScrollViewContents {
CGSize boundsSize = zoomTable.bounds.size;
CGRect contentsFrame = self.imageView.frame;
if (contentsFrame.size.width < boundsSize.width) {
contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
} else {
contentsFrame.origin.x = 0.0f;
}
if (contentsFrame.size.height < boundsSize.height) {
contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
} else {
contentsFrame.origin.y = 0.0f;
}
self.imageView.frame = contentsFrame;
}
It sounds to me as though you want something like what Facebook or Twitter have, where you tap on an image and it zooms to fit the screen.
What you need to do is to consider that as a navigational step -- i.e. conceptually similar to what happens if you select a row in a standard table view and it pushes a new view controller onto the navigation controller stack, except that you probably want to present modally using a custom transition.
In the simple case, this would mean adding a tap gesture recogniser to the cell's image view; for the full effect you would add a pinch gesture recogniser to make an interactive transition.
I'd recommend watching the following WWDC videos:
2013 Session 218 "Custom Transitions Using View Controllers"
2014 Session 214 "View Controller Advancements in iOS 8"
2014 Session 228 "A Look Inside Presentation Controllers"
From your question What I understand is that you wanted to implement the functionality of Zoom in and Zoom out for ImageView. You first implemented that in a sample where you had a ScrollView Inside a ViewController and then ImageView inside that ScrollView. Things worked for you as expected. But then when you wanted the same functionality inside the TableViewCell, things didn't work for you.
So, I am suggesting you to use a View inside ScrollView as a ZoomView, and that ZoomView should have the ImageView as subview. And return that ZoomView in the method:
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView { return self.ZoomView; }
I'm attempting to reproduce the iTunes 11 behavior of navigable views within a popover. I can't seem to find a way to get my animation to happen at the same time as the popover's contentSize change happens, though.
The basic setup I have is a custom view subclass MyPopoverNavigationView with two subviews: the old and new views that I want the popover to navigate between. The popover's contentViewController has a MyPopoverNavigationView instance as its view. I do this:
// Configure constraints how I want them to show the new popover view
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *ctx) {
[ctx setDuration:0.25];
[ctx setAllowsImplicitAnimation:YES];
[self layoutSubtreeIfNeeded];
} completionHandler:nil];
As far as I can tell from the Auto Layout WWDC 2012 videos, this is the recommended way to animate changes to views' frames as a result of constraint changes. It works, but the animation happens in two phases:
First, the popover's contentSize will change to accommodate the new view that I'm moving to (before that view becomes visible, so it partially obscures the existing content).
Second, the views animate as I expect, so that the constraints system I installed is satisfied.
From setting some breakpoints, it looks like -layoutSubtreeIfNeeded eventually calls a private method on the popover called _fromConstraintsSetWindowFrame:, which does the popover size animation outside my animation group. My context's duration isn't respected, and my animations don't happen until the popover's size change is complete.
How can I get my views to animate together with the popover's size change?
Turns out the trick is to explicitly set the popover's contentSize property outside of the animation and completion blocks. The relevant snippet from the sample GitHub project I put together to figure it out looks like:
// Configure constraints for post-navigation view layout
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *ctx) {
[ctx setDuration:0.25];
[ctx setAllowsImplicitAnimation:YES];
[self layoutSubtreeIfNeeded];
} completionHandler:^{
// Tear down some leftover constraints from before the transition
}];
// Explicitly set popover's contentSize so its animation happens simultaneously
containingPopover.contentSize = postTransitionView.frame.size;
This works fine for me on Sierra:
let deltaHeight = 8
let contentSize = popover.contentSize
NSAnimationContext.runAnimationGroup({ (context) -> Void in
context.allowsImplicitAnimation = true
popover.contentSize = NSSize(width: contentSize.width, height: contentSize.height+deltaHeight)
})
In trying to attempt to use Apple's UIViewController containment I have encountered a problem with animating the transition between two UIViewControllers.
Here is the set up... I have created a UITabBarController and within one of the tabs I have created a UIViewController as a container. This ViewController manages the transition between a UIViewController and a UINavigationController. The before view is:
When the Next button is tapped the view begins it's transition with a flipFromRight transition. During the transition, the Navigation bar is in "to" view but is located 20pixels down from the top edge of the view. Picture below:
The green is the background color of the container view. Once the new view completes the transition, the Navigation bar snaps up to the top of the view and the final result is:
The time to snap in place is independent of the duration of the animation. I achieve the final state that I want, but the transition is a problem.
I have instrumented the viewController lifecycle and the frames of the Navigation bar and the UITableView are as specified in the XIB. The xib looks like this:
Here is the code:
In -viewDidLoad -
_fromVC = [[FromVC alloc] initWithNibName:#"FromVC" bundle:nil delegate:self];
[self addChildViewController:_fromVC];
[self.view addSubview:_fromVC.view];
[_fromVC didMoveToParentViewController:self];
In my button handler -
- (void)buttonSelected
{
//
// Create the "to" View controller
//
ToVC *toVC = [[ToVC alloc] initWithNibName:#"ToVC" bundle:nil];
//
// Create the navigation controller for the study activity
//
_toNavCon = [[UINavigationController alloc] initWithRootViewController:toVC];
[self addChildViewController:_toNavCon];
[_fromVC willMoveToParentViewController:nil];
[self transitionFromViewController:_fromVC
toViewController:_toNavCon
duration:0.7
options:UIViewAnimationOptionTransitionFlipFromRight
animations:nil
completion:^(BOOL finished) {
[_fromVC removeFromParentViewController];
[_toNavCon didMoveToParentViewController:self];
}];
}
There is no code in "to" view controller that changes the appearance of the view controller.
Another bit of information... When I "toggle In-Call status bar" in the simulator the gap at the top of the navigation bar is the height of the In-Call status bar.
I have looked at everything on the web and there is nothing that helps. Has anyone seen this and has anyone fixed it?
I did find an answer!! Subclass UINavigationController and override
- (BOOL)wantsFullScreenLayout{
return NO;
}
Apparently, UINavigationController and UITabController always want the full screen (default YES) - adjusting for the status bar - and you can not set this property any other way. It has been working for me.
Everything in my app is working a treat - but there is one niggling problem.
The UIImagePickerController seems to return the status bar when it is called. Obviously the app has the statusbar hidden throughout.
Now I have worked around this by rehiding it upon completion or canelation of the picker. This resulted in a black bar at the top of the app. So after the rehide I have had to reposition the titlebar and other table contents for it to fit.
All in all this works perfectly fine. However, the UIImagePickerController is called in detail view of a table. Therefore when the user has used the picker (and ive resized after use) and clicks the back button to return to the main table there is a small graphical glitch.
The detail view has been shifted up to hide the statusbar void, yet when I return to the main table and the app slides horizontally back to the main view, for a split second a 20px black box can be seen above the items on the detail view?
To recap. UIImagePickerController returns the staus bar (seemingly no matter what) and after coding to get rid and reformat the view I get a time (messy) graphical issue when returning to the main view.
Surely there is a way to stop the statusbar returning so I dont have to bodge bar back out using code? I have it set 'off' in the plist.
It very odd! Cheers
This helps to me.
1) You must delegate the UIImagePickerController
2) Add this to ViewController:
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{ // Esconder el StatusBar. Provocado por el iOS7 y el UIImagePickerController
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
}
implementing UIimagepicker controller use this.and hide status bar in to plist(statusbar initialy hidden=true) and set Uiview size 320x480 & implementing this
if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary])
{
UIImagePickerController *picker= [[UIImagePickerController alloc]init];
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[self presentModalViewController:picker animated:YES];
[picker release];
}
So basically I have this project, where I have 4 different tab bar pages. 2 of them uses navigation controller & tab bar, and 2 are just view controllers. Now there is this one viewcontroller, which I need to add a scroll view to. So basically, I click on the item in the tab bar, it takes me to a view controller, where I can scroll down and up. I have been following Youtube Video Link, but I do not get it to work. The problem is that I use the exact code, and I change the ViewController class to the .h and .m name (ScrollViewViewController), and I put in a scroll view that has 320 x 1000, and a button at the top, but it doesn't scroll! How can I solve this problem?
Note: That if you don't know my problem, but could very detailed walk me through on how to add a scroll view to storyboards in xcode 4.2, then that would be amazing :)!
scrollerViewController.m:
- (void)viewDidLoad {
[scroller setScrollEnabled:YES];
[scroller setContentSize:CGSizeMake(320, 1000)];
[super viewDidLoad];
}
scrollerViewController.h
#interface scrollerViewController : UIViewController {
IBOutlet UIScrollView *scroller;
}
#end
And here comes a picture of the storyboard.
I need some of the code in order to really know your problem, but let me throw in my guess. You may be setting the size 320x1000 to the frame size of the scrollview by any chance? The frame size of the scrollview has to stay 320x480, and the contentSize should be 320x1000. Sorry if it was a wrong guess, but shouldn't do any harm :)
Something like this in your ViewController of the scrollView...
- (void) viewDidLoad {
[super viewDidLoad];
UIScrollView * scrollView = self.view;
scrollView.frame = (CGRect){scrollView.frame.origin, CGSizeMake(320, 480)};
scrollView.contentSize = CGSizeMake(320, 1000);
scrollView.backgroundColor = [UIColor whiteColor]; // I am setting the white background, so that the scroll indicator is visible
}
I am assuming you are setting ScrollView as the top view of the ViewController in your Storyboard as in the attached image.
I followed the exact same movie and it did not work for me either. But this movie did: http://www.youtube.com/watch?v=YBlwepYK7Zs
I followed that one and now i got it to work.
A bit late but might be more people looking for a solution to this.