Slide-down view on iPhone - 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];

Related

UIView positioning related questions (iOS)

I have a bunch of questions:
How do I position a UIView so that it is on the bottom of the view I am adding it to?
How can I add a subview to a view so that it is positioned in the corner of the superview with a small gap (Like if I want a 'x' cross sign for closing something)
Is their a utility class for easy UIView positioning (and rotation)?
Any references, open source tutorials etc. will be more then welcome!
(a) How do I position a UIView so that it is on the bottom of the view I am adding it to?
OK, let's say you want to position button as a subview at the bottom of view form, you calculate the origin.y of the subview button by subtracting button's height from the height of the form
CGRect buttonFrame = button.frame;
buttonFrame.origin.y = form.bounds.size.height - buttonFrame.size.height;
button.frame = buttonFrame;
[form addSubview:button];
You can change origin horizontal position as well. You want it on the bottom left of form?
buttonFrame.origin.x = 0;
Or on the right edge of form?
buttonFrame.origin.x = form.bounds.size.width - buttonFrame.size.width;
Or in the middle (horizontally) of form?
buttonFrame.origin.x = (form.bounds.size.width - buttonFrame.size.width) / 2;
or another way using CGRectGetMidX (found in CGGeometry utility methods):
buttonFrame.origin.x = CGRectGetMidX(form.bounds) - buttonFrame.size.width/2;
Autoresizing handles adjusting the frame when the parent view's size changes. But you still have to position it first.
int xOffset = 20;
int yOffset = 20;
CGRect BottomRight_NewFrame = CGRectMake((superview.frame.size.width - subview.frame.size.width-xOffset), (superview.frame.size.height - subview.frame.size.height-yOffset), subview.frame.size.width, subview.frame.size.height);
subview.frame = BottomFrame;
You can use the new Autolayout feature of iOS 6 or the old Struts & Springs in the Interface Builder to achieve this.
This tutorial explains both:
http://msmvps.com/blogs/kevinmcneish/archive/2012/12/10/tutorial-ios-6-auto-layout-versus-springs-and-struts.aspx
Or you can set the autoresizing mask programatically. It is explained pretty well here:
UIView autoresizingMask - Interface Builder to Code - Programmatically create struts and springs - Swift or Objective-C
It's easy enough to just set the frame, e.g. (untested code )
subview.frame = CGRectMake((superview.frame.origin.x - subview.frame.origin.size.width/2)-20, (superview.view.frame.origin.y - subview.frame.origin.size.height/2)-20, subview.view.frame.size.width, subview.view.frame.size.height);
if you'll be doing a lot of this then create a utility class or method.
Autolayout will help you position the views and maintain those positions if the size of the superview changes. If the superview isn't going to change, you don't really need to mess with constraints -- you can just set the position of the subview appropra
If you're adding view viewB to view viewA:
a) To position viewB so that it's bottom edge corresponds to the bottom edge of viewA:
viewB.frame.origin.y = viewA.bounds.size.height - viewB.bounds.size.height;
b) You don't say which corner, but it's just a matter of doing the math. For example, the upper right corner of viewA is at {viewA.bounds.size.x, 0} in viewA's coordinate system. If you want to put viewB there, set it's origin to:
{viewA.bounds.size.x-viewB.bounds.size.x, 0}
If you want to add a margin, you can add that to the computation:
int margin = 10;
{viewA.bounds.size.x-viewB.bounds.size.x-margin, margin}
d) Use NSLayoutConstraint to access the autolayout system's constraints programmatically. There's a nice visual format language, so that for your question (a) you could set the constraint for viewA to:
V:|-[viewB]-0-|
The V means that you're working in the vertical direction, |'s represent the edges (top and bottom, thanks to the V) of the superview (that's viewA), and the 0 means that the distance between viewB and the bottom of its superview should be 0.
You can setup constraints in iOS6 but if you want to work on older os's you need to position them manually. Math.

append UIView's to the top of a UIScrollView

For example in Tweetbot's iPhone app. When you open up the app and new tweets come in, it will just be appended to the top of the UIScrollView and the current tweet you see did not get refreshed. How can I achieve the same thing effect?. Say I have a UIScrollView with a UIView added to the UIScrollView starting at origin 10,10.
I then downloaded a few items and I want to put it at 10,10.. so I basically need to shift this old item at 10,10 down right? If I do so then during that shifting user's will see it shifted, which is not the effect I want. Where as in Tweetbot's app it seems that nothing is being shifted around, it's just that you grow the area above the 10,10 and append new stuff's there.
How do I do this?
Basically I wanted to implement the insertRowAtIndexPath in a UIScrollView.
Will restate the question this way: how to add content to the top of a UIScrollView without moving the content that's already there (relative to it's current offset).
If this is the right question, the right answer is to do the add and shift down just as you suggested, but then scroll by the same height as added content, giving the illusion that the old content didn't move.
- (void)insertRowAtTop {
// not showing insert atIndexPath because I don't know how you have your subviews indexed
// inserting at the top, we can just shift everything down. you can extend this idea to
// the middle but it requires that you can translate from rows to y-offsets to views
// shift everything down
for (UIView *view in self.scrollView.subviews) {
if ([view isKindOfClass:[MyScrollViewRow self]]) {
MyScrollViewRow *row = (MyScrollViewRow *)view; // all this jazz so we don't pick up the scroll thumbs
row.frame = CGRectOffset(row.frame, 0.0, kROW_HEIGHT); // this is a lot easier if row height is constant
}
}
MyScrollViewRow *newRow = [[MyScrollViewRow alloc] initWithFrame:CGRectMake(0.0,0.0,320.0,kROW_HEIGHT)];
// init newRow
[self.scrollView addSubview:newRow];
// now for your question. scroll everything so the user perceives that the existing rows remained still
CGPoint currentOffset = self.scrollView.contentOffset
self.scrollView.contentOffset = CGPointMake(currentOffset.x, currentOffset.y + kROW_HEIGHT);
}
If you set the contentOffset without animating there wont be a visible scrolling animation. So if your new view is 200 points tall you can set the origin of the new view at (10,10) and the old view at (10,210) and set the contentOffset of the scroll view to (10,210) you should achieve the effect you intend. You'll also need to increase the contentSize of your scroll view to be big enough for all of the content it contains.

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

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.

How to zoom when scroll UIScrollView

I add few labels in UIScrollView and I want when I scroll, the the middle label font size can become bigger. And the previous middle label font size shrinks to smaller. And the change happens gradually. Like below. Label 1 move to left shrink smaller and label 2 move to middle becomes bigger. All labels in a UIScroll view.
I tried some, like when scroll I tried zoom scroll page, seems complex than I thought...
Anyone has good idea? Thanks!
Pretty simple really. Just stick a UIScrollViewDelegate to the scrollview and do something like this..
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
for (int i = 0; i < [scrollView.subviews count]; i++) {
UIView *v = [scrollView.subviews objectAtIndex:i];
float position = v.center.x - scrollView.contentOffset.x;
float offset = 2.0 - (abs(scrollView.center.x - position) * 1.0) / scrollView.center.x;
v.transform = CGAffineTransformIdentity;
v.transform = CGAffineTransformScale(v.transform, offset, offset);
}
}
But, if you aren't impressed by the affine transform, you could still scale the rect using the offset factor and set the UILabel to adjustsFontSizeToFitWidth... and you are done!
Just make sure there is enough space! Else it could get out of hand very easily.
Assumptions :
There are no other views in the scrollview apart from these labels.
Its required to work just horizontally.
It could be done with CoreAnimation. You have to keep the index of the main label (that one in the center), and after scrolling is done or when scrolling starts (use some proper for you method in UIScrollViewDelegate) and simply shrink side labels by animation.
Just make the size of the font bigger (with an animation block) when it is the middle one, and smaller when it is not.
You can add a category to UILabel with -(BOOL)isMiddle and set it to true/false.

Push a UIImageView with another UIImageView, possible?

Is it possible to pan a UIImageView and when it intersects with the frame of another UIImageView, have that other UIImageView be pushed around by the other UIImageView without just passing over it? I hope that makes sense!
If it is possible, could you give me some ideas on how I'd go about implementing this?
I imagine it would go something like...
If frame intersects frame from left or right on the x/y axis, have the other frame move in that same direction with same distance as the pushing frame. While that logic somewhat makes sense to me, I'm not sure how I'd implement that in code.
I'd really appreciate any advice you can offer.
So, you're only concerned with left / right panning...
Let's assume you have 2 UIImageViews, a & b, as subviews of some other UIView and defined as class members of some class.
You can get help detecting panning gestures (dragging touches) on each of these views by creating instances of UIPanGestureRecognizer and adding them to each UIImageView using method addGestureRecognizer:
When creating each UIPanGestureRecognizer, you need to designate a selector to receive the gesture events. Let say it's called didMove:
Now, some sample code:
- (void) didMove:(UIPanGestureRecognizer*)recognizer {
UIView* view = [recognizer view];
// Remember our UIImageViews are class members called, a & b
//
UIView* otherView = view == a ? b : a;
if (recognizer.state == UIGestureRecognizerStateBegin
|| recognizer.state == UIGestureRecognizerStateChanged) {
// Moving left will have a negative x, moving right positive
//
CGPoint translation = [recognizer translationInView:view.superview];
view.center = CGPointMake(view.center.x + translation.x, view.center.y);
// Reset so we always track the translation from the current view position
//
[recognizer setTranslation:CGPointZero inView:view.superview];
if (CGRectIntersectsRect(view.frame, otherView.frame)) {
// Translate by the same number of points to keep views in sync
//
otherView.center = CGPointMake(otherView.center.x + translation.x, otherView.center.y);
}
}
}
It may not be exactly what you need, but it should give you a good idea how it could be done.
Sounds like you'd better use something like Cocos2D with it's sprites, intersections and some basic physics...
You can do it like this,
First, Create an UIImageView and assign an UIImage to it.
Second, When the user swipes or makes any gesture just change the coordinates of the card by setting its frame again.
Since u want the card to slide away, you have to change the x coordinate to a negative value such that it slides off from the view.
The above said code of reframing the imageview should be given within a UIView setAnimation group of methods...with a required delay.
To achieve the push effect have a method called within the the same animation block of codes which creates a new imageview (having same name as that of first imageview) and keep it out of the view. That is beyond the width of the screen. (dont forget to release the card which was pushes away previously)
Then subsequently again make a new frame for this imageview such that it appears in the centre of the screen....When above three steps of code are put within the animation block of code for UIView or UIImageView with a delay time , u will achieve the required effect.
Hope this helps....
happy coding....