Animated Subviews - ios

I have a split view controller in my Ipad app, in which the detail view has subviews which are UITableView's.
The functionality I am trying to implement is to get an info view at the same position of a subview when a button(info button) on the subview is pressed. I need to get this info view by animation and that animation is that when the info button is pressed, that subview alone would flip (UIAnimationOptionFlipFromRight) and the info view would show...
This is how try to implement it -
-(void) showInfoView: (id)sender
{
infoView = [[InfoViewController alloc] initWithNibName:#"ViewViewController" bundle:nil];
infoView.view.frame = CGRectMake(250, 300, 200, 200);
[UIView transitionWithView:self.view duration:1
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
[self.view addSubview:infoView.view];
}
completion:nil];
}
When I run the simulator and press the info button on any subview, what happens is that the animation happens perfectly, i.e. the subview flips from the right but the infoView is not getting displayed.
It would be great if someone could tell me where I am going wrong here.

The basic steps of performing an animation to onscreen is as follows:
Create the view.
Move it to the initial (offscreen) position
Add to the view hierarchy
Perform the animation.
It seems like you're skipping step 3 and possibly step 2.

Did you try setting the subview frame to the frame of your superview, i.e
info.View.frame = self.view.frame;

Related

iOS Action app extension with transparent background?

I’m trying to make an iOS Action app extension with minimal UI. Basically it would just show a progress indicator until the action completed. I just want to be able to animate the view so that it slides down from the top & then slides back up when the action has completed. If anyone is familiar with Instapaper’s Share extension, then that’s the kind of basic UI I’m looking for.
The problem is that when I try to duplicate this functionality - I just have a small UIView that animates down from the top - I get a black background behind my view. I can’t figure out how to make that background transparent so that the stuff behind my view is still visible. Does anyone know how to do this?
As a starting point I’m just using the default Action Extension template that’s created by Xcode...
Create a new iOS app project in Xcode.
Add a new target -> Action Extension.
In the ActionViewController.m file add a viewWillAppear method to animate the view (using a 1 second animation so that the black background is easily seen):
Code:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
CGRect currFrame = self.view.frame;
CGRect newFrame = currFrame;
newFrame.origin.y -= newFrame.size.height;
self.view.frame = newFrame;
[UIView animateWithDuration:1.0 animations:^{
self.view.frame = currFrame;
}];
}
When this is run the view is animated sliding down from the top. However rather than seeing the UI of the calling App all you see is a black background.
I’ve tried a number of things - changing the modalPresentationStyle (doesn’t seem to do anything), setting the entire view to hidden (this just makes the whole screen black), etc.
For reference this is using iOS 9.3.2 and Xcode 7.3.1.
From what I understand from Apple docs
In iOS, an Action extension:
Helps users view the current document in a different way
Always appears in an action sheet or full-screen modal view
Receives selected content only if explicitly provided by the host app
The fact that the Action extension always appear in a full-screen view on an iPhone might mean that there's no way of having a transparent background.
I am certain that a Share extension can be animated (I've done it myself) how you want it and have a transparent background. That's why Instapaper's Share extension works nicely.
You are facing two problems:
1. When you present a view controller, the default behavior is the controller is full screen context. That is the reason you see a black screen.
2. You are trying to change self.view.frame when the controller is presented on full screen.
Yet there is a way to achieve this kind of behavior you are looking for, in one of three ways:
A. Specify "modalPresentationStyle" to "UIModalPresentationOverCurrentContext" and set the presenting controller to "definesPresentationContext".
That way, when you present the controller, the presenting controller will be behind the presented controller.
And insted of changing self.view.frame you will set self.view background color to clear, and add a subview and use it as a background view:
//Presenting view controller:
-(void) presentPopUpViewController {
self.definesPresentationContext = YES; //self is presenting view controller
self.presentedVC.modalPresentationStyle = UIModalPresentationOverCurrentContext;
[self presentViewController:self.presentedVC animated:NO completion:nil];
}
//Presented view controller:
-(void) viewDidLoad {
[super viewDidLoad];
CGRect currFrame = self.view.frame;
CGRect newFrame = currFrame;
newFrame.origin.y -= newFrame.size.height;
self.myBackroundView = [[UIView alloc] init];
self.myBackroundView.backgroundColor = [UIColor yellowColor];
self.myBackroundView.frame = newFrame;
[self.view addSubview:self.myBackroundView];
self.view.backgroundColor = [UIColor clearColor];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[UIView animateWithDuration:5.0 animations:^{
self.myBackroundView.frame = self.view.frame;
}];
}
B. Add the presented view controller as a Child view controller. that way the life cycle stays the same, but you can add I'ts view as a subview, and change I'ts frame.
//Presenting view controller:
-(void) presentChildViewController {
[self addChildViewController:self.presentedVC];
[self.view addSubview:self.presentedVC.view];
[self.presentedVC didMoveToParentViewController:self];
}
//Presented view controller:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
CGRect currFrame = self.view.frame;
CGRect newFrame = currFrame;
newFrame.origin.y -= newFrame.size.height;
self.view.frame = newFrame;
[UIView animateWithDuration:1.0 animations:^{
self.view.frame = currFrame;
}];
}
C. Don't use UIViewController, use UIView. Use a "Decorator" object, that you pass it the ViewController you would like the view to disaply on, and the "Decorator" will add the view as subview, and deal with the animation. No need for an example for this scenario.
It is wrong to start animations when your view hasn't yet appeared. Can you, please, try the same code in viewDidAppear.
Also animating main view controller's view will make underlying layers visible, so you've got to use another view on top of view controller's main view, like this:
UIView *progressView = [[UIView alloc] init];
progressView.frame = self.view.bounds;
progressView.backgroundColor = [UIColor redColor];
[self.view addSubView:progressView];

iOS - Insert subview between the background and toolbar of another view

Pardon my ugly illustration below:
I have a view controller A that has a toolbar T at the bottom; toolbar is part of controller A, created by choosing "Opaque Toolbar" as "Bottom Bar" in the Attributes Inspector of view controller A. I'd like a banner B to slide up from the top of toolbar T, stay there for 2 seconds then disappear. Banner B is not within the view hierarchy of controller A. The issue right now is that the banner covers the toolbar during the sliding up process; I'd like the notification banner to emerge from the top of the toolbar, initially completely covered by the toolbar, instead of covering the toolbar.
Changing the layer index of the banner doesn't work, because the banner is not within the layer system of the view below it.
Here's my code snippet that adds the banner and animates it:
[containerView addSubview:innerView];
// Animate In
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
[innerView setFrame:target];
} completion:^(BOOL finished) {
if (finished) {
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
[innerView addGestureRecognizer:tapGestureRecognizer];
}
}];
Is there a way to insert the banner so that it appears above the background of controller A but below the toolbar of the controller?
Maybe you can try :
[self.view bringSubviewToFront : toolbarView]; //Top level
[self.view insertSubview:bannerView belowSubview: toolBarView]; //below your toolbar
I assume your containerView is the view of a UINavigationController, as you cannot add a toolbar in the attributes inspector for UIViewController. I would suggest you just add the banner to self.view
[self.view addSubview:innerView];
If you feel like this will not work. You should probably subclass UINavigationController and add the subview of it.
[self.view insertSubview:bannerView belowSubview: self.toolbar]; //toolbar is a property of UINavigationController
I suppose it's possible to say something like:
[self.navigationController.view insertSubview:bannerView belowSubview: self.navigationController.toolBarView];
Generally you want view controllers to manager their own view hierarchy. So it is better to avoid the third options.

Mapkit custom callout slide view from bottom

I have a map which has multiple Annotations on it. When I click one I want to slide a small UIView from the bottom with some information about this pin. If I click on a button called more I want the view to expand upwards with more information in it.
I'm not sure how to do this the right way. My idea is to define a new xib and view controller for the custom callout. But I only want to display a small portion of that view so should I set its frame from the beginning to a fixed small value or rather use autolayout to shrink it to its content? And how can I slide it up from the bottom? I think I need to show it either modally or as a childviewcontroller and use some animation.
When the user clicks on more I would use another DetailViewController with a xib file which either would be shown also modally or also as a childviewcontroller. Is there any example how to achieve something like this? Or are there any better ways to do this?
For now the custom callout view would be the same for each Annotation. But I would have three different DetailViewControllers depending on which Annotation was clicked.
My basic idea is to separate these things so my MapViewController won't get too big.
I wouldn't use a view controller for the slide-in view. Just create a UIView with a frame that's just outside of your main view frame, and make it a subview. Like this:
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height, self.view.frame.size.width, 200)];
[self.view addSubview:v];
self.infoView = v;
You can do that in viewDidLoad, or wherever you want in the initial setup of your view. It won't be visible because it's just off the bottom of the screen. Then when the user taps the callout button, animate your infoView into position like this:
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
// Populate infoView with whatever info you want to show about the annotation
//
// ...
//
// Then animate the view into position
[UIView beginAnimations:#"infoView" context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelegate:self];
self.infoView.center = CGPointMake(0, self.view.frame.size.height - 200);
[UIView commitAnimations];
}

presentViewController resets all UIView animations

Say I have two view controllers, A and B. UIView instance viewObject is subview of A in the nib, with frame 0, 0, 100, 100.
Now I execute the following
[UIView animateWithDuration:0.5 animations:^{[viewObject setFrame:CGFrameMake(100, 100, 100, 100)];}];
It worked all fine. But when I call:
[self presentViewController:B animated:YES completion:NULL];
viewObject appears to jump back to the frame before the animateWithDuration:animations: message during the transition of the view controllers.
Likewise when I dismiss B the viewObject is at its position in the nib.
How do I make viewObject stay in the same place?
try to set
[viewObject setClipsToBounds:YES]
at least before the second animation. If you need it not to clip to bounds(because of shadows and such) you can reset the property to no after the second animation is completed.
It worked for me every time I had this problem.

Two similar animations behave differently

I have a UIView configured through Storyboard. This view (self.view) has one child, a button (self.cartButton). Later in the code I add a subview to this view:
[self.view addSubview:self.cart.view];
I have the following method:
- (void)openCart
{
[UIView animateWithDuration:2 animations:^{
self.cartButton.frame = CGRectMake(0.0f, 0.0f, self.cartButton.frame.size.width, self.cartButton.frame.size.height);
self.cart.view.frame = CGRectMake(0.0f, 0.0f, self.cart.view.frame.size.width, self.cart.view.frame.size.height);
}];
}
I think it should move both objects to the top of the screen, but what really happens is that the cartButton animates to the bottom most position, and the cart view to the top. Why does this happen? Did I mess up the coordinate systems somewhere?
UPDATE: If I comment out the second line in the animation (animating the cart view), cartButton flies to the top.
UPDATE2: It has something to do with my cartView. It is a generic view (created in Storyboard). When I try to do the same animation for example with a NavigationController, it works.
UPDATE3: If my cart view has no subviews, it works fine. If it has anything in it (a button, navigationBar, anything...), it does not work.

Resources