我有一个名为 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;
}
问题不在于自动布局限制......
问题是,在
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