iOS:如何将UIViewAnimationCurve转换为UIViewAnimationOptions?

ma1*_*w28 47 enums objective-c uiview uiviewanimation ios

UIKeyboardAnimationCurveUserInfoKey有一个UIViewAnimationCurve值.如何将其转换为相应的UIViewAnimationOptions值以用于options参数+[UIView animateWithDuration:delay:options:animations:completion:]

// UIView.h

typedef enum {
    UIViewAnimationCurveEaseInOut,         // slow at beginning and end
    UIViewAnimationCurveEaseIn,            // slow at beginning
    UIViewAnimationCurveEaseOut,           // slow at end
    UIViewAnimationCurveLinear
} UIViewAnimationCurve;

// ...

enum {
    // ...
    UIViewAnimationOptionCurveEaseInOut            = 0 << 16, // default
    UIViewAnimationOptionCurveEaseIn               = 1 << 16,
    UIViewAnimationOptionCurveEaseOut              = 2 << 16,
    UIViewAnimationOptionCurveLinear               = 3 << 16,
    // ...
};
typedef NSUInteger UIViewAnimationOptions;
Run Code Online (Sandbox Code Playgroud)

显然,我可以使用switch语句创建一个简单的类别方法,如下所示:

// UIView+AnimationOptionsWithCurve.h

@interface UIView (AnimationOptionsWithCurve)
@end

// UIView+AnimationOptionsWithCurve.m

@implementation UIView (AnimationOptionsWithCurve)

+ (UIViewAnimationOptions)animationOptionsWithCurve:(UIViewAnimationCurve)curve {
    switch (curve) {
        case UIViewAnimationCurveEaseInOut:
            return UIViewAnimationOptionCurveEaseInOut;
        case UIViewAnimationCurveEaseIn:
            return UIViewAnimationOptionCurveEaseIn;
        case UIViewAnimationCurveEaseOut:
            return UIViewAnimationOptionCurveEaseOut;
        case UIViewAnimationCurveLinear:
            return UIViewAnimationOptionCurveLinear;
    }
}

@end
Run Code Online (Sandbox Code Playgroud)

但是,有更简单/更好的方式吗?

Noa*_*oon 44

您建议的类别方法是"正确"的方法 - 您不一定能保证这些常量保持其价值.不过,看看它们是如何定义的,似乎你可以做到

animationOption = animationCurve << 16;
Run Code Online (Sandbox Code Playgroud)

...如果编译器感觉像是在抱怨,可能会使用强制转换为NSUInteger,然后转换为UIViewAnimationOptions.

  • 我建议用`NSAssert(UIViewAnimationCurveLinear << 16 == UIViewAnimationOptionCurveLinear,@"意外实现UIViewAnimationCurve")来保护它; (8认同)
  • @lhunath 7 用于键盘动画并且是私有的。但是从键盘通过 userInfo 传递。 (2认同)

Dav*_*oni 35

可以说你可以采用你的第一个解决方案,并使其成为内联函数,以节省堆栈推送.它是如此紧密的条件(常量限制等),它应该编译成一个非常小的组件.

编辑:每个@matt,这里你去(Objective-C):

static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCurve curve)
{
  switch (curve) {
    case UIViewAnimationCurveEaseInOut:
        return UIViewAnimationOptionCurveEaseInOut;
    case UIViewAnimationCurveEaseIn:
        return UIViewAnimationOptionCurveEaseIn;
    case UIViewAnimationCurveEaseOut:
        return UIViewAnimationOptionCurveEaseOut;
    case UIViewAnimationCurveLinear:
        return UIViewAnimationOptionCurveLinear;
  }
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特3:

extension UIViewAnimationOptions {
    init(curve: UIViewAnimationCurve) {
        switch curve {
            case .easeIn:
                self = .curveEaseIn
            case .easeOut:
                self = .curveEaseOut
            case .easeInOut:
                self = .curveEaseInOut
            case .linear:
                self = .curveLinear
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这不是一个很好的方法.`UIKeyboardWillShowNotification`的`[userInfo [UIKeyboardAnimationCurveUserInfoKey] integerValue]`返回7.此开关中没有这种情况.查看最高投票的答案. (12认同)

Tom*_*art 18

在Swift你可以做到

extension UIViewAnimationCurve {
    func toOptions() -> UIViewAnimationOptions {
        return UIViewAnimationOptions(rawValue: UInt(rawValue << 16))
    }
}
Run Code Online (Sandbox Code Playgroud)


Ant*_*nes 11

基于交换机的解决方案的一个问题是它假设不会传递任何选项组合.但实践表明,可能存在假设不成立的情况.我发现的一个实例(至少在iOS 7上)当您获得键盘动画来动画您的内容以及键盘的外观/消失.

如果您收听keyboardWillShow:keyboardWillHide:通知,然后获得键盘宣布将使用的曲线,例如:

UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
Run Code Online (Sandbox Code Playgroud)

您可能会获得值7.如果将其传递给switch函数/方法,则无法获得该值的正确转换,从而导致不正确的动画行为.

Noah Witherspoon的回答将返回正确的值.结合这两种解决方案,您可以编写如下内容:

static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCurve curve)
{
    UIViewAnimationOptions opt = (UIViewAnimationOptions)curve;
    return opt << 16;
}
Run Code Online (Sandbox Code Playgroud)

正如诺亚所指出的,这里的警告是,如果Apple改变了两种类型不再对应的枚举,那么这个函数就会破坏.无论如何使用它的原因是,基于开关的选项在您今天可能遇到的所有情况下都不起作用,而这样做.

  • 如果 UIKeyboardAnimationCurveUserInfoKey 的值为 7,这听起来像是一个错误。该文档指出该值是一个 UIViewAnimationCurve。UIViewAnimationCurve 被定义为 NS_ENUM,而不是 [NS_OPTIONS](http://nshipster.com/ns_enum-ns_options/)。因此,唯一可能的值是 0、1、2 和 3。任何其他值都没有意义。另一方面,UIViewAnimationOption 被定义为 NS_OPTIONS,并且可以有大约 32,000 个不同的值。即使是 Noah 的解决方案也无法处理 7 的值,它会简单地将其视为传入 UIViewAnimationCurveLinear 。 (2认同)

bso*_*sod 5

iOS 10+
Swift 5

转换UIView.AnimationCurve为 的Swift 替代方案(UIView.AnimationOptions甚至可能不可能)是使用UIViewPropertyAnimator(iOS 10+),它接受UIView.AnimationCurve并且是比UIView.animate.

您很可能会使用UIResponder.keyboardAnimationCurveUserInfoKey,它返回一个NSNumber. 此密钥的文档是(Apple 自己的符号,不是我的):

public class let keyboardAnimationCurveUserInfoKey: String // NSNumber of NSUInteger (UIViewAnimationCurve)
Run Code Online (Sandbox Code Playgroud)

使用这种方法,我们可以消除任何猜测:

if let kbTiming = notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber, // doc says to unwrap as NSNumber
    let timing = UIView.AnimationCurve.RawValue(exactly: kbTiming), // takes an NSNumber
    let curve = UIView.AnimationCurve(rawValue: timing) { // takes a raw value
    let animator = UIViewPropertyAnimator(duration: duration, curve: curve) {
        // add animations
    }
    animator.startAnimation()
}
Run Code Online (Sandbox Code Playgroud)