Leveled UITableView without NavigationController - ios

I have data structured as a tree (and showed in UITableView) and I would like to show it the way it is usually done using UINavigationController and pushing child views on to the stack. The problem is that my UITableView (in an iPad app) takes only 1/6 of the screen (there is a UINavigationController that handles other fullscreen iPad screens in this app, so using NavigationController to control the levels of table is the good way).
Is there a simple way to get a good visual effect of changing the levels of TableView without using NavigationController? Right now I just change the data source and reload data, but that does something like a flicker on the screen (the user can't really see, that the structure of the data is tree-like).
I thought of creating a few TableViews and then just animating the resize (from full width to 0 the TableView that we are leaving, and from 0 to width at the same time of the another one) of a table just to make something like segue effect - but I am not sure if this is a good approach.

You can use reloadSections:withRowAnimation: with UITableViewRowAnimationLeft and UITableViewRowAnimationRight instead of a default reloadData. This will look like the push you are looking for.
Reference: http://developer.apple.com/library/ios/documentation/uikit/reference/UITableView_Class/Reference/Reference.html#//apple_ref/occ/instm/UITableView/reloadSections:withRowAnimation:

You can try this code (after you set the proper values:))
__block CGRect frame = tableView.frame;
[UIView animateWithDuration:0.5 animations:^{
frame.origin.x -=tableView.frame.size.width;
} completion:^(BOOL finished) {
frame.origin.x = yourViewThatContainsTheTable.frame.size.width
tableView.frame = frame;
[tableView reloadData];
[UIView animateWithDuration:0.3 animations:^{
frame.origin.x = 0;
tableView.frame = frame;
}];
}];
Basically if you are using subclasses of UIView you can use [UIView animateWith...] methods to perform simple animations or you can use CAAnimations/CABasicAnimation/CAAnimationGroup/CAKeyframeAnimation for more complex animations on subclasses of UIView.
If you want a custom transition for a UIViewController you can use CATransition.

Related

How to create an animation like in Facebook Pages top menu?

In facebook'a new app, Pages, when you swipe down the menu appears at the top. The animation I am trying to do is the animation when you tap the settings button which is when each cell moves to the left one at a time, but very smoothly. How can I achieve such an animation without using Facebook's pop engine in Objective C ?
Bear in mind that each cell is probably a custom subclass of UIView (i.e. not UITableViewCell or other apple class) so you will have to design your UI carefully.
The animation, however, is easily achievable using the UIView class. Apple's documentation is a good place to start to learn about animation.
What you want is something like:
NSTimeInterval delay = 0.2;
for (UIView *myView in myViewArray) {
[UIView animateWithDuration:0.5 delay:delay options:UIViewAnimationOptionCurveEaseInOut animations:^{
/*
* This is where you set the properties you want to animate.
* If you're using AutoLayout (i.e. NSLayoutConstraint) you need to set the constant property of the relevant constraint, not the view's frame property.
*/
} completion:nil];
delay += 0.2;
}
This will provide you with animations that occur one after the other (0.2 seconds apart).

UIView's frame gets overridden by Storyboard on initial load

I need to change the width of a subview depending on, say, available disk space. So I have a selector that's called upon the view controller's viewWillLayoutSubviews as such:
CGRect rect = self.usageView.bounds;
rect.size.width = self.someCalculatedwidth;
[self.usageView setFrame:rect];
[self.usageView setNeedsDisplay];
Really, I just want to change the width of the subview. It works if the view controller is the initial controller; however, if it is transitioned from, say, a Push Segue it stopped working. What happens is I'd see my desired width rendered for moment but then it gets changed to the original width as per its blueprint on the Storyboard.
The behavior feels like the iOS caches the original (storyboard) view in a block during the push segue animation, and upon completion it executes the block which doesn't have my calculations above; thereby overridden the desired view. Any idea?
There are two approaches.
The first approach is to circumvent the problem, with a side benefit of cool animation. Since I have no clue when or how many times or exactly how iOS enforces the auto-constraints, I simply delay my UIView modifications and smooth it out with animation. So I wrap view animation around the code in my selector:
[UIView animateWithDuration:.25 animations:^{
CGRect rect = self.usageView.bounds;
rect.size.width = self.someCalculatedwidth;
[self.usageView setFrame:rect];
[self.usageView setNeedsDisplay];
}];
Then I'd call my selector in my controller's viewDidAppear:animated:
[self performSelector:#selector(resetUsageView) withObject:self afterDelay:0.0];
Now most would say this is a cop-out or a kludge. And that's fair.
In the second approach I attack the root of the problem - auto layout/constraints. I add a Width constraint to my usageView in the storyboard, and connect its IBOutlet to my code.
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *usageViewWidthConstraint;
Then I modify the constraint as per the calculated width in my controller's viewWillAppear:animated:
[self.usageViewWidthConstraint setConstant:self.someCalculatedWidth];
Voila! A one liner.
Now what if I want animation given I got a taste of it from the first approach? Well, constraint changes are outside the realm of animation so basically I'd need a combination of the first and second approaches. I change the UIView's frame for animation, and modify the constraint as well to "set the record straight":
[UIView animateWithDuration:.25 animations:^{
CGRect rect = self.usageView.bounds;
rect.size.width = self.someCalculatedwidth;
[self.usageView setFrame:rect];
[self.usageViewWidthConstraint setConstant:self.someCalculatedWidth]; // <<<<
[self.usageView setNeedsDisplay];
}];

Custom transition between UIViewControllers

I've just changed my app from being TabView driven to CollectionView driven, as there are too many sections of my app to be feasible for a TabView. When you start the app you are presented with several items in a CollectionView and selecting any of these items will take you to the relevant section of the app.
In XCode, the collection view lives in its own storyboard and each section of the app has its own storyboard.
In the CollectionView's didSelectItemAtIndexPath, I launch the relevant starboard as follows;
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"relevant_storyboard" bundle:nil];
UIViewController* vc = [storyboard instantiateInitialViewController];
[self presentViewController:vc animated:YES completion:nil];
Now, none of the built-in transition animations really suit launching from a CollectionView, so I'd really like a custom effect, such as zoom in. However, I'm struggling to find any decent examples that work for me to create any kind of custom transition. I've tried [UIView transitionFromView], but I don't think that suits transitioning between UIViewControllers. I've tried transitionFromViewController:toViewController: but don't think I have the view hierarchy set up correctly. I've also tried using CATransition without success.
I've thought about doing it with a custom segue but, as my CollectionView is in it's own storyboard and have separate storyboards for each section of my app, I can't see how I can do this. At least not without having all sections of the app inside one storyboard, which would make the storyboard huge and difficult to manage.
So, can anyone give me any code examples or pointers on how I can solve this?
In my app I used a similar effect to zoom in from a thumbnail in a collection view cell to a child view controller that took up the entire screen. You could conceivably do the same thing for a navigation controller push as well.
In my code, I had a scoreView property on the cell subclass that I wanted to zoom up into the full screen. In your case, you may want to use a UIImageView with a screenshot of your new view. Alternatively, you could present the new view controller with a screenshot of the old view controller and then animate from there.
//Instantiate the view controller to be animated in...
//If the cell is not completely inside the collection view's frame, a dissolve animation might be more graceful.
BOOL dissolveAnimation = !CGRectContainsRect(CGRectMake(0, 0, self.collectionView.frame.size.width, self.collectionView.frame.size.height), cellRect);
//Get the frame of the cell in self.view coordinates, then the frame of the thumbnail view
CGRect cellRect = [self.collectionView layoutAttributesForItemAtIndexPath:indexPath].frame;
cellRect = CGRectOffset(cellRect, 0.0, -self.collectionView.contentOffset.y);
VSScoreCell *scoreCell = (VSScoreCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
CGRect scoreRect = dissolveAnimation ? CGRectMake(0.0, 0.0, self.view.frame.size.width, self.view.frame.size.height) : CGRectMake(cellRect.origin.x + scoreCell.scoreView.frame.origin.x, cellRect.origin.y + scoreCell.scoreView.frame.origin.y, scoreCell.scoreView.frame.size.width, scoreCell.scoreView.frame.size.height);
VSScoreView *scoreView = [[VSScoreView alloc] initWithFrame:scoreRect];
//Initialize the view that will be animated up (in this case scoreView)...
if (dissolveAnimation)
scoreView.alpha = 0.0;
[self.view addSubview:scoreView];
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
if (dissolveAnimation)
scoreView.alpha = 1.0;
else
scoreView.frame = CGRectMake(0.0, 0.0, self.view.frame.size.width, self.view.frame.size.height);
} completion:^(BOOL finished) {
if (finished)
{
//Add scoreDisplayController as a child view controller or present it without animation
[scoreView removeFromSuperview];
}
}];
Of course, the new iOS might make this easier (my lips are sealed), but I hope this is somewhat helpful for your situation!
Have you tried the UIView animation block?
[UIView animationWithDuration:1.0 animation^ {
// do custom animation with the view
}completion:^(BOOL finished) {
if(finished) {
NSLog(#"Finished");
}
}];
It allows you to do custom animations when dealing with UIView(s), and even with UIViewControllers. I use it alot when dealing with custom animation actions.
EDIT:
for example, if you'd like to make the view of the current controller to move up the screen, and the second view controller to slide down in place of it, just do
[UIView animationWithDuration:1.0 animation^ {
// do custom animation with the view
// make sure CoreGraphics.framework is imported
// sliding current view to the top of the screen
self.view.transform = CGAffineTransformMakeTranslation(0,0);
// sliding 2nd view down..
// uncomment the following line, and one of the options for translation
//SecondView *sv = [[SecondView alloc] init];
// just edit the x,y in CGAffineTransformMakeTranslation to set where it will go
//sv.view.transform = CGAffineTransformMakeTranslation(320, 480) // iphone 4
//sv.view.transform = CGAffineTransformMakeTranslation(768, 1024) // ipad 1
}completion:^(BOOL finished) {
if(finished) {
NSLog(#"Finished");
}
}];
Hope this helps!

Google Plus Like Tableviewcell Popout

I'm trying to mimic what the Google+ app does when a user clicks on a cell in the tableView. I realize this is a highly customized version of a tableView, but I'm wondering if anyone has any pointers on where to start. Most big features like this seem to have an opensource project where someone is already mimicking it, but I cannot find one for this. Thanks!
dispatch_async(dispatch_get_main_queue(), ^{
CALayer *layer = cell.layer;
layer.transform = CATransform3DMakeTranslation(-layer.bounds.size.width/4, 0, 0.0f);;
NSTimeInterval animationDuration =0.2;
// The block-based equivalent doesn't play well with iOS 4
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:animationDuration];
cell.layer.transform = CATransform3DIdentity;
cell.layer.opacity = 1.0f;
[UIView commitAnimations];
});
play with the transform you should be able to make it . use this in the cell will display function
The 'Feed' view looks like a UITableView with a customized UITableViewCell that has a cell.contentView of the UITableViewCell being animated with UIView animateWithDuration: or something in setSelected:animated: when the didSelectRow: on the 'Feed' view tableview is called.
If I were to make that customized UITableViewCell, I'd probably have two views depending on the situation: 'Setup A: the view for when it's being scrolled in the feed view' and a 'Setup B: view for when it's been clicked to show the contents of the post more indepth'
setup A would be a more normal UITableViewCell, but setup B would have the same header view of the user who posted (what you see when you're scrolling the feed view), but below that it looks like it has it's own UITableView (which animates after it's been clicked and loads all the users and their comments if there are any comments).
That's just what comes to mind initially -- Hope that's a start?

self.view.frame is no longer animating when in storyboard's initial view controller

This is a strange problem. I have an iPhone view controller for creating a new user with some text fields that require animation to avoid the keyboard when it is shown, i.e. the view animates up and down to keep a minimum clearance between the top of the keyboard and the lower edge of the text field. That worked fine when it was presented modally from the login existing user screen, the initial view controller in the storyboard.
Then I decided to change the app a bit so that the login/create user views would belong to the same view controller. They transition like so:
if(accountCreateView == nil) {
UINib *nib = [UINib nibWithNibName:#"NewAccountView" bundle:nil];
NSArray *nibViews = [nib instantiateWithOwner:self options:nil];
accountCreateView = [nibViews objectAtIndex:0];
}
[UIView transitionFromView:(displayingLoginView ? loginView : accountCreateView)
toView:(displayingLoginView ? accountCreateView : loginView)
duration:0.6
options:(displayingLoginView ?
UIViewAnimationOptionTransitionFlipFromRight :
UIViewAnimationOptionTransitionFlipFromLeft)
completion:^(BOOL finished) {
if (finished) {
displayingLoginView = !displayingLoginView;
}
}
];
This works fine too. However the code that does animation of the views does not. When a text field becomes active the code to check proximity of the text field to the keyboard is called but nothing happens when the animation block executes.
So I checked:
if([UIView areAnimationsEnabled])
NSLog(#"Animations are enabled");
else
NSLog(#"Animations are disabled");
Animations are certainly enabled.
So I moved it back further to viewDidLoad, trying a simple animation to see what works, before anything else:
CGRect viewFrame = loginNameTextField.frame;
viewFrame.origin.y = 400.f;
[UIView animateWithDuration:1.f
delay:0.f
options:UIViewAnimationOptionCurveEaseInOut
animations:^ {
loginNameTextField.frame = viewFrame;
}
completion:NULL];
This works fine! Text field moves slowly down the window.
This does nothing, and that is the confusing part:
CGRect viewFrame = self.view.frame;
viewFrame.origin.y = 400.f;
[UIView animateWithDuration:1.f
delay:0.f
options:UIViewAnimationOptionCurveEaseInOut
animations:^ {
self.view.frame = viewFrame;
}
completion:NULL];
viewFrame when set from self.view.frame has origin 0,20 and size 320,460 as expected.
I tried setting the frame property directly:
CGRect viewFrame = self.view.frame;
viewFrame.origin.y = 400.f;
self.view.frame = viewFrame;
Looks like I can change the frame of subviews of self.view but not of self.view. A fix might be to leave the initial view controller as a blank view in the storyboard and create xibs for both the login view and create user view and load them in viewDidLoad - but why?
edit: Yes, switching the storyboard version of the view controller to a plain empty view and adding the view containing the actual UI from a xib as a subview does make animations work again. Still don't know why though.
If all you need to do is keep your text fields above your keyboard, why not drop the UIViewController and change to a UITableViewController and use static cells? You place one text field per cell (creating enough cells to meet your needs) and style it how you like. When you don't have a first responder and there are few enough cells to fit on the screen, it won't scroll. It there are more than what will fit, then it will scroll to reach the bottom/top cells. Yet no matter how many cells you have, if you select a text field that will be covered up by the keyboard, the device will automatically scroll the cell to a viewable position. It's all built in and I use this feature all the time. It works with text fields and text views. Just remember to comment out the number of sections, number of rows per section, and the cell for index path methods so the static cells will work correctly. It saves a lot of coding.
HTH
Rob

Resources