Change frame in viewDidLayoutSubviews

When the view controller’s view is first displayed, I want to start an animation in which all the elements in the view controller slide from the outside at the bottom of the screen to their natural positions. To do this, I execute subview.frame.origin.y += self.view.frame.size.height in viewDidLayoutSubviews . I also tried viewWillAppear , but it doesn't work at all. Then I animate them to their natural positions using subview.frame.origin.y -= self.view.frame.size.height in viewDidAppear .

The problem is that viewDidLayoutSubviews is called several times throughout the life of the controller. That way, when things like showing the keyboard, all of my content is replaced again outside the view.

Is there a better way to do this? Do I need to add some kind of flag to check if the animation is already running?

EDIT: here is the code. Here, I call prepareAppearance on viewDidLayoutSubviews , which works, but viewDidLayoutSubviews is called several times over the life of the controller.

 - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; [self prepareAppearance]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self animateAppearance]; } - (NSArray *)animatableViews { return @[self.createAccountButton, self.facebookButton, self.linkedInButton, self.loginButton]; } - (void)prepareAppearance { NSArray * views = [self animatableViews]; NSUInteger count = [views count]; for (NSUInteger it=0 ; it < count ; ++it) { UIView * view = [views objectAtIndex:it]; CGRect frame = view.frame; // Move the views outside the screen, to the bottom frame.origin.y += self.view.frame.size.height; [view setFrame:frame]; } } - (void)animateAppearance { NSArray * views = [self animatableViews]; NSUInteger count = [views count]; for (NSUInteger it=0 ; it < count ; ++it) { __weak UIView * weakView = [views objectAtIndex:it]; CGRect referenceFrame = self.view.frame; [UIView animateWithDuration:0.4f delay:0.05f * it options:UIViewAnimationOptionCurveEaseOut animations:^{ CGRect frame = weakView.frame; frame.origin.y -= referenceFrame.size.height; [weakView setFrame:frame]; } completion:^(BOOL finished) { }]; } } 
+6
source share
2 answers

If you need to revive something when the view appears, and then not touch the subview later, I would suggest the following:

  • Do not change / tap viewDidLayoutSubviews
  • Add logic to move items off the screen (to the starting position before the animation) in viewWillAppear
  • Add logic to animate elements to their correct position in viewDidAppear

UPDATE

If you use auto-layout (which is very good), you cannot animate views by changing their frames directly (because auto-layout will ignore this and change them again). What you need to do is expose the outputs for the constraints responsible for the Y-position (in your case) and change these constraints, rather than set frames.

Also, be sure to include the call [weakView layoutIfNeeded] after updating the constraints in the animation method.

+6
source

I did two things in viewDidAppear: It seems that when viewDidAppear: is called, the view is not actually visible, but here. Thus, user interface elements are never displayed in their original position if they are replaced there.

0
source

Source: https://habr.com/ru/post/978634/


All Articles