I've two buttons on iPad that opens two popups. They are opened with the code:
CGRect frame = [self.view convertRect:self.addNewFolderButton.frame fromView:self.addNewFolderButton.superview];
[self.popover presentPopoverFromRect:frame
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
and
CGRect frame = [self.view convertRect:self.addNewUserButton.frame fromView:self.addNewUserButton.superview];
[self.popover presentPopoverFromRect:frame
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
but my popView isn't opened in the same kind of way (the link with the calling button in positioned in different way. Why?
Because you are giving the arrow direction UIPopoverArrowDirectionAny which mean it could be any, set the specific direction for homogeneous behaviour
Related
I have an app which has two subviews.I wanna double tap the video view(the top one) to make it fullscreen to display video.(By fullscreen, I mean it should be in landscape mode) So how should I do in the method -(void)handleTapGesture::(UITapGestureRecognizer*)recognizer?
I guess, first of all, I should hide status bar and navigation bar; then rotate the video view to make it landscape left/right programmatically.
BTW, for some reason, I have to make my app only support portrait mode.Forgive my terrible English, if you don't understand my question clearly, plz leave a comment, thanks.
Update:
I've hided status bar and navigation bar both, but when I make the video view fullscreen, it seems navigation bar and status bar are still there and my video view cannot move up to the top of the screen!
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
[self.navigationController.navigationBar setHidden:YES];
[UIView beginAnimations : #"video full screen" context:nil];
[UIView setAnimationDuration:0.3];
[UIView setAnimationBeginsFromCurrentState:YES];
self.videoView.frame = self.view.bounds;
moviewGLView.frame = CGRectMake(0, 0, self.videoView.frame.size.width, self.videoView.frame.size.width*3/4);
moviewGLView.center = self.videoView.center;
videoDefault.center = self.videoView.center;
[UIView commitAnimations];
add this code in your ViewDidLoad() method
UITapGestureRecognizer *tapOnTopView = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(makeTopViewFullScreen)];
tapOnTopView.numberOfTapsRequired = 1;
[topView addGestureRecognizer:tapOnTopView];
add the following function
-(void)makeTopViewFullScreen
{
[UIView animateWithDuration:0.3
animation:^
{
topViewHeightConstraint = self.view.frame.size.height;
bottomViewHeightConstraint = 0;
[self layoutIfNeeded];
}
completion:^
{
//your code to rotate the topView
}
}
note that the topViewHeightConstraint and bottomViewHeightConstraint should be outlets connected in IB.
Hope that helps, let me know if you need more help.
I display a popover in map view as follows:
But when i rotate the device, It shows Like:
as you see it covers the annotation instead of showing just beside the annotation.
My code is as as shown below:
CGPoint annotationPoint = [mapView convertCoordinate:aView.annotation.coordinate toPointToView:mapView];
float boxDY=annotationPoint.y;
float boxDX=annotationPoint.x;
CGRect box = CGRectMake(boxDX,boxDY,5,5);
UILabel *displayLabel = [[UILabel alloc] initWithFrame:box];
[popView presentPopoverFromRect:displayLabel.frame inView:mapView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
[displayLabel release];
Help me to solve this problem.
You can try to override the following method (declared in UIViewcontroller.h) :
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration;
You'll probably need to save the state of your viewController (like isPopoverPresented, which annotation etc.) and update the popover's rect accordingly.
I'm trying to write an app that uses custom animations to transition between view controllers in the view stack in a UINavigationController.
Right now, I'm adding multiple laters of the same view, just to get the mechanics up and running properly.
My visual goals are as follows:
1. Create a new "FirstVC".
2. Set the alpha value of [FirstVC view] to 0.
3. Set the transform value of [FirstVC view] to 25% in both directions (vertical & horizontal).
In the animations block, what I'd like to accomplish is:
1. Set the transform value of [OutgoingVC view] to 500% in both directions (blowing it up 5x).
2. Set the alpha value of [OutgoingVC view] to 0 (fading out to nothing).
3. Set the transform value of [FirstVC view] to 1 (bringing it back to its original size).
4. Set the alpha value of [FirstVC view] to 1 (fading in to full color).
The net effect of this animation should be that the "top page" blows up and fades out while the "next/bottom page" blows up (only to full screen) and fades in. The idea is of a user falling through a floor or something. "Going deeper."
I've slowed my animation down to a 10.0 sec interval so I can get a better handle of what's going on.
It appears that the animations taking place on "OutgoingVC" (i.e., the top view) are correct. The bottom view, however, seems to come in fine (100% of screen size and alpha=1.0), but then it keeps going and appears to blow up to either 4x/5x and also fade out to an alpha of 0. Once the screen has gone completely black, the new UIViewController (FirstVC) is correctly displayed on screen.
Does anyone see why my code wouldn't behave the way I want?
Thanks!
Stupid me! Here's the code:
- (IBAction)AddNewScreenPressed:(id)sender
{
FirstVC *newViewController = [[FirstVC alloc] init];
UIView *currentView = [self view];
UIView *newView = [newViewController view];
[newView setTransform:CGAffineTransformMakeScale(0.25, 0.25)];
[newView setAlpha:0];
[UIView transitionWithView:[self view]
duration:10.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
[[self view] addSubview:newView];
[currentView setAlpha:0];
[currentView setTransform:CGAffineTransformMakeScale(5.0, 5.0)];
[newView setAlpha:1.0];
[newView setTransform:CGAffineTransformMakeScale(1, 1)];
}
completion:^(BOOL finished){
[[self navigationController] pushViewController:newViewController animated:NO];
}
];
}
You have added newView as a subview of currentView, so perhaps the animations you are doing to currentView also apply to newView - it presumably scales up and changes the alpha of its subviews as well, including newView. Could you have the initial scale of newView to be 0.2, and drop the rescaling of newView in the animation block, so it would end up at 1.0 scale? Not sure what to do about the alpha, though...
EDIT after seeing your solution below - glad you've got it working, would it perhaps be cleaner (if you're grabbing images off the screen to handle your views anyway) to have a separate viewcontroller to manage the transition, passing it the two images so it can do the animations internally, so you push it to the front with no animation, perform your animation starting with the first view's image and ending with the second view's image (so you can just do standard animations on two imageViews instead of the whole view of the controller), and then push the second view controller once you are done?
After playing with it some more, I've gotten over one hump and met with another obstacle.
I'm upvoting jrturton's answer because the problem did indeed seem related to the fact that I was adding the new view to [self view].
What I did to get around this was add a buffer view ([self BackView]) that exists between [self view] and its subviews.
Then, then following code works to push a new view controller:
- (IBAction)AddNewScreenPressed:(id)sender
{
FirstVC *newViewController = [[FirstVC alloc] init];
UIView *newView = [newViewController view];
[newView setAlpha:0];
[newView setTransform:CGAffineTransformMakeScale(0.25, 0.25)];
[[self view] addSubview:newView];
[UIView transitionWithView:[self view]
duration:1.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
[[self BackView] setTransform:CGAffineTransformMakeScale(5, 5)];
[[self BackView] setAlpha:0];
[newView setTransform:CGAffineTransformMakeScale(1, 1)];
[newView setAlpha:1];
}
completion:^(BOOL finished){
[newView removeFromSuperview];
[[self navigationController] pushViewController:newViewController animated:NO];
[[self BackView] setTransform:CGAffineTransformIdentity];
[[self BackView] setAlpha:1];
}
];
}
Now, of course, I've run into another problem, namely, popping controllers.
When I pop them, the animation seems to work just fine, but when I get to the end (completion block) and do the following:
[[self navigationController] popViewControllerAnimated:NO];
or even:
[[self navigationController] popViewControllerAnimatedYES];
what I end up with is a blank white screen.
I'm assuming that something I'm doing during my "push animation" is causing this problem, but I can't figure it out! Help!
Thanks!
When setStatusBarHidden:NO is set before the view loads, the UINavigationBar and other elements appear aligned immediately below the StatusBar as they should. However, when setStatusBarHidden:NO is set after the view loads, the UINavigationBar is partially covered.
The StatusBar must be revealed after loading the said view, but how can this be done without encountering the aforementioned problem?
I found a hack in a code of mine, though can't remember or find where it came from. The trick is to refresh the navigation bar by hiding and reshowing it:
[self.navigationController setNavigationBarHidden:YES animated:NO];
[self.navigationController setNavigationBarHidden:NO animated:NO];
In my code the function looks like this:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
[self.navigationController setNavigationBarHidden:YES animated:NO];
[self.navigationController setNavigationBarHidden:NO animated:NO];
}
However, BE WARNED, this is a hack, and currently I'm struggling with some bugs that appear to originate from this code (navigation item doesn't match navigation content). But since it did work for me in some places, I'd thought I'd mention it.
Edit:
I think I found the initial post here:
How do I get the navigation bar in a UINavigationController to update its position when the status bar is hidden?
GL,
Oded
(I realise this was an old question, but I just spent half an hour trying to find the answer myself without success, so I thought I would post it here for anyone else who get stuck... especially if you are trying to SHOW the status bar and your view is ending up overlapping it)
I found this works if you want to HIDE the status bar...
[[UIApplication sharedApplication] setStatusBarHidden:YES];
[self.view setFrame: [[UIScreen mainScreen] bounds]];
but not when you want to SHOW the status bar...
in that case I use this solution which works, but worries me because it hard codes the status bar height to 20...
it also worries me that I have to adjust the view differently depending on orientation. but if I didn't do that it always had the 20 point gap on the wrong edge.
In my case I want to turn the status bar off for some views, and then back on when I return. I had particular problems if I rotated the device while the bar was off. so the switch statement, although ugly (someone might post a cleaner solution), works.
[[UIApplication sharedApplication] setStatusBarHidden:NO];
CGRect frame = [[UIScreen mainScreen] bounds];
switch (self.interfaceOrientation)
{
case UIInterfaceOrientationPortrait:
frame.origin.y = 20;
frame.size.height -= 20;
break;
case UIInterfaceOrientationPortraitUpsideDown:
frame.origin.y = 0;
frame.size.height -= 20;
break;
case UIInterfaceOrientationLandscapeLeft:
frame.origin.x = 20;
frame.size.width -= 20;
break;
case UIInterfaceOrientationLandscapeRight:
frame.origin.x = 0;
frame.size.width -= 20;
break;
}
[self.view setFrame:frame];
My guess is the nav bar is being loaded before the status bar is shown, so the position of the nav bar is (0,0) which then overlaps with the status bar at (0,0). You can just move the frame of the navigation bar (or set up an animation block) in viewDidLoad, after you call setStatusBarHidden:NO.
Try doing navigationBar.frame = CGRectMake(0,20,320,44);
The status bar is 320x20, so just moving your navigation bar down by 20 should accomodate for it.
If you are having this problem because you are not displaying the status bar while your Default.png is loading, and then want to display the status bar immediately upon viewing your first View Controller, just make sure you put [[UIApplication sharedApplication] setStatusBarHidden:NO]; before [self.window makeKeyAndVisible]; in your AppDelegate.m. It happens so quick, you won't ever see the status bar on the splash screen.
[[UIApplication sharedApplication] setStatusBarHidden:NO];
[self.window makeKeyAndVisible];
Here's what I'm doing in my root controller now in iOS 5 after I tell the status bar to animate in. Ugly, but it seems to work.
CGRect rect;
if ( self.interfaceOrientation == UIInterfaceOrientationPortrait )
rect = CGRectMake(0, 20, 320, 460);
else if ( self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown )
rect = CGRectMake(0, 0, 320, 460);
else if ( self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft )
rect = CGRectMake(20, 0, 300, 480);
else
rect = CGRectMake(0, 0, 300, 480);
[UIView animateWithDuration:0.35 animations:^{ self.view.frame = rect; }];
in iOS 7 you can use:
setNeedsStatusBarAppearanceUpdate
for example:
[self.mainViewController.navigationController setNeedsStatusBarAppearanceUpdate];
apple docs:
Call this method if the view controller's status bar attributes, such
as hidden/unhidden status or style, change. If you call this method
within an animation block, the changes are animated along with the
rest of the animation block.
use it only for iOS 7.
I am trying to get a popover to appear at a map kit annotation point but cannot find a "rect" in the annotation view properties to use the rect method of calling uipopovercontroller. If given an annotation on map kit how does one find the appropriate "frame"?
To give paul more information, here is my attempt: I have already used:
- (void)mapView:(MKMapView *)mapView2 annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{
NSLog(#"annotationView...");
MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager];
int choice = 0;
for (NSMutableDictionary *locationDictionary in [myGizmoClass searchResultsForResortLocation])
{
if([view.annotation.title isEqualToString:[locationDictionary objectForKey:#"name"]])
{
DetailViewTableStyleController *controller = [[DetailViewTableStyleController alloc] initWithlocationData:[[myGizmoClass searchResultsForResortLocation] objectAtIndex:choice] nibName:#"DetailViewTableStyle" bundle:[NSBundle mainBundle]];
controller.categoryCode = [locationDictionary objectForKey:#"category_code"] ;
//create a popover controller
popoverControllerDetail = [[UIPopoverController alloc] initWithContentViewController:controller];
// set contentsize
[popoverControllerDetail setPopoverContentSize:CGSizeMake(320,480)];
//present the popover view non-modal
[popoverControllerDetail presentPopoverFromRect:view.rightCalloutAccessoryView.frame inView:mapView2 permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
[controller release];
break;
}
choice = choice + 1;
}
}
And... I get a popover at the upper left at the edge of the mapview.
Can anyone tell me why? I am trying to get it to appear near the pin/annotationview.
Okay,
Here is what I found as the only work-around so far:
CGPoint annotationPoint = [mapView2 convertCoordinate:view.annotation.coordinate toPointToView:mapView2];
float boxDY=annotationPoint.y;
float boxDX=annotationPoint.x;
CGRect box = CGRectMake(boxDX,boxDY,5,5);
UILabel *displayLabel = [[UILabel alloc] initWithFrame:box];
[popoverControllerDetail presentPopoverFromRect:displayLabel.frame inView:mapView2 permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
[displayLabel release];
The bad part about this is that I am faking putting it at the view of the annotation. I simply get the annotation.coordinate of the annotation view (MKMapKit style coordinates) and convert the coordinates to screen coordinates. Then I create a uilabel (to get a frame-capable construct on the screen) placed where the Annotation View would be. I then tell the popovercontroller to appear at that frame location.
Here is the kicker. I do not add the uilabel to the mapView2. I simply release it once popovercontroller has drawn itself.
Hack, but clean.
The problem with the first example in this thread is in the call to presentPopoverFromRect:inView:permittedArrowDirections:animated:. The value for the inView: parameter is not correct. It should be view.rightCalloutAccessoryView.
That is, the rectangle's coordinates are relative to rightCalloutAccessoryView.
With this change, the behavior will be similar to the maps application which seems right.
If you have the MKAnnotationView, and you imply that you do, then you can use its frame property, as it is a UIView subclass.
Didn't read all the replies... but I had a similar problem. I wanted to show a popover box when I tap the accessory button in the callout bubble.
This is how I solved my problem.
First of, I center the map to the annotation I chose, which I think is always a good idea in this case. Then I implement the popover with a fixed size and position it at the correct position (in my opinion).
Here is a part of my code:
-(void)mapView:(MKMapView *)theMapView annotationView:(MKAnnotationView *)pin calloutAccessoryControlTapped:(UIControl *)control
{
UIViewController *detailController = [[detailedPinView alloc] initWithNibName:#"detailedPinView"
bundle:nil
annotationView:pin];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
UIPopoverController* aPopover = [[UIPopoverController alloc] initWithContentViewController:detailController];
[aPopover setDelegate:self];
[aPopover setPopoverContentSize:CGSizeMake(320, 320) animated:YES];
[detailController setPopover:aPopover];
[detailController release];
[mapView deselectAnnotation:pin.annotation animated:YES];
self.popoverController = aPopover;
[mapView setCenterCoordinate:pin.annotation.coordinate animated:YES];
[self.popoverController presentPopoverFromRect:CGRectMake(382,498,0,0) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
}
else
{
[self presentModalViewController: detailController animated:YES];
}
[detailController release];
}
The issue with presentPopoverFromRect:inView:permittedArrowDirections:animated is that if the content size of the popover is greater than what fits on the screen (because the annotation is close the the edge of the screen, for example), the system will first (at least partially) disregard the rectangle, and put the tip of the arrow somewhere in the middle of the view. I've solved this by doing it this way:
self.popover = [[UIPopoverController alloc] initWithContentViewController:placeDetailViewController];
[self.popover presentPopoverFromRect:CGRectMake(0, 0, 29, 31) inView:view.rightCalloutAccessoryView permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
The rightCalloutAccessoryView is normally a detail disclosure button, which has a size of 29 by 31. So, what I do, is create a 29 by 31 rectangle, which covers the entire accessory view. Then I present the popover from the accessory view. By increasing the 29 to 40, the popover arrow will be outside the callout bubble, but only if there is enough space to present the entire callout on the screen. If not, the arrow will be moved inside the bubble. To maintain a consistent interface, I left the width at 29.