如何使动画代码可用于多个类 - 可重用性

时间:2016-03-22 16:52:51

标签: ios objective-c cocoa-touch

想要在几个视图控制器中使用下面的代码(UIImageView淡入/淡出),但不想让它在许多位置编码。我一直在研究使用类别,协议和扩展。我知道这里已经讨论了这些概念,但还没有找到一个更通用的答案。

如果我要使用一个类别,它将以哪个类为基础? UIView的? NSObject的?

Apple的Working with Protocols似乎是一种可能的解决方案,其中就是这句话:

  

在对象的类未知或需要保持隐藏的情况下,协议也很有用。

请建议为什么要使用另一个。

dispatch_async(dispatch_get_main_queue(), ^{
    [UIView animateWithDuration:0.8 delay:0.5 options:0 animations: ^{
                         self.statusImageView.alpha = 1.0f;
                     }
                     completion: ^(BOOL finished) {
                         [UIView animateWithDuration:0.8 delay:2.5  options:0 animations: ^{
                                              self.statusImageView.alpha = 0.0f;
                                          }
                                          completion: ^(BOOL finished) {
                                              self.statusImageView.hidden = YES;
                                          }];
                     }];
});

3 个答案:

答案 0 :(得分:4)

在这种情况下,我不会使用类别或子类,但使用composition over inheritance:将动画代码放在类Animator中,将其作为属性添加到视图控制器或一个UIView子类。当动画发生时,在动画师上调用一个方法,该方法将为给定视图设置动画。

您现在可以针对不同情况配置动画师,并在需要时重复使用它。您也可以对所需的动画师进行子类化。

Animator.h

@protocol Animator <NSObject>

-(void)animate;

@end

@interface Animator : NSObject <Animator>
-(instancetype)initWithView:(UIView *)view;
@end


@interface FadeInAndOutAnimator : Animator
-(instancetype)initWithView:(UIView *)view
             fadeInDuration:(CGFloat)fadeInDuration
            fadeOutDuration:(CGFloat)fadeOutDuration;


-(instancetype)initWithView:(UIView *)view
             fadeInDuration:(CGFloat)fadeInDuration
            fadeOutDuration:(CGFloat)fadeOutDuration
                fadeInDelay:(CGFloat) fadeInDelay
                fadeOuDelay:(CGFloat) fadeOutDelay;
@end

Animator.m

#import "Animator.h"

@interface Animator ()
@property(nonatomic, weak) UIView *view;
@end

@implementation Animator
-(instancetype)initWithView:(UIView *)view
{
    self = [super init];
    if (self){
        self.view = view;
    }
    return self;
}

-(void)animate {
    NSAssert(NO, @"%@ must be overwritten in a subclass", NSStringFromSelector(_cmd));

}
@end


@interface FadeInAndOutAnimator ()
@property CGFloat fadeOutDuration;
@property CGFloat fadeInDuration;
@property CGFloat fadeInDelay;
@property CGFloat fadeOutDelay;
@end

@implementation FadeInAndOutAnimator

-(instancetype)initWithView:(UIView *)view
             fadeInDuration:(CGFloat)fadeInDuration
            fadeOutDuration:(CGFloat)fadeOutDuration
{
    return [self initWithView:view
               fadeInDuration:fadeInDuration
              fadeOutDuration:fadeOutDuration
                  fadeInDelay:.8
                  fadeOuDelay:2.5];
}

-(instancetype)initWithView:(UIView *)view
             fadeInDuration:(CGFloat)fadeInDuration
            fadeOutDuration:(CGFloat)fadeOutDuration
                fadeInDelay:(CGFloat)fadeInDelay
                fadeOuDelay:(CGFloat)fadeOutDelay
{
    self = [super initWithView:view];
    if(self){
        self.fadeOutDuration = fadeOutDuration;
        self.fadeInDuration = fadeInDuration;
        self.fadeInDelay = fadeInDelay;
        self.fadeOutDelay = fadeOutDelay;
    }
    return self;
}


-(void)animate
{

    [UIView animateWithDuration:self.fadeInDuration delay:self.fadeInDelay options:0 animations: ^{
        self.view.alpha = 1.0f;
    }
                     completion: ^(BOOL finished) {
                         [UIView animateWithDuration:self.fadeOutDuration delay:self.fadeOutDelay  options:0 animations: ^{
                             self.view.alpha = 0.0f;
                         }
                                          completion: ^(BOOL finished) {

                                          }];
                     }];

}

@end

ViewController.m

#import "ViewController.h"
#import "Animator.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *statusView;
@property (strong, nonatomic) id<Animator> animator;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

- (IBAction)animateTapped:(id)sender {
    self.animator = [[FadeInAndOutAnimator alloc] initWithView:self.statusView
                                                fadeInDuration:.8
                                               fadeOutDuration:.5];
    [self.animator animate];
}

- (IBAction)animateFastTapped:(id)sender {
    self.animator = [[FadeInAndOutAnimator alloc] initWithView:self.statusView
                                                fadeInDuration:.8
                                               fadeOutDuration:.5
                                                   fadeInDelay:0
                                                   fadeOuDelay:0];
    [self.animator animate];
}
@end

我在GitLab

发布了一个完整的项目

请注意,Animator也是一种协议。视图控制器接受实现此协议的任何对象。类Animator现在是抽象的,animate必须被覆盖。

答案 1 :(得分:1)

当您为视图控制器设置自定义属性statusImageView时,协议扩展和包含实际动画代码的默认实现可能是一个很好的组合,以便重用动画代码。

示例协议可能如下所示,您是否定义了符合类型中需要存在的所有变量以及将在默认实现中提供的动画函数的签名:

protocol StatusAnimatable {
    var statusView: UIView { get }
    func animateStatusView()
}

我是默认实现,然后我们可以添加实际的动画代码:

extension StatusAnimatable {
    func animateStatusView() {
        UIView.animateWithDuration(0.3) {
            // Perform all animations using statusView
            self.statusView.alpha = 1
        }
    }
}

现在我们能够将任何具有statusView属性的UIViewController符合我们的协议并在其上调用animateStatusView。

编辑:您似乎想要在任何UIView上执行此特定淡入淡出动画 如果是这种情况,您只需在UIView上添加一个包含您要执行的特定淡入淡出动画代码的扩展名:

extension UIView {
    func fadeIn(duration: NSTimeInterval = 0.5, delay: NSTimeInterval = 0, completion: (() -> Void)? = nil) {
        UIView.animateWithDuration(duration, delay: delay, options: [], animations: { () -> Void in
        self.alpha = 1.0
        },
        completion: { _ in
            completion?()
        })
    }

    func fadeOut(duration: NSTimeInterval = 0.5, delay: NSTimeInterval = 0, completion: (() -> Void)? = nil) {
        UIView.animateWithDuration(duration, delay: delay, options: [], animations: { () -> Void in
            self.alpha = 0
        },
        completion: { _ in
            self.hidden = true
            completion?()
        })
    }
}

然后将两个函数链接起来:

let someView = UIView()

someView.fadeIn {
    someView.fadeOut()
}

答案 2 :(得分:1)

UIView上的类别。 创建一个新文件(File \ New \ New File)并选择iOS \ Cocoa Touch \ Objective-C类别模板。为类别输入“Animation”,为Category打开“UIView”,然后完成创建文件。类别允许我们向现有类添加一些方法(在本例中为UIView),而不对其进行子类化:扩展对象功能的非常简洁和强大的方法。我们将把所有动画代码都放在这个类别中。

在UIView + Animation.h中:

- (void) moveTo:(CGPoint)destination duration:(float)duration delay:(float)delay option:(UIViewAnimationOptions)option;

然后在UIView + Animation.m中添加实现,如下所示:

- (void) moveTo:(CGPoint)destination duration:(float)duration delay:(float)delay option:(UIViewAnimationOptions)option
{
    [UIView animateWithDuration:duration delay:delay options:option
        animations:^{
            self.frame = CGRectMake(destination.x,destination.y, self.frame.size.width, self.frame.size.height);
        }
        completion:nil];
}

<强>用途:

#import "UIView+Animation.h"

您决定拥有动画的代码中的某个地方:

UIButton* movingButton= //initialization code of button or UIView or say any subclass of UIView

[movingButton moveTo:
CGPointMake(button.center.x - (movingButton.frame.size.width/2),
         button.frame.origin.y - (movingButton.frame.size.height + 5.0))                
        duration:1.0 option:0]; // above the tapped button

希望这有助于您了解基本流程。

除了基本概念之外,还有一个很好的类别UIView+Animation