【问题标题】:iOS: How to convert UIViewAnimationCurve to UIViewAnimationOptions?iOS:如何将 UIViewAnimationCurve 转换为 UIViewAnimationOptions?
【发布时间】:2011-11-11 17:42:58
【问题描述】:

UIKeyboardAnimationCurveUserInfoKey 具有 UIViewAnimationCurve 值。如何将其转换为对应的UIViewAnimationOptions 值,以便与+[UIView animateWithDuration:delay:options:animations:completion:]options 参数一起使用?

// 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;

显然,我可以使用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

但是,有没有更简单/更好的方法?

【问题讨论】:

    标签: objective-c ios uiview enums uiviewanimation


    【解决方案1】:

    您建议的类别方法是执行此操作的“正确”方法——您不一定能保证这些常量保持其值。但是,从查看它们的定义方式来看,您似乎可以这样做

    animationOption = animationCurve << 16;
    

    ...如果编译器想抱怨的话,可能会先转换为 NSUInteger,然后再转换为 UIViewAnimationOptions。

    【讨论】:

    • 我建议使用 NSAssert(UIViewAnimationCurveLinear &lt;&lt; 16 == UIViewAnimationOptionCurveLinear, @"Unexpected implementation of UIViewAnimationCurve"); 之类的东西来保护它
    • @lhunath 7 用于键盘动画并且是私有的。但通过键盘的 userInfo 传递。
    【解决方案2】:

    可以说,您可以采用您的第一个解决方案并将其设为内联函数,以节省您自己的堆栈推送。这是一个非常严格的条件(常量绑定等),它应该编译成一个非常小的程序集。

    编辑: 根据@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;
      }
    }
    

    斯威夫特 3:

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

    【讨论】:

    • 我该怎么做?我认为 LLVM 会尽可能自动将 Objective-C 方法转换为内联函数。
    • 听起来有人已经回答了你的问题:stackoverflow.com/questions/8194504/…
    • 我在答案中添加了内联版本。
    • "return" 立即发生,所以不会。
    • 这不是一个好方法。 UIKeyboardWillShowNotification[userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue] 返回 7。在这个 switch 中没有这种情况。查看票数最高的答案。
    【解决方案3】:

    在 Swift 中你可以做到

    extension UIViewAnimationCurve {
        func toOptions() -> UIViewAnimationOptions {
            return UIViewAnimationOptions(rawValue: UInt(rawValue << 16))
        }
    }
    

    【讨论】:

      【解决方案4】:

      基于 switch 的解决方案的一个问题是它假设永远不会传入任何选项组合。但实践表明,可能存在假设不成立的情况。我发现的一个例子是(至少在 iOS 7 上)当您获得键盘动画以使您的内容与键盘的出现/消失一起动画时。

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

      UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
      

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

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

      static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCurve curve)
      {
          UIViewAnimationOptions opt = (UIViewAnimationOptions)curve;
          return opt << 16;
      }
      

      这里需要注意的是,正如 Noah 也指出的那样,如果 Apple 更改了两种类型不再对应的枚举,那么这个函数就会中断。无论如何都要使用它的原因是,基于开关的选项并非在您今天可能遇到的所有情况下都有效,而这确实有效。

      【讨论】:

      • 如果 UIKeyboardAnimationCurveUserInfoKey 的值为 7,这听起来像是一个错误。文档声明该值是 UIViewAnimationCurve。 UIViewAnimationCurve 被定义为 NS_ENUM,而不是 NS_OPTIONS。因此,唯一可能的值是 0、1、2 和 3。任何其他值都没有意义。另一方面,UIViewAnimationOption 被定义为 NS_OPTIONS,并且可以有大约 32,000 个不同的值。甚至 Noah 的解决方案都无法处理 7 的值,它只会将其视为传入 UIViewAnimationCurveLinear。
      【解决方案5】:

      iOS 10+
      Swift 5

      UIView.AnimationCurve 转换为 UIView.AnimationOptions 的 Swift 替代方法是使用 UIViewPropertyAnimator (iOS 10+),它接受 UIView.AnimationCurve 并且是比 UIView.animate 更现代的动画师.

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

      public class let keyboardAnimationCurveUserInfoKey: String // NSNumber of NSUInteger (UIViewAnimationCurve)
      

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

      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()
      }
      

      【讨论】:

        猜你喜欢
        • 2013-10-29
        • 2014-12-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多