简体   繁体   中英

Animated uiview switches back to its original state when rotating device

I try to make a tiny animation under IOS7. My view controller has 2 views: a tableview and a mapview. I have a button, that switches between 'show map full screen' and 'show map and tableview' state.

Here is my experimental code:

float mapNewX =  self.mapView.frame.origin.x == 0 ? 375 : 0;
float mapNewWidth = mapNewX == 0 ? self.view.frame.size.width : self.view.frame.size.width - mapNewX;

[UIView animateKeyframesWithDuration:0.1 delay:0.0 options:UIViewAnimationCurveEaseOut animations:^
 {
     CGRect mapViewFrame = self.mapView.frame;
     mapViewFrame.origin.x = (mapNewX);
     mapViewFrame.size.width = mapNewWidth;
     self.mapView.frame = mapViewFrame;
 }
completion:^(BOOL finished)
 {
     NSLog(@"OK");
 }];

It works, but when I rotate my device, it switches back to the original state. So, when I have full map display, when rotating device, it switches back to tableview + mapview on the screen.

The same happens, if I come back from another view controller by navigation controller. If I tap detail disclosure of a map pin, it switches back to the original layout.

What should I do? Thanks very much!

Update: here are the screenshots before/after rotation (some sensitive data is masked out)

在此处输入图片说明

在此处输入图片说明

If you loose frame changes after rotation or screen navigation, it probably relates to view controller's layout methods viewWillLayoutSubviews , viewDidLayoutSubviews or life cycle methods viewWillAppear: , viewDidAppear: . Check implementations of these methods if you override them.

Because you use auto layout, it's better to animate its constraints instead of frames.

As far as I understood, your task is to move left side of a map to reveal a table view underneath (opened/closed states). With autolayout your screen might look as simple as this:

在此处输入图片说明

(UIImageView view plays a role of a map)

UITableView constraints

  • constant width constraint
  • top, leading and bottom space to superview constraints set to zero

UIImageView constraints

  • top, bottom and trailing space to superview constraints set to zero
  • leading space to superview set to table width (opened state, animate this constraint)

To implement desired effect we need to animate leading space to superview constraint (selected on a the screenshot). We create an outlet to this constraint and change its constant property to opened/closed state.

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mapLeadingSpace;

Set initial state of the menu before it will appear on screen. We set a default value, but you can restore an appropriate boolean setting from NSUserDefaults .

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    self.mapLeadingSpace.constant = 0; // default is closed
}

Next, toggle animation on bar button press.

- (IBAction)toggle:(id)sender
{
    self.mapLeadingSpace.constant = self.mapLeadingSpace.constant == 0
        ? self.tableView.frame.size.width    // opened
        : 0;                                 // closed

    [UIView animateWithDuration:.2 animations:^{
        [self.imageView layoutIfNeeded];
    }];
}

Using - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation

Create a variable to indicate whether it is full screen map or split view, then run the routine once rotation is completed? Not sure how hi-tech of a solution that is, but it's what i'd do! :)

I had a similar split views (2 UItableViews), here is how i managed to fix it.

  • First, have a global static variable lets call it static BOOL _isViewSplitted its static so when you push or pop viewContorller is stays in memory.

  • Override didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation check your variable here and if YES, split them if NO, just return.

  • Also, you have to check (do the same as above) in viewWillAppear when the viewController is popped.

You could probably fix this by coding your constraints and then animating them.

Something like this (the values in this code doesn't match the ones in your example):

[subview setTranslatesAutoresizingMaskIntoConstraints:NO];
[parentview setTranslatesAutoresizingMaskIntoConstraints:NO];

[parentview addSubview:subview];

NSDictionary *views = NSDictionaryOfVariableBindings(subview);
[parentview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[subview]"
                                                                   options:0
                                                                   metrics:nil
                                                                     views:views]];

NSArray *firstConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[subview]"
                                                                    options:0
                                                                    metrics:nil
                                                                      views:views];
[parentview addConstraints:firstConstraints];
[parentview layoutSubtreeIfNeeded];
[parentview removeConstraints:firstConstraints];

NSArray *secondConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-100-[subview]-100-|"
                                                                     options:0
                                                                     metrics:nil
                                                                       views:views];
[parentview addConstraints:secondConstraints];

subview.superview.wantsLayer = YES;
[NSAnimationContext runAnimationGroup:^(NSAnimationContext* context) {
    [context setDuration:.3];
    [context setAllowsImplicitAnimation:YES];
    [context setTimingFunction: [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
    [subview layoutSubtreeIfNeeded];
} completionHandler:^{
        [[self delegate] createOptionMenuForPDFViewWithInvoice:invoice andSuperView:parentview];
}];

Tom, your big problem (IMHO) is that you are calling this code that you've provided in your question too often. Every time its going to toggle the map from full screen to split because it makes this decision from where it is now. Why don't you just have a boolean ivar you check? you can flip this in your button action to toggle the maps full screen state, then it won't matter if you repeatedly call this code on reorient etc, just alter the condition in your turnery operators at the start there to this new boolean instead of querying whether the map is full screen or not

This question stood a greater chance of being answered relatively quickly without the need for a bounty had the following information been provided in the original post (in general, this might be a good set of guidelines for any post related to layout in iOS):

(1.) is Auto Layout enabled in the storyboard?

(2.) are you using a mixed (hybrid) approach or pure Auto Layout? in other words, which views have their translatesAutoresizingMaskIntoConstraints set to YES ?

(3.) if any layout methods or rotation callback methods are overridden, provide that implementation

(4.) show us your constraints

I was able to figure out the answer to #1 from the comments.

My answer given the little I know about the problem:

If you are using Auto Layout on the map view, then setting its frame in the animation block won't permanently re-position the view because the map view's constraints haven't been changed. Instead, update the map view's constraints before the animation block, then send layoutIfNeeded to self.view inside the animation block.

// update constraints…

[UIView animateWithDuration:0.5 animations:^{
    [self.view layoutIfNeeded];
}];

Also give a try to this in viewDidload of your Viewcontroller class

if ([self respondsToSelector:@selector(edgesForExtendedLayout)]) {
    self.edgesForExtendedLayout = UIRectEdgeNone;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM