Making a view appear like twitter app does with new tweet (iPad) - iphone

I'm trying to achieve an effect similar to what twitter does when you bring up the new tweet dialogue. They drop down a view from the top, shrinking the other views but still allowing you to interact with all of them if you dismiss the keyboard. It obviously isn't a modal view, but I can't put my finger on what the starting point to do something similar to this would be.

It looks straight-forward as a view hierarchy, just cleverly dressed with art. The bottom is the regular interface, above is a view containing the UITextView with some nice notepad art around it.
One way to achieve this is to hang two subviews under the view controller's main view. The first child contains the notepad art and the text view. It's positioned at 0,-NOTEPAD_HEIGHT. The second child is at 0,0 and occupies the entire parent view's bounds.
The compose button tells the text view to become first responder, and when editing begins...
- (void)textViewDidBeginEditing:(UITextView *)textView {
[self setNotepadHidden:NO animated:YES];
}
I often make a show/hide method of the following form to rearrange things like this ...
- (void)setNotepadHidden:(BOOL)hidden animated:(BOOL)animated {
NSTimeInterval duration = (animated)? 0.3 : 0.0;
CGFloat offset = (hidden)? -NOTEPAD_HEIGHT : NOTEPAD_HEIGHT;
[UIView animateWithDuration:duration animations:^{
self.firstChild.frame = CGRectOffset(self.firstChild.frame, 0.0, offset);
self.secondChild.frame = UIEdgeInsetsInsetRect(self.secondChild.frame, UIEdgeInsetsMake(offset, 0, 0, 0));
}];
}
Call with ...Hidden:YES whenever you want to hide it again. Make sure that the second child's subviews have autoresizing behavior setup so that they do the right thing when their parent shrinks.
I often find the need for one like this, also...
- (BOOL)isNotepadHidden {
return self.firstChild.frame.origin.y < 0.0;
}
Hopefully, that's a good start.

Related

UIView not animating the first time

This code animates my view every time it's run, except for the first time. When the keyboard is displayed or hidden, a UIView is repositioned:
[UIView
animateWithDuration:0.26
animations:^{
[self setupActiveOverlayViewFrame];
}completion:nil];
-(void)setupActiveOverlayViewFrame {
float optimalOverlayHeight = [self.activePanel optimalHeight];
float realOverlayHeight = MIN(optimalOverlayHeight, self.displayView.frame.size.height);
if (self.activePanel.frame.size.height != realOverlayHeight) {
self.activePanel.frame = CGRectMake(self.activePanel.frame.origin.x, 0, self.activePanel.frame.size.width, realOverlayHeight);
}
self.activePanel.center = [self correctCenterForOverlay];
}
The method I posted is just to show that all it does is re-size and re-position it.
The first time this code is run, it doesn't animate. It just jumps into position. Every time after that, it animates correctly.
Its possible that the "keyboard did display" notification is being called before your view is fully set up, like if you've pushed a view controller onto a navigation controller and immediately give a text view focus on viewDidLoad
You can either keep track of when the keyboard is up or down and when viewDidAppear is called, check if the keyboard is up, and run it. Or you can postpone giving focus to a text view/field until viewDidAppear: is called.
I got the same problem. I want to advice you to check Panel center before(!) animating to find out has it right position at the first run. If it's not. Just set right position. Also try the next code
[self setupActiveOverlayViewFrame];
[UIView animateWithDuration:0.26
animations:^{
[self.view layoutIfNeeded];
}completion:nil];
P.S. If you are using autolayout better use constraints to move your views using the code above.

How to have tabBar animate in after the view is fully pushed into view?

I am trying to emulate the way TweetBot/NetBot animates the tabBar in after a push from the tableView of Accounts action. When the view is fully pushed, only then does the taBar animate in from the bottom. I have tried all sorts of hide/show methods and all seem to fail when it comes to the "show" part.
Does anyone have a suggestion as to how this can be done?
First of all, I presume you are not using a UITabViewController since it cannot be pushed into a UINavigationController stack, so I think you are using a standalone UITabBar embedded in a UIViewController. Is this assumption right?
Try with this code (I didn't try it).
- (void)viewDidAppear {
[super viewDidAppear];
// Calls showTabBar method after SOME_DELAY. You can also call directly [self showTabBar] if you want zero delay.
[self performSelector:#selector(showTabBar) afterDelay:SOME_DELAY];
}
- (void)showTabBar {
// Before the animation begins, your UITabBar must be outside the view controller's view frame.
CGRect tabBarFrame = CGRectMake(0,
CGRectGetHeight(self.view.bounds),
CGRectGetWidth(self.view.bounds),
CGRectGetHeight(self.tabBar.frame);
self.tabBar.frame = tabBarFrame;
// Let's start with the animation, setting a new frame for tab bar inside an animation block
[UIView animateWithDuration:ANIMATION_DURATION animations:^{
// Change origin Y. It assumes that the height of self.tabBar is right, otherwise put the height you want instead of CGRectGetHeight(self.tabBar.frame).
tabBarFrame.origin.y = CGRectGetHeight(self.view.bounds) - CGRectGetHeight(self.tabBar.frame);
self.tabBar.frame = tabBarFrame;
}];
}

User interaction outside the UIView bounds

What i'm trying to do is "navigating" into a bigger UIView with buttons and controls positioned in different places. I've made the size of the main UIView twice bigger than the usual. It is 640x480 instead of 320x480.
After clicking a button in the first part of the screen i've made a moving translation of -320px in the x-direction to show the second "hidden" part of the screen where other functions will reveals to the user. Everything works perfect apart the fact i can't get the UIView back to the original position. It seems that the button i use to get back to the original position and that is "outside the bounds" of the 320px, doesn't work.
My UIView is referenced as "introScreen", and the following is the function i call to translate the screen through the x direction:
- (void)secondPartOfTheScreen {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.15];
introScreen.transform = CGAffineTransformMakeTranslation(-320, 0);
[UIView commitAnimations];
}
Calling this function my UIView moves correctly, but when my button appear in this part of the screen, it doesn't get any user interaction. If i move that button in the first part of the screen, it works correctly. It have something to do with the screen bounds? It is possibile to solve it?
Thanks in advance!
You should try to subclass your UIView and override its pointInside:withEvent: method so that also a point outside its bounds is recognized as belonging to the view. Otherwise, there is no chance that user interaction outside of the view bounds are handled as you would like to.
This is what the Event Handling Guide say:
In hit-testing, a window calls hitTest:withEvent: on the top-most view of the view hierarchy; this method proceeds by recursively calling pointInside:withEvent: on each view in the view hierarchy that returns YES, proceeding down the hierarchy until it finds the subview within whose bounds the touch took place. That view becomes the hit-test view.
You could use something like this in your custom UIView:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
for (UIView* view in self.subviews) {
if (view.userInteractionEnabled && [view pointInside:[self convertPoint:point toView:view] withEvent:event]) {
return YES;
}
}
return NO;
}
You could do with this:
#interface MyView: UIView
#end
#implementation MyView
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
for (UIView* view in self.subviews) {
if (view.userInteractionEnabled && [view pointInside:[self convertPoint:point toView:view] withEvent:event]) {
return YES;
}
}
return NO;
}
#end
add this class definition to the beginning of the .m file where you are using the view. (You could also use separate .h/.m files, but for the sake of testing if everything works, it is enough). Then, replace UIView with MyView in the instantiation of your current UIView, and it should work.
You're not using the transform property properly, you should take into account the previous value of the property like this:
introScreen.transform = CGAffineTransformConcat(introScreen.transform, CGAffineTransformMakeTranslation(-320, 0));
Hope this helps, and check Apple's UIView documentation and CGAffineTransform documentation for more details.
2 things.
First why would you use transform when you are merely moving the object. You can set the frame just as easily.
CGRect frame = introScreen.frame;
frame.origin.x -= 320;
introScreen.frame = frame;
(Yes this will work inside an animation)
second, if you translate a view its contents should be within its bounds regardless of the size you want displayed in the first place.
therefore your container view (the one with the buttons) should be a size so that all contained pieces are within its bounds. Anything outside its bounds will not function. Having the main view twice its size is not as useful as having the contained view at twice its size.
Think of it like this.
Your table should be able to hold whatever your looking at. If you have a map you want to display you would slide it around on the table. the table does not need to be as big as the map but the map needs to be big enough to present you with all of its contents. if the map was say too short to display the poles and you would be without the information. No matter how much you transformed the map you would not be able to use the north or south poles..
To ensure the app reflects this you can turn on clip subviews on all the views in question, then if its outside the bounds it will not be visible.

How to move view from inside a container view to inside another container view?

I've got a main view.
Inside main view are two container views: buttons container and display container.
Inside each of those containers are buttons and display fields, respectively.
So in short, I've got three levels of views: main, sub (containers), and sub-sub (buttons and fields).
When a button is pressed, I want to animate an image of that button from the button area to the display area. That is, I need to move it two levels up, then two levels back down.
Currently, I'm creating a UIImage, identical to the custom button's UIImage, on top of the button. I move it, then destroy it at the end of the animation, so I don't have to alter the actual button (which I want to stay in place so I can re-use it).
Obviously I can get this UIImageView's center/bounds/frame.
However, I'm having trouble determining the coordinates for the destination. Frame and Center are relative to the superview, but that's just one level up. Seems like there's a lot of math do to in order to add up the correct X and Y offsets to get to the destination.
Is this a job for UIView's convertRect:toView: or convertRect:fromView: ? I'm having a hard time determining just how to use those, or deciding whether they're actually the right methods to use.
Seems like a common enough problem - moving something from one "nested" view to another "nested" view - but I've searched and can't find the answer.
Those convertRect methods are tough to get the hang of. Your view contains two sub views subA and subB, and subA contains a button, and you'd like to animate the button moving from subA to subB. Let's do the animation in the view controller that has the main view....
// subA is the receiver. that's the coordinate system we care about to start
CGRect startFrame = [subA convertRect:myButton.frame toView:self.view];
// this is the frame in terms of subB, where we want the button to land
CGRect endFrameLocal = CGRectMake(10,10,70,30);
// convert it, just like the start frame
CGRect endFrame = [subB convertRect:endFrameLocal toView:self.view];
// this places the button in the identical location as a subview of the main view
// changing the button's parent implicitly removes it from subA
myButton.frame = startFrame;
[self.view addSubview:myButton];
// now we can animate in the view controller's view coordinates
[UIView animateWithDuration:1.0 animations:^{
myButton.frame = endFrame; // this frame in terms of self.view
} completion^(BOOL finished) {
myButton.frame = endFrameLocal; // this frame in terms of subB
[subB addSubview:myButton];
}];

Slide-down view on iPhone

I want to create a slide-down animation, like in the image in the link. When I press on "Previous", I want it to slide down to the center of the screen. I am not really sure how to implement that. Any suggestion would be really handy.
http://imageshack.us/photo/my-images/718/slideto.png/
This looks like a UITableView. You can place a custom cell with the button Previous, which will load the new data above and then call the scrollToRowAtIndexPath:atScrollPosition:animated: function to perform the animation.
The whole thing is pretty likely a UIScrollView of some sort (UITableViews are scroll views as well). When the buttons is pressed, either use setContentOffset:animated: or scrollRectToVisible:animated: to do the scrolling. The "magic" is just in calculating the correct offset or rect. I'd suggest going with setContentOffset:animated:. It should work roughly like this:
CGPoint p;
p.x = 0;
// Get middle of the view to be centered.
p.y = CGRectGetMidY(myViewThatShouldBeCentered.frame);
// Need to offset it by half the scroll view frame, otherwise
// you'd just see the lower half of the view peeking out at the
// top of the scroll view.
p.y -= CGRectGetHeight(myScrollView.frame) / 2;
[myScrollView setContentOffset:p animated:YES];