I have an UIView and its subview. When I animate myView (size.x+20) my subview is being animated too, however I want to translate my subview at the same time independently(origin.x-40) (without the subview moving because of the resizing of myView).
I was able to do it by adjusting the translations (or better position, because I might wanna change y later) .fromValue, .toValue and .duration to compensate movement caused by changes to myView.
UIView beginAnimations:nil context:NULL];
[myView setFrame:pVnewFrame];
[UIView commitAnimations];
CABasicAnimation *bBTranslateAnimation = [CABasicAnimation animationWithKeyPath:#"position"];
bBTranslateAnimation.delegate = self;
bBTranslateAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(160, 480)];
bBTranslateAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(160, 436)];
bBTranslateAnimation.duration = 0.35;
[buttonBar addAnimation:bBTranslateAnimation forKey:#"anim"];
But that is no proper solution. Is there a good way to move view and subviews independently?
(setting autoresizeSubviews and autoresizingMask doesn't help)
Thanks in advance!
The way to do this is to make the two views siblings, instead of making one view a subview of the other. Just put the view that was the subview on top of the view that was the parent view, and it should look like you want it to.
If you want the "child" view to clip to the contents of the former parent view, you might need to put the view that was the child inside a container view that is a sibling of the view that was the parent, and set the container view to opaque = no and with a clear background color. That will affect performance, however.
Related
I have an NSPopover, which switches between two View Controllers.
I set one at launch,
self.popover.contentViewController = viewController1;
and I change to the other one based on a user action:
self.popover.contentViewController = viewController2;
AppKit does some animations to the NSPopover itself to accomodate the second VC, which work well, but the transition between the two View Controller’s views is jarring. I would like them to slide in and out horizontally or at the very least cross fade.
I am using layer-backed views, which I'm setting like this in the AppDelegate’s applicationDidFinishLaunching method:
self.popover.contentViewController.view.wantsLayer = YES;
self.popover.contentViewController.view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;
Here’s what I’ve tried:
fading out the first View Controller's view in -viewWillDisappear and fading in the second View Controller's view in -viewWillAppear. This fades out the first VC’s view immediately and does not fade the second VC’s view back in
fading out the first VC’s view’s layer before switching the popover’s contentViewController:
CALayer *theLayer = self.popover.contentViewController.view.layer;
CABasicAnimation* fadeAnim = [CABasicAnimation animationWithKeyPath:#"opacity"];
fadeAnim.fromValue = [NSNumber numberWithFloat:1.0];
fadeAnim.toValue = [NSNumber numberWithFloat:0.0];
fadeAnim.duration = 1.0;
[theLayer addAnimation:fadeAnim forKey:#"opacity"];
theLayer.opacity = 0.0;
self.popover.contentViewController = self.viewController;
This second strategy doesn't wait for the animation to finish before switching the View Controllers over.
How do I animate changing the NSPopover’s contentViewController?
So since you have multiple child view controllers in a parent view controller you should follow the view controller containment protocol. Rather than animating the views you want to animate the transition between the view controllers themselves.
toController.view.frame = fromController.view.bounds;
[containerViewController addChildViewController:toController];
[fromController willMoveToParentViewController:nil];
[containerViewController transitionFromViewController:fromViewController
toViewController:toViewController
duration:0.2
options:UIViewAnimationOptionTransitionCrossDissolve
animations:nil
completion:^(BOOL finished) {
[fromViewController removeFromParentViewController];
[toViewController didMoveToParentViewController:containerViewController];
toViewController.view.frame = containerViewController.view.bounds;
}];
Where containerViewController is your contentViewController.
Pretty much just tell the old view controller it's being removed from the parent, animate the transition, and remove the old controller and let the new controller know it's been moved to the parent.
More info from apple here: https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html
And good tutorial on custom transitions here: http://www.objc.io/issues/12-animations/custom-container-view-controller-transitions/
I have a view controller that on certain action presents another view controller that covers bottom half of the screen.
When I animate the second view controller (presented view controller) from the bottom to cover the bottom half of the screen, I also want animate the presenting view to top half of the screen. See the code below -
- (void)animatePresentationWithTransitioningContext:(id<UIViewControllerContextTransitioning>) transitioningContext
{
UIViewController *presentedController = [transitioningContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *presentedControllerView = [transitioningContext viewForKey:UITransitionContextToViewKey];
UIView *containerView = [transitioningContext containerView];
presentedControllerView.frame = [transitioningContext finalFrameForViewController:presentedController];
CGPoint center = presentedControllerView.center;
center.y = containerView.bounds.size.height;
presentedControllerView.center = center;
[containerView addSubview:presentedControllerView];
// This returns nil
UIView *presentingControllerView = [transitioningContext viewForKey:UITransitionContextFromViewKey];
[UIView animateWithDuration:[self transitionDuration:transitioningContext] delay:0.0 usingSpringWithDamping:1.0 initialSpringVelocity:0.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
CGPoint presentedViewCenter = presentedControllerView.center;
presentedViewCenter.y -= containerView.bounds.size.height/2.0;
presentedControllerView.center = presentedViewCenter;
// This is where I want to move the presenting view controller.
// of course it does not work.
CGPoint presentingViewCenter = presentingControllerView.center;
presentingViewCenter.y -= containerView.bounds.size.height/2.0;
presentingControllerView.center = presentingViewCenter;
} completion:^(BOOL finished) {
[transitioningContext completeTransition:finished];
}];
}
The viewForKey:UITransitionContextFromViewKey returns nil. I overrode shouldRemovePresentersView to return NO in my custom UIPresentationController but I still get nil. I am using UIModalPresentationCustom as the modal presentation style for the view that I am presenting.
EDIT:
I realized that I can achieve the same affect by animating the presenting view by overriding the presentationTransitionWillBegin method in my custom UIPresentationController. But I would like to get an answer on accessing the presenting view controller.
EDIT:
Since I have found a solution, clarifying my question: (1) Is this the expected behavior i.e. the presentingViewController is going to be nil in the animator object?
I am using UIModalPresentationCustom as the modal presentation style for the view that I am presenting.
Well, there's your problem. In that case, the From view will be nil. It is nil unless you are using the FullScreen presentation style.
I'm not saying you are wrong to make the presentation style Custom. You need to do that, if you want to leave the presenting view controller's view in place, and if you want to supply your own presentation controller. And you do! But in that case, the work is divided: it is the presentation controller that becomes responsible for the final position of the view.
By the way, it occurs to me (thinking about it some more) that you are doing something you should probably not be doing. You are not expected to move the presenting view controller's view. It might be safer to take a snapshot view of the presenting view controller's view and animate that, behind your presented view controller's view.
I am creating my own slide menu which is pretty simple.
I am creating a subview, adding it to the view and then sliding the self.view to the right by 170px which reveals the new subview (named secondView).
However, none of the buttons on the new view are clickable after moving the self.view to the right (as they were before I did this). So I image that because the view has moved, the view is the clickable region of the app.
Is there a way to move the content across as opposed to the view to allow the new view to be clickable.
The problem is:
Touches outside of a view's frame never trigger events to its subviews.
I mean, touches outside of self.view.frame never trigger event to secondView that is one of subviews of self.view.
see: Event Handling Guide for iOS
You can [self.view.window addSubview:secondView] and move both self.view and secondView.
[self.view.window addSubView:secondView];
CGRect viewFrame = self.view.frame;
CGRect secondFrame = secondView.frame;
viewFrame.origin.x += 170;
secondFrame.origin.x += 170;
[UIView animateWithDuration:0.2 animations:^{
self.view.frame = viewFrame;
secondView.frame = secondViewFrame;
}];
Note:
Above code may cause troubles if self is not window's rootViewController. e.g. in UINavigationController.
I have some subviews that I place inside each of my tab bar's view controllers. Right now I'm sizing it with a pixel count but it only works on the iPhone 4 and 4s and not the iPhone 5 because of the longer screen size. I could check for the device and then size it that way but I feel like there has to be an easier way to do this.
viewController1.view.frame = CGRectMake(0, 0, 320, 460);
I colored the subview yellow so it's easier to see.
You should NOT change frame of tabbar's content view controller's view. UITabBar takes itself care of sizing the child view controller's frame properly.
If you want to add subview to content view controller (controller under some tab) and make that view to always automatically resize with the controllers main view (self.view), you can use combination of superviews frame and autoresizing.
Example code (you can do this in - (void)viewDidLoad for example):
UIView *view = [[UIView alloc] initWithFrame:self.view.bounds];
view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:view];
If you want to do this by setting frame than DO this:
[childView setFrame:childView.superview.bounds];
The master view in your view controller should already be the size of the usable space on the sceen. In general, if you want a view to be the same size as it's parent, you can use view.frame = view.superview.frame, though I doubt that would be a good idea to call on the view controller's view.
This is how I switch from one UIView to another UIView: self.view = MyView;
How would I fade out a view to my MySecondView?
You can use UIView's build in transition code to cross dissolve between them.
[UIView transitionFromView:self.view toView:MySecondView duration:0.25 options:UIViewAnimationOptionTransitionCrossDissolve completion:^(BOOL finished) {
// What to do when its finished.
}];
You can also use the regular animation options with something like this. I haven't tested this code as I'm not in front of Xcode but I use it all the time for labels and things without default animations.
[UIView animateWithDuration:0.25 animations:^{
[self.view setAlpha:0.0];
self.view = mySecondView;
[self.view setAlpha:1.0];
}];
Please note that it's not a good idea to do things like self.view = MyView to change screens. By the time you get to a few screens, your viewController will be filled with spaghetti code. You should consider presenting new view controllers that manage their views. One way you can do fade is as follows:
Fade the current view to black (with animation)
In the view controller that you are going to push use viewWillAppear to fade the view to black as well
Push/Present the view controller without animations.
Now use the viewDidAppear method of the newly presented view controller to fade in the view (with animation).