如何通过设置具有自动布局约束的框架来动画 UIView?

问题描述 投票:0回答:1

我有一个名为 MyView 的 UIView 子类

@implementation MyView
{
  UIView<SomeImageProtocol> *_imageView;
}

- (instancetype) init
{
  if (self = super) {
    _imageView = ...
    [self addSubview:_imageView];
  }
}

...
- (void)layoutSubviews
{
  [super layoutSubviews];

  _imageView.frame = self.bounds;
}

..

在我的 MyViewController 中,我想触发一些图像动画,即缩小尺寸:

-(void)viewDidLoad
{
  _dwellTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(animateView) userInfo:nil repeats:NO];
}

- (void)animateView
{
  [myView animateOnImage];
}

在 MyView 类中,当我设置

_imageView
的框架时,动画似乎不起作用,但是当我设置其不透明度(如
.alpha
.layer.cornerRadius
)时,它工作得很好。我在网上遇到的资源表明
_imageView
具有自动布局约束,并且其框架仍然由我在
layoutSubviews
中指定的控制。动画似乎总是以自动布局结束。

-(void)animateOnImage {
  [UIView animateWithDuration:2 animations:^{
    [self shrinkImageView];
  }];
}

- (void)shrinkImageView {
  // Doesn't work
  _imageView.frame = CGRectMake(100, 100, 400, 500);
  // Works
  _imageView.alpha = 0.5;
}
ios objective-c uiview uiviewanimation
1个回答
0
投票

问题不在于自动布局限制......

问题是,在

UIView. animateWithDuration
中设置子视图的框架会触发对
layoutSubviews
的调用...您可以在其中再次设置子视图的框架。

为了避免这种情况,我们可以添加一个

CGRect
属性来跟踪
self.bounds
,并且在
layoutSubviews
中,仅在边界发生更改时更新
_imageView.frame

示例代码...


MyView.h

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface MyView : UIView
-(void)animateOnImage;
@end

NS_ASSUME_NONNULL_END

MyView.m

#import "MyView.h"

@implementation MyView

{
    UIView *_imageView;
    CGRect myBounds;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        _imageView = [UIView new];
        _imageView.backgroundColor = UIColor.blueColor;
        [self addSubview:_imageView];
        myBounds = CGRectZero;
    }
    return self;
}
- (void)layoutSubviews
{
    [super layoutSubviews];
    
    // debug - log that layoutSubviews was called
    NSLog(@"layoutSubviews");
    
    // only execute the following if self.bounds has changed
    if (!CGRectEqualToRect(myBounds, self.bounds)) {
        
        // debug - log that self.bounds has changed
        NSLog(@"self.bounds changed, so set _imageView frame");
        myBounds = self.bounds;
        _imageView.frame = self.bounds;
    }
}

-(void)animateOnImage {
    [UIView animateWithDuration:2 animations:^{
        [self shrinkImageView];
    }];
}

- (void)shrinkImageView {
    _imageView.frame = CGRectMake(50, 100, 150, 200);
    _imageView.alpha = 0.5;
}


@end

AnimSubViewController.h

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface AnimSubViewController : UIViewController

@end

NS_ASSUME_NONNULL_END

AnimSubViewController.m

#import <UIKit/UIKit.h>
#import "AnimSubViewController.h"
#import "MyView.h"

@interface AnimSubViewController ()
{
    MyView *myView;
}
@end

@implementation AnimSubViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = UIColor.systemBackgroundColor;
    
    myView = [MyView new];
    myView.backgroundColor = UIColor.systemYellowColor;
    myView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:myView];
    
    UILayoutGuide *g = self.view.safeAreaLayoutGuide;
    
    [NSLayoutConstraint activateConstraints:@[
        [myView.topAnchor constraintEqualToAnchor:g.topAnchor constant:120.0],
        [myView.leadingAnchor constraintEqualToAnchor:g.leadingAnchor constant:80.0],
        [myView.trailingAnchor constraintEqualToAnchor:g.trailingAnchor constant:-80.0],
        [myView.bottomAnchor constraintEqualToAnchor:g.bottomAnchor constant:-120.0],
    ]];
    
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(animateView) userInfo:nil repeats:NO];
}

- (void)animateView
{
    [myView animateOnImage];
}

@end
© www.soinside.com 2019 - 2024. All rights reserved.