I have a UITableView. On the tap of a button i want to display my custom view, then, once the view is visible, remove a particular item from the tableview. The custom view hides the tableview so i would like the remove to occur after this new view is visible.
Currently, i have this, which adds the custom view and then should remove the item, and reload the table, but the reload is occurring just as the animation ends (i have an animation block, changing the views alpha), so i can see the update.
[self.view addSubview:customView];
[itemArray removeObject:object];
[self.tableView reloadData];
How can i delay the reload until after the view is visible?
Thanks.
Try adding reloadData to viewDidAppear:
-(void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.tableView reloadData];
}
That should give you the required delay.
You mention you're animating the view yourself; you should call reloadData when the animation completes by using something like:
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(methodThatCallsReloadData)];
// or even
[UIView setAnimationDelegate:self.tableView];
[UIView setAnimationDidStopSelector:#selector(reloadData)];
or if you're using the block-based API:
[UIView animateWithDuration:... completion:^(BOOL finished) {
[self.tableView reloadData];
}];
or you can use the performSelector method :
[self performSelector:#selector(myOtherMethod) withObject:nil afterDelay:1.5];
Related
I have a design where I need to swap in and out two tableviews using one viewcontroller (I need the same navigation title to appear for both). So I've created each tableview in its own subclassed tableviewcontroller class, then I've included a class variable for each in my viewcontroller. Each tableview has the viewcontroller as a parent and calls the viewcontrollers swap method when a swap needs to occur. This method and the viewDidLoad method is listed below:
- (void) viewDidLoad
{
[super viewDidLoad];
[[self navigationItem] setTitle: AddAPhotoViewControllerTitle];
SelectAnAlbumTableViewController *selectAnAlbumTableViewControllerTemp = [[SelectAnAlbumTableViewController alloc] initWithParent: self];
[self setSelectAnAlbumTableViewController: selectAnAlbumTableViewControllerTemp];
[selectAnAlbumTableViewControllerTemp release];
[[self view] insertSubview: [[self selectAnAlbumTableViewController] tableView] atIndex: 0];
}
- (void) switchTableViews
{
if ([[[self selectAnAlbumTableViewController] tableView] superview] == nil)
{
[[self view] insertSubview: [[self selectAnAlbumTableViewController] tableView] atIndex: 0];
[[[self selectAPhotoTableViewController] tableView] removeFromSuperview];
[selectAPhotoTableViewController release];
selectAPhotoTableViewController = nil;
}
else
{
SelectAPhotoTableViewController *selectAPhotoTableViewControllerTemp = [[SelectAPhotoTableViewController alloc] initWithAssetGroup: [[self selectAnAlbumTableViewController] assetGroup] parent: self];
[self setSelectAPhotoTableViewController: selectAPhotoTableViewControllerTemp];
[selectAPhotoTableViewControllerTemp release];
[[self view] insertSubview: [[self selectAPhotoTableViewController] tableView] atIndex: 0];
[[[self selectAnAlbumTableViewController] tableView] removeFromSuperview];
}
I have 2 questions:
1) When I'm doing the swap, I'm inserting the new tableview, animating the transition (I didn't include the animation code to keep it concise), then removing the old tableview from the superview. Is this the correct order / correct way to do this? It works fine, but I'm wondering if there isn't some code smell here.
2) Using this design pattern, what would be the best way to go about putting in an activity indicator that can be displayed while each tableview is loading? I've tried implementing the indicator in the viewcontroller, and it seemed to work ok, but I wasn't sure how to set it's position? In terms of what? The center of? I guess this goes back to the first question I asked to, what is the superview and when, or if there is even a superview?
Just use two UITableViews.
Instantiate them and put them in instance variables. If you need to, save the state i.e. which table view is being displayed.
I see nothing wrong with it. I assume your reason is that each table controller has its own logic as to deserve a separate class. It is a bit unorthodox to embed controllers inside controllers, and you'll have to pass the view lifecycle calls.
Example:
-(void) switch
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.75];
[UIView setAnimationDelegate:self];
if ([self.visibleVC isKindOfClass:[OrangeVC class]])
{
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.view cache:YES];
[self.visibleVC viewWillDisappear:TRUE];
[self.visibleVC.view removeFromSuperview];
self.visibleVC = self.appleVC;
[self.view addSubview:self.visibleVC.view];
}
else if ([self.visibleVC isKindOfClass:[AppleVC class]])
{
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.view cache:YES];
[self.visibleVC viewWillDisappear:TRUE];
[self.visibleVC.view removeFromSuperview];
self.visibleVC = self.orangeVC;
[self.view addSubview:self.visibleVC.view];
}
[self.visibleVC viewWillAppear:TRUE];
[UIView commitAnimations];
}
Note the viewWillAppear calls, you need at least that to wake up the table. I'm not sure if you should call viewWillDisappear, but it doesn't hurt. If you don't, watch it in instruments in case the controller is not cleaning up properly.
You can add any activity hud in the self.view of the parent controller, use a hud widget, darken the view currently loading, ..., it's a design issue more than technical. Remember to disable the user interaction while you load (userInteractionEnabled=NO) on the parent view and the button that initiated the switch.
Anyone have a walk around on animating the process when switching tabs?
I am currently doing this with 2 issues.
1: It messes up my UIControl colors.
2: It switches the tab first, thereafter perform the animation.
I want to implement something like the effect when pushing a view to navigation stack.
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:bookingVC.view cache:YES];
[UIView commitAnimations];
[self.tabBarController setSelectedIndex:0];
Right now I am doing this, which doesn't have any animation effect.
NSArray *tabBarVC = [self.tabBarController viewControllers];
UINavigationController *firstTabNC = [tabBarVC objectAtIndex:0];
BookingViewController *bookingVC = (BookingViewController *)[[firstTabNC viewControllers] objectAtIndex:0];
[bookingVC setNSStringProperty:newString]; //this string will be displayed on a UITextField whenever viewDidAppear;
// So, I need some animation here when I switch tabs to make it more obvious that the UITextField is updated.
[self.tabBarController setSelectedIndex:0];
I am totally clueless on creating my own animation, please do guide me along. I am opened to ideas to better the presentation.
Thanks.
Any reason you need to use the pre-iOS4 way of animating? Animation blocks simplify this:
[UIView animateWithDuration:1.0
delay:0
options:UIViewAnimationOptionTransitionCurlUp
animations:
^{
// show your desired view however you do it
}
completion:
^(BOOL finished) {
[self.tabBarController setSelectedIndex:0];
}];
EDIT: Sorry, I didn't pick up you were doing a view transition somehow.. silly. There's another block-based method for that case:
[UIView transitionWithView:bookingVC.view
duration:1.0
options:UIViewAnimationOptionsTransitionCurlUp
animations:
^{
// do view transitioning here
}
completion:
^(BOOL finished) {
[self.tabBarController setSelectedIndex:0];
}];
EDIT 2: Alright, so you seem to need some help with the context of all this. You should be animating changes to active tabs in the UITabBarControllerDelegate protocol (which your custom class would implement). In particular, the tabBarController:didSelectViewController: method.
I'd recommend you first override the tabBarController:shouldSelectViewController: method to make sure that you don't accidentally animate a transition from a tab to the same tab:
- (BOOL)tabBarController:(UITabBarController *)tabBarController
shouldSelectViewController:(UIViewController *)viewController
{
return viewController != [tabBarController selectedViewController];
}
In didSelectViewController:, do the animation. I'm also going to take this opportunity to rework your code, because if you're doing it within this method then the selectedIndex of the tab bar controller is handled automatically, and whatever you were doing with bookingVC.view can be replaced by a direct transition between the active tab controller and the new (selected) one:
- (void)tabBarController:(UITabBarController *)tabBarController
didSelectViewController:(UIViewController *)viewController
{
[UIView transitionFromView:[tabBarController selectedViewController].view
toView:viewController.view
duration:1.0
options:UIViewAnimationOptionsTransitionCurlUp |
UIViewAnimationOptionsShowHideTransitionViews
completion:nil];
}
Note here that I use UIViewAnimationOptionsShowHideTransitionViews as part of your animation options. I'm doing this because our animation is on top of the system already changing the active tab, so we don't want to muck it up by trying to remove the view from the view heirarchy at the same time as the system is.
Try placing your animation sequence in didSelectItem::
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item;
Then set up a modalView controller and animate the page curl effect from there.
Hey all... I have a view controller (A) which on some action, alloc init's another view controller (B) and then adds B's view to its view as a subview. So now ViewController B's view is a subview of ViewController A. The problem I have is If I simply remove B's view from A it seems to still stick around for example. View B contains a web view, when I load a video on the webView, even after I remove the view from view Controller A's view I can still hear the video??
How can I destroy viewcontroller B and remove its subview from A? Im finding this tricky as I dont really push it onto a navigationcontroller's stack which I can just pop from... I hope this makes sense, if not please say and I will try and clarify.
Many thanks
Jules
-(void)showNewsWebView:(int)index {
NewsWebViewController *myWebView = [[[NewsWebViewController alloc] initWithNibName:#"NewsWebViewController" bundle:nil]autorelease];
//setup webview with request etc
[[self.view.superview superview] addSubview:myWebView.view];
myWebView.alpha = 0.
[UIView beginAnimations:#"test" context:nil];
[UIView setAnimationDuration:.3];
myWebView.view.alpha = 1.;
[UIView commitAnimations];
}
//called after delegate callback from webviewcontroller
- (void)newsWebViewDismissedView:(NewsWebViewController *)controller {
[UIView beginAnimations:#"test" context:nil];
[UIView setAnimationDuration:.3];
controller.view.alpha = 0.0;
[self performSelector:#selector(removeView:) withObject:controller.view afterDelay:.5];
[UIView commitAnimations];
}
-(void) removeView:(UIView *)view {
[view removeFromSuperview];
view = nil;
}
Does ViewController B really need to be a ViewController?
If you're adding subviews you should probably have B subclass UIView instead of UIViewController. Adding B's view as a subview essentially negates any advantage you'd have of B being a ViewController.
Anyway to answer your question. You might want to make viewcontroller B an ivar of A so that viewcontroller A can manage the memory of viewController B. Once you remove the view of B from A, you can release viewcontroller B from memory (I still don't support this as it sounds like ineffective code. You should probably state what you're aiming to do, and post some code as to how you're doing it so we can help you out better :) )
EDIT:
From your code seems like you should just be pushing and popping. Are you using MyWebViewController just to show a webpage? You might be better off using a simple UIWebView.
I also noticed something wrong in your animation code for setting alpha to 0. If you want some method to be executed after an animation ends you should use the following code:
//called after delegate callback from webviewcontroller
- (void)newsWebViewDismissedView:(NewsWebViewController *)controller {
[UIView beginAnimations:#"test" context:nil];
[UIView setAnimationDuration:.3];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(removeView)];
controller.view.alpha = 0.0;
[UIView commitAnimations];
}
ViewControllerB *vc = [[[ViewControllerB alloc] initWithNibName:#"SomeNib" bundle:nil] autorelease];
This should do the trick. Autoreleasing the view controller at the end of its initial allocation should tell the application to deallocate it after you remove the view from viewcontroller A since nothing else is holding a retain value on it. Though using a navigation controller might be an easier solution if you are willing to rework your code to push and pop the view instead
I have a toolbar in my RootViewController and I then hide the toolbar in a SubViewController using the following code:
RootViewController
- (void)viewDidLoad {
...
[self.navigationController setToolbarHidden:FALSE animated:FALSE];
...
}
- (void)viewDidAppear:(BOOL)animated {
[self.navigationController setToolbarHidden:FALSE animated:TRUE];
[super viewDidAppear:animated];
}
SubViewController
- (void)viewDidLoad {
...
[self.navigationController setToolbarHidden:YES animated:YES];
[super viewDidLoad];
}
This all works as expected i.e. the toolbar will be hidden and unhidden using a nice vertical animation when moving from one view to another and back again.
However, there appears to be a nasty animation issue when moving from the RootViewController to the SubViewController. As the toolbar is being hidden, a white bar will appear where the toolbar was, and then quickly disappears across the screen from right to left.
Hopefully I've explained this well enough for you to understand.
Any ideas how to fix this?
Have you tried doing the animation in SubViewController's -viewWillAppear: method? You may have better luck there.
I have seen this problem a couple of times and I have found that putting the call to setToolbarHidden:animated: in the viewWillAppear: method does not always give a smooth animation with no white rectangle artifacts.
What does always work is to put the setToolbarHidden:animated: call in the viewDidAppear: method. This means that the toolbar hiding animation is triggered after the navigation controller has finished pushing the new view onto the stack, so no white rectangles. However, it also means that the whole animation is in two stages: the first animates the view in, the second hides the toolbar, so you have the appearance of a "delayed" toolbar hide. I acknowledge that this isn't always what you want.
TRY THIS
- (IBAction)hideTheToolBar:(id)sender{
//[toolBar setHidden:YES];
if (toolBar.hidden == NO)
{
[UIView animateWithDuration:0.25 delay:0.0
options:UIViewAnimationOptionCurveLinear | UIViewAnimationOptionAllowUserInteraction
animations:^(void)
{
toolBar.alpha = 0.0f;
}
completion:^(BOOL finished)
{
toolBar.hidden = YES;
}
];
}
}
You can (probably should) do this in the subview controller's designated initializer, e.g. initWithNibName:bundle:
I have found very useful to set the hidesBottomBarWhenPushed property in the init of your view controller.
For instance:
- (id)init
{
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
// Custom initialization
self.navigationItem.rightBarButtonItem = self.editButtonItem;
self.hidesBottomBarWhenPushed = YES;
}
return self;
}
It hides those spurious toolbars that appear in the push and pop transitions. Also, it frees you from manually hiding the toolbar in the ViewWillAppear method or similar approaches.
i have coded following in appDelegeate .m file .but i cant run presentModalViewController method.if i run [self.window addSubview:mview] ,it does not show the result..?any help
to go from one controller to another controller?here mtController is Navigationcontroller.
- (void)flip
{
MViewController *mview = [[MViewController alloc] init];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:2.0];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft
forView:window
cache:YES];
[mtController.view removeFromSuperview];
[self.window addSubview:mview];
// [self presentModalViewController:mailView animated:YES];
[UIView commitAnimations];
[mailView release]
}
If you want to add a NavigationController to a window you are supposed to call
[self.window addSubview:mview.view];
And if you want to go from one view in a NavigationController to another view, the correct thing to do would be to push the new ViewController.
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
In your code sample you create an object called mview, but then below in the presentModalViewController you reference mailView (and in the release). Is that a mistake?
After you add the mview.view to the window, is it the only view on the stack? If not, you might need to bring it to the front. Also, assuming the release statement at the bottom was meant to be [mview release] you're going to have another problem if you don't save/retain that view controller. I don't believe adding it to the window subviews retains it.