Tweetbot like shrink view transition - ios

Does anyone know how to achieve the present view controller's view to shrink and put another one over it with a transparency? It is achieved in Tweetbot 3 when you tap on your avatar in the top left on the navigation bar. Should I take a snapshot for example?

In order to achieve this effect you will have to rebuild your view stack from scratch.
So as there is no possibility to change the viewController.view's frame, you'll have to add a kind of container subview a little like this:
#implementation ViewController
#synthesize container;
- (void)viewDidLoad {
[super viewDidLoad];
container = [[UIView alloc] initWithFrame:self.view.frame];
[self.view addSubview:container];
// add all views later to this insted of self.view
// continue viewDidLoad setup
}
Now if you have that, you can animate the shrinking behavior like so:
[UIView animateWithDuration:.5 animations:^{
container.frame = CGRectMake(10, 17, self.view.frame.size.width-20, self.view.frame.size.height-34);
}];
Okay, I assume you are developing for iOS 7, so we'll make use of some new APIs here (for earlier versions there are alternative frameworks). Now since WWDC UIView's got a resizableSnapshotViewFromRect:(CGRect) afterScreenUpdates:(BOOL) withCapInsets:(UIEdgeInsets) method returning a single UIView object.
[UIView animateWithDuration:.5 animations:^{
container.frame = CGRectMake(10, 17, self.view.frame.size.width-20, self.view.frame.size.height-34);
} completion:^(BOOL finished) {
UIView *viewToBlur = [self.view resizableSnapshotViewFromRect:container.frame afterScreenUpdates:YES withCapInsets:UIEdgeInsetsZero];
}];
If you do not want to rewrite your view management, you can also first take a snapshot of your main view this way, set it to the container and then animate only the image. But remember, you can not interact with the captured view then.
When you've got that, you can download the two category files from this repo (They're from WWDC, so directly from Apple!). Basically, what they do is, they add some cool new methods to the UIView class, of which we'll use applyDarkEffect. I haven't tested this, maybe another method fits your needs better here.
Anyways if we implement that into the block and maybe also add a UIImageView to display the blurred overlay, it should look something like this:
[UIView animateWithDuration:.5 animations:^{
container.frame = CGRectMake(10, 17, self.view.frame.size.width-20, self.view.frame.size.height-34);
} completion:^(BOOL finished) {
UIView *viewToBlur = [self.view resizableSnapshotViewFromRect:container.frame afterScreenUpdates:YES withCapInsets:UIEdgeInsetsZero];
UIImage *image = [viewToBlur applyDarkEffect];
UIImageView *blurredView = [[UIImageView alloc] initWithFrame:self.view.frame];
[self.view addSubview:blurredView];
// optionally also animate this, to achieve an even smoother effect
[blurredView setImage:image];
}];
You can then add your SecondViewController's view on top of the stack, so that it's delegate methods will still be called. The bounce effect of the incoming account view can be achieved via the new UIView animation method animateWithDuration:(NSTimeInterval) delay:(NSTimeInterval) usingSpringWithDamping:(CGFloat) initialSpringVelocity:(CGFloat) options:(UIViewAnimationOptions) animations:^(void)animations completion:^(BOOL finished)completion (more on that in the documentation)
I hope that will help you with your project.

Related

UIView setAnimationTransition only working sometimes but not always?

I'm writing a little card game where 4 cards are dealt to the screen and the user can tap each card to reveal (and again to hide) it.
Each card-front and card-back are stored in image views. A UIButton catches the user tap and should flip the card over.
I've added front and back of the card as subviews to a container view and I use the method UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight for the animation.
Please see the code below, I've removed the handling of the 4 different cards in the methods below for the sake of simplification & readability. Also I've replaced some array/filename-juggling for the cards with static image-names for front and back here.
The strange thing with this code now is the sometimes it works as expected 10 times in a row (i.e. the flipping animation is shown) but sometimes no animation is shown at all (i.e. the other card side is shown but without the flipping.). Then it's the other way round: Sometimes the card is shown without any animation for 7 or 8 times then suddenly the flipping-animation is shown.
It's driving me nuts as I can't see the reason for this strange behaviour.
Do you have any idea? I'm building for iOS 8 to iOS 10.
From the .h file:
#interface GameViewController : UIViewController
{
UIImageView *cardback1;
UIImageView *cardfront1;
UIView *containerView;
BOOL c1Flipped;
// much more...
}
And from the .m file:
-(void)flipCardButtonClicked:(id)sender
{
containerView = [[UIView alloc] initWithFrame: CGRectMake(25,420,220,300)];
[self.view addSubview:containerView];
c1Flipped = !c1Flipped;
cardback1 = [[UIImageView alloc] initWithFrame: CGRectMake(0,0,220,300)];
cardfront1 = [[UIImageView alloc] initWithFrame: CGRectMake(0,0,220,300)];
if (c1Flipped)
{
cardback1.image = [UIImage imageNamed:#"backside.png"];
cardfront1.image = [UIImage imageNamed:#"frontside.png"];
}
else
{
cardback1.image = [UIImage imageNamed:#"frontside.png"];
cardfront1.image = [UIImage imageNamed:#"backside.png"];
}
[containerView addSubview:cardfront1];
[containerView addSubview:cardback1];
[cardfront1 release];
[cardback1 release];
[self performSelector:#selector(flipSingleCard) withObject:nil afterDelay:0.0];
}
-(void)flipSingleCard
{
[containerView.layer removeAllAnimations];
[UIView beginAnimations:#"cardFlipping" context:self];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(flipDidStop:finished:context:)];
[UIView setAnimationDuration:0.5];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:containerView cache:YES];
[containerView exchangeSubviewAtIndex:0 withSubviewAtIndex:1];
[UIView commitAnimations];
}
-(void)flipDidStop:(NSString*)animationID finished:(BOOL)finished context:(void *)context
{
[containerView removeFromSuperview];
[containerView release];
}
My guess is that [self performSelector:#selector(flipSingleCard) withObject:nil afterDelay:0.0]; is the culprit. Seems like this could be a timing issue. Have you tried adding an actual delay to this? Say...0.1? I believe that doing this could fix this, OR just simply calling the method directly instead of using performSelector.
Looks like it indeed was a timing issue as suspected by BHendricks.
Adding a 0.1 delay solved the problem.
Thanks a lot.

How can I present one ViewController on top of another ViewController?

Let's say I have a class that is a subclass of UIViewController named FullSizeViewController. I also have another class that is a subclass of UIViewController called TQHViewController (TQH: Three Quarter Height). How would I
be able to display TQHViewController over FullSizeViewController, with the only one quarter of FullSizeViewController visible?
animate this action?
I'm basically attempting to make something like a UIPopover, but animate it so it slides in from the bottom of the screen.
#property(nonatomic,retain) TQHViewController *template;
template = [[TQHViewController alloc]init];
template.view.frame = CGRectMake(20, 1000, 280, 450);
And you can also set the frame for the subView and decide the size of the subview. if you want animation from the bottom use UIViewwithanimation.
[UIView animateWithDuration:0.9f
delay:0.1f
usingSpringWithDamping:0.65f
initialSpringVelocity:0.1f
options:UIViewAnimationOptionCurveEaseOut animations:^{
template.view.frame = CGRectMake(20, 63, 280, 450);
[self.view addSubview:overlayWindow];
}
completion:^(BOOL finished) {
//Completion Block
}];
Use like this so your functionalities will not be lost and make nill after the usage.
If anything wrong feel free to comment it.

Showing UIImageView on startup after a delay with close button

I want to show a UIImageView after 3 seconds from the view did load. This image view is kind of a static Ad, a static image loaded that will show up when the application starts as mentioned. This ismageView should have a close button on it so the user can close it just like the typical ad behavior. It's my first time dealing with this kind of situations so please help me out i'm totally lost.
Till now i got to animate a view containing an image view with fade in and out animations, which is perfect..but now haw can i add a close button to it, to make her dismiss only when that button is pressed? this is my code
UIImageView *wnn = [[UIImageView alloc]init];
wnn.frame = CGRectMake(100, 100, 300, 300);
[wnn setImage:[UIImage imageNamed:#"menu-icon.png"]];
UIView *jn = [[UIView alloc]init];
[jn addSubview:wnn];
[self.navigationController.view addSubview: jn];
[jn setAlpha:0.f];
[UIView animateWithDuration:2.f delay:0.f options:UIViewAnimationOptionCurveEaseIn animations:^{
[jn setAlpha:1.f];
} completion:^(BOOL finished) {
[UIView animateWithDuration:2.f delay:0.f options:UIViewAnimationOptionCurveEaseInOut animations:^{
[jn setAlpha:0.f];
} completion:nil];
}];
You can follow below steps:
Open Storyboard file with your viewcontroller.
Place UIView from library to your main view of viewcontroller.
Place your image and close button on new UIview placed.
Now set IBOutlet for this uiview.
Make hide/show this view instead of image and close button in your method.

Completion^ block not being called in UIView animation

Is there something I'm missing here?
i want the imageview to slide in then slide out from the bottom of the screen.
also, this seems to put the UIImageView behind the navigation bar how can I make a CGRect to fit the screen under the navbar?
_finga = [[UIImageView alloc] initWithFrame:CGRectMake(0, 88, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height-88)];
hiddenFrame = CGRectOffset(_finga.frame, 0, _finga.frame.size.height);
_finga.frame = hiddenFrame;
[self.view addSubview:_finga];
_finga.image = [UIImage imageNamed:#"Finga"];
[UIView animateWithDuration:2.0 animations:^{
_finga.frame = self.view.bounds;
}completion:^(BOOL done) {
if (done) {
NSLog(#"Complete!");
[UIView animateWithDuration:2.0 animations:^{
_finga.frame = hiddenFrame;
}];
}
}];
The CGRect you initialize _finga with would put it under the nav bar. In the animation, you are setting the frame to the bounds of the view, which would put it behind the bar, since the y value would be 0.
You could write less code by initializing _finga with this frame from the start:
CGRectMake(0,
self.view.bounds.size.height,
self.view.bounds.size.width,
self.view.bounds.size.height-88);
That will put the view off the screen. Then after you add it as a subview and set its image, animate the view back up to this frame:
CGRectMake(0,
88,
self.view.bounds.size.width,
self.view.bounds.size.height-88);
Which will put the view just below the nav bar. Consider also replacing all the hard coded 88s with a variable or #define so that you can play around with it more easily and in case the nav bar height ever changes.
As for the completion block, try logging that #"Complete!" string before you check if done is YES, or put a breakpoint there and see what the value of done is. Your animation may not be completing for some reason, which would explain why the code in the completion block is not being run.
Generally, though, if you are just using the completion block to run another animation after the first one, you don't need to check the done BOOL at all. It's only crucial when something else in your program depends on the state of the animation. For example, the user may click a button which animates something and then takes the user to a different section of the app. But if the user cancels the animation, you may not want to go to the other section after all, so you can check done.

On iOS, what is a good way to implement a slide-out panel full of widgets?

I wonder for an iOS app, what is the best way to implement a slide-out panel with a button and slider inside it?
That is, the app will have a small "i" icon at the bottom right corner of the screen that kind of look like:
When that icon is touched, a panel of size about 1/4 of the screen will slide out from the bottom, showing a button, a slider, or possibly some other widgets.
What is the best way to implement it -- using Interface Builder or is it better purely by code, and the object to use for the panel (UIView or any existing widget that has this slide out / slide in function?).
Assuming you are going to use this in multiple locations within your app you would probably want to make it as a reusable component. I would suggest making the slide out screen its own view controller where you could create its view using IB if you need. When the user hits the 'i' you would alloc the slide out view controller, add its view as a subview to your current view controller and animate it in. If you need to communicate between the slide out and your current view controller you could just create a custom delegate protocol on your slide out. I recognize this is making some assumptions but would likely be a clean solution for you.
Creating this by Interface Builder is a pain (anyways, that tool sucks). You should definitely create that in code. It will also be easier to maintain if you can concentrate on the code and don't have to mess with setting mysterious properties in IB only for the view to be displayed at all...
You can try implementing it like this:
#interface SlideOutMenu: UIView {
UIImageView *imgView;
UITapGestureRecognizer *tap;
}
- (id)initWithImage:(UIImage *)img;
#end
#implementation SlideOutMenu
- (id)initWithImage:(UIImage *)img
{
if ((self = [self initWithFrame:CGRectMake(0, someY, 44, 44)]))
{
imgView = [[UIImageView alloc] initWithImage:img];
[self addSubview:imgView];
[imgView release];
tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(_tapped)];
[self addGestureRecognizer:tap];
[tap release];
}
return self;
}
- (void)_tapped
{
if (self.frame.origin.x > 1.0) // 1.0: some tolerance b/c of floating point numbers
{
[UIView animateWithDuration:0.3 animations:^{
self.frame = CGRectMake(0, self.frame.origin.y, self.frame.size.width, self.frame.size.height);
}];
}
else
{
[UIView animateWithDuration:0.3 animations:^{
self.frame = CGRectMake(120.0, self.frame.origin.y, self.frame.size.width, self.frame.size.height);
}];
}
}
#end

Resources