I have a UIView that contains a subview called menuView managed by a MenuViewController.
I wrote that code :
- (void) viewDidLoad
{
[self.menuView setFrame:CGRectOffset(self.menuView.frame, 0.0, self.menuView.frame.size.height)];
[super viewDidLoad];
}
- (void) viewDidAppear:(BOOL)animated
{
[UIView beginAnimations:#"SomeAnimation" context:nil];
[UIView setAnimationDuration:2.0];
[UIView setAnimationDelegate:self];
[UIView setAnimationTransition:UIViewAnimationTransitionNone forView:self.menuView cache:YES];
[self.menuView setFrame:CGRectOffset(self.menuView.frame, 0.0, -self.menuView.frame.size.height)];
[UIView commitAnimations];
[super viewDidAppear:animated]; // tried at begining too
}
But when loaded, no animation is visible... I also tried with viewWillAppear with no change.
it is called outside MenuViewController with :
- (IBAction) showMenu
{
MenuViewController* menuController = [[MenuViewController alloc] initWithNibName:#"Menu" bundle:nil];];
[self.view addSubview:menuController.view];
// [menuController release]; for try
}
What's the problem ?
P.S. : What I want to do is when the view is displayed, some part of that view (the menuView) move. That view (partially transparent) is intended to cover a superview that is owned by another ViewController.
Are you calling the viewDidAppear method from your view controller? Some controllers, such as the UINavigationController, will take care of that, but if you're using a custom controller, you'll need to call it. Typically, you would call viewWillAppear, then add the view, the call viewDidAppear.
So your code block would be:
- (IBAction) showMenu
{
MenuViewController* menuController = [[MenuViewController alloc] initWithNibName:#"Menu" bundle:nil];];
[menuController viewWillAppear:YES];
[self.view addSubview:menuController.view];
[menuController viewDidlAppear:YES];
[menuController release];
}
Can you confirm that self.menuView is actually properly initialized and is not nil?
Perhaps you should first call the super initialization of your method ?
[super viewDidAppear:animated]
Related
Is it possible to present a modal view controller in such a way that the modal view is confined to the space included in a CGRect?
If not, please explain how to replicate the cross-disolve modal view transition between two views.
Thanks.
To cross-dissolve to a regular view controller, you can set it's modalTransitionStyle to UIModalTransitionStyleCrossDissolve then present it modally.
To perform a cross-dissolve between some pair of subviews (confined to their frame CGRects), you can use this UIView method:
+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion.
Here's how you might use that in code:
#interface ViewController ()
#property(strong,nonatomic) UIView *redView;
#property(strong,nonatomic) UIView *blueView;
#end
#implementation ViewController
#synthesize redView=_redView;
#synthesize blueView=_blueView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.redView = [[UIView alloc] initWithFrame:CGRectMake(40.0, 40.0, 240.0, 100.0)];
self.redView.backgroundColor = [UIColor redColor];
[self.view addSubview:self.redView];
self.blueView = [[UIView alloc] initWithFrame:CGRectMake(40.0, 40.0, 240.0, 100.0)];
self.blueView.backgroundColor = [UIColor blueColor];
}
- (IBAction)crossDisolve:(id)sender {
UIView *fromView = (self.redView.superview)? self.redView : self.blueView;
UIView *toView = (fromView==self.redView)? self.blueView : self.redView;
[UIView transitionFromView:fromView
toView:toView
duration:1.0
options:UIViewAnimationOptionTransitionCrossDissolve
completion:^(BOOL finished) {NSLog(#"done!");}
];
// now the fromView has been removed from the hierarchy and the toView has been added
// please note that this code depends on ARC to release objects correctly
}
The harder part of your question is the idea of making that new sub-view "modal" by which I'm guessing you mean that covers only part of the display but takes input focus exclusively. The nearest thing to that in the SDK is UIAlertView.
How about you just use UIView animations:
UIView* view = [[UIView alloc] initWithFrame:CGRectMake(x,y,w,h)];
[view setAlpha:0];
[self.view addSubView:view];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[view setAlpha:1];
[UIView commitAnimations];
Voila! It fades in! :)
I want to fade the whole screen (including navigation bar) to black when a user presses a button on a uinavigationcontroler, before showing a new view. (i don't want to push this new view, for various reasons).
How would I achieve this?
EDIT
Thanks to Mac and Eiko, I have figured it out. Here's the code I used. Not sure if it is optimal, but it does the trick.
// this is called from a programmatically constructed button.
// change (void) to (IBAction) if linking from IB.
- (void)fadeAndShow:(id)sender
{
// blackView is a #property which has been #synthesize-d
// do I really need to alloc and init this?
blackView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
blackView.alpha = 0.0;
[blackView setBackgroundColor:[UIColor blackColor]];
[self.navigationController.navigationBar.superview addSubview:blackView];
[UIView beginAnimations:#"fadeAway" context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.75];
[UIView setAnimationDidStopSelector:#selector(showNewScreen:finished:context:)];
blackView.alpha = 1.0;
[UIView commitAnimations];
}
-(void)showNewScreen:(NSString *)animationID finished:(BOOL)finished context:(void *)context
{
// I guess you could fade in somewhere in the new view controller.
// don't know how to fade back in this view tho... viewDidAppear?
NewViewController *controller = [[NewViewController alloc] initWithNibName:#"NewView" bundle:nil];
[self.navigationController setNavigationBarHidden:YES];
controller.hidesBottomBarWhenPushed = YES;
[[self navigationController] pushViewController:controller animated:NO];
[blackView removeFromSuperview];
[controller release];
}
Off the top of my head (I haven't actually tested the following at all):
-(IBAction) buttonClicked:(id) sender
{
[UIView beginAnimations:#"myAnimation" context:nil];
[UIView setAnimationDuration:ANIMATION_DURATION];
blackView.alpha = 1.0;
[UIView commitAnimations];
}
Create a UIView in the navigationbar's superview (which I'm assuming is window-sized) that is the same size as the window.
Set that view's backgroundColor to [UIColor blackColor], and its alpha to 0.0.
In your button handler do something like the above (assuming your new UIView is blackView and ANIMATION_DURATION is your desired animation time in seconds).
Then, add your new view on top.
EDIT: too quick for me Eiko! Also, code at the top since the ordered list seems to screw around with the code formatting - sorry the answer reads a little odd.
You can add a black coloured UIView in screen size on top of your current view, and animate its alpha from 0 to 1. When the animation is done, add your new view. You can remove the black one then. Animate from 1 to 0 for the opposite effect - going from black to the content).
I have a seachButton in the navigation bar which upon hitting calls following method:
- (IBAction)search:(id)sender
{
if (nil == searchViewController)
searchViewController = [[SearchViewController alloc] initWithNibName:#"SearchViewController" bundle:nil];
searchViewController.view.backgroundColor = [UIColor clearColor];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown
forView:searchViewController.view
cache:NO];
[self.view addSubview:searchViewController.view];
[UIView commitAnimations];
}
It should load SearchViewController.xib which contains a view with UISearchBar and two buttons. When I call the search method for the first time, the view appears very quickly with some weird animation, when I call it again, the animation is alright. Does anyone have a clue what could be wrong?
Add both views to window in appdelegate, I had the same problem you had and this worked. Strange because later on I remove them from the superview but it is still working.
A UIViewController does not load its view until the view method is called. I guess the problem is the view is not loaded when you call the animation first time. You can try to modify you code like this:
if (nil == searchViewController) {
searchViewController = [[SearchViewController alloc] initWithNibName:#"SearchViewController" bundle:nil];
searchViewController.view;
}
Try putting the animation code in the viewDidLoad function. This ensures all assets and views from the nib file have been loaded and are able to be used by your app.
- (IBAction)search:(id)sender
{
if (nil == searchViewController)
searchViewController = [[SearchViewController alloc] initWithNibName:#"SearchViewController" bundle:nil];
[self.view addSubview:searchViewController.view];
}
-(void)viewDidLoad
{
self.view.backgroundColor = [UIColor clearColor];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown
forView:self.view
cache:NO];
[UIView commitAnimations];
}
Try putting the code for animations and view loading in the viewDidAppear: method instead of viewDidLoad: (which Chris suggested above).
In general, I have had problems doing view related things in viewDidLoad:. But doing those in viewDidAppear: has always helped (I might be missing some subtlety that causes this behavior).
I have a view that is added as a subview in a viewcontroller. The subview has delegates methods and the viewcontroller is assinged as its delegate. The subview has a animation and calls a delegate method when the animation is finished.
The problem is that when the viewcontroller is removed from the view by the navigationcontroller the subview isn't deallocated. Probably because it's release count is >0. When the viewcontroller is removed from view before the subview animation finishes the subview tries to call the delegate (which is the viewcontroller which doesn't exist anymore) method and i get a EXC_BAD_ACCESS.
Maybe some sample will clarify things ;):
The view
- (void)somefunction {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(viewDidAnimate:finished:context:)];
[UIView setAnimationDuration:0.3];
self.frame = CGRectMake(0, 320, 320, 47);
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
[UIView commitAnimations];
}
-(void)viewDidAnimate:(NSString *)animationID finished:(BOOL)finished context:(void *)context{
if (self.delegate != NULL && [self.delegate respondsToSelector:#selector(viewWasAnimated:)]) {
[delegate viewWasAnimated:self];
}
}
viewcontroller
- (void)viewDidLoad{
[super viewDidLoad];
MYView *myview = [[MYView alloc] init];
myview.delegate = self;
[self.view addSubview:myview];
[myview release];
}
- (void)viewWasAnimated:(MYView *)view{
}
I found out that after the
[UIView commitAnimations];
line the retainCount of the view is 2, and after the
[delegate viewWasAnimated:self];
the release count is 3. So this is probably why the view isn't released. I know I am not supposed to look at retain counts but don't know what else to do.
Your view is not deallocated because the UIView beginAnimation Block retains it for the whole animation duration. So if your controller can be released during your animation you can remove the UIView from the controller's view in the dealloc and in the animationDidStop: method check if the superview is equal to nil if so, then don't send the message
-(void)viewDidAnimate:(NSString *)animationID finished:(BOOL)finished context:(void *)context{
if (self.superview != nil && self.delegate != NULL && [self.delegate respondsToSelector:#selector(viewWasAnimated:)] ) {
[delegate viewWasAnimated:self];
}
}
Another approach you could take is to cancel the UIView animation but that is not as intuitive.
In the dealloc method of the UIViewController do
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView commitAnimations];
This will cancel the current animations. And you will receive finished == NO in the animationDidStop message
(void)viewDidAnimate:(NSString *)animationID finished:(BOOL)finished context:(void *)context{
if ( finished && self.delegate != NULL && [self.delegate respondsToSelector:#selector(viewWasAnimated:)] ) {
[delegate viewWasAnimated:self];
}
}
First off i guess it's a typo but:
[self.view addSubview:adview];
should be
[self.view addSubview:myview];
About your question:
1-Put MYView *myview; in your viewcontroller.h
2-Before you release the viewcontroller, call:
[myview removeFromSuperView];
This should deallocate your subview.(if there are no other additional retains)
Good luck.
How to create that black/gray modal popup kind of view that many apps use, when some long pending operation is in progress?
Like when using location based services, loading a webpage, the screen goes dim and there is a modal view showing a spinning icon "Please wait..."
Example in the following screenshot:
This is actually the undocumented (in 2.2.1 anyway) UIProgressHUD. Create one like this:
In your .h:
#interface UIProgressHUD : NSObject
- (UIProgressHUD *) initWithWindow: (UIView*)aWindow;
- (void) show: (BOOL)aShow;
- (void) setText: (NSString*)aText;
#end
In your .m:
- (void) killHUD: (id)aHUD
{
[aHUD show:NO];
[aHUD release];
}
- (void) presentSheet
{
id HUD = [[UIProgressHUD alloc] initWithWindow:[contentView superview]];
[HUD setText:#"Doing something slow. Please wait."];
[HUD show:YES];
[self performSelector:#selector(killHUD:) withObject:HUD afterDelay:5.0];
}
If you want to avoid undocumented api you can also take a look at MBProgressHUD. It's similar to UIProgressHUD and even has some additional features.
I think the simplest (a few lines of code), fully documented and most beautiful way is to use the UIAlertView with a UIActivityIndicatorView:
http://iosdevelopertips.com/user-interface/uialertview-without-buttons-please-wait-dialog.html
(source: iosdevelopertips.com)
If you add a UIView as a subview of the main window it will cover the entire UI. Make it partially transparent and partially translucent and it will look like a popup.
This example shows how to fade the Default.png splash screen, starting with that it's pretty straightforward to add a couple methods to your application delegate (that has a pointer to the main window) to present and dismiss the progress view.
Take a look at the Wordpress iPhone app (http://iphone.wordpress.org/) for an example of how to do this without using any undocumented API's.
I use LoadingHUDView for this purpose, and it works always.
get LoadingHUDView.m and LoadingHUDView.h and do the following in your base class (or whatever)
#pragma mark ActivityIndicator Methods
-(void) showModalActivityIndicator:(NSString *)message
{
loadingView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)]retain];// origional
//loadingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)]; //testing
loadingView.backgroundColor = [UIColor grayColor]; //[UIColor colorWithRed:1 green:1 blue:1 alpha:0.3];
LoadingHUDView *loadHud = [[LoadingHUDView alloc] initWithTitle:message];
loadHud.center = CGPointMake(160, 290);
[loadingView addSubview:loadHud];
[loadHud startAnimating];
[loadHud release];
[loadingView setAlpha:0.0];
[self.tableView addSubview:loadingView];
[UIView beginAnimations:#"fadeOutSync" context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.5];
[loadingView setAlpha:0.5];
[UIView commitAnimations];
}
-(void) hideModalActivityIndicator {
if (loadingView) {
[UIView beginAnimations:#"fadeOutSync" context:NULL];
[UIView setAnimationDidStopSelector:#selector (removeTranparentView) ];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.5];
[loadingView setAlpha:0];
[UIView commitAnimations];
}
}
-(void)removeTranparentView
{
[loadingView removeFromSuperview];
[loadingView release];
loadingView = nil;
}
HOPE THIS HELPS.
thank you
In XIB file, place UIView and UIActivityIndicatorView.
Set ActivityIndicatorView Property SetAnimated to Yes.
#property (retain, nonatomic) IBOutlet UIView* m_LoadingView;
While Starting long operations, Add the LoadingView to the view
m_LoadingView.layer.cornerRadius = 10;
m_LoadingView.center = self.view.center;
[self.view addSubview:m_LoadingView];
After completing the process, Remove LoadingView from super view.
[m_LoadingView removeFromSuperView];