50 votes

IOS: Comment convertir UIViewAnimationCurve en UIViewAnimationOptions?

La clé UIKeyboardAnimationCurveUserInfoKey a une valeur de type UIViewAnimationCurve. Comment puis-je la convertir en une valeur correspondante de UIViewAnimationOptions pour l'utiliser avec l'argument options de +[UIView animateWithDuration:delay:options:animations:completion:] ?

// UIView.h

typedef enum {
    UIViewAnimationCurveEaseInOut,         // lent au début et à la fin
    UIViewAnimationCurveEaseIn,            // lent au début
    UIViewAnimationCurveEaseOut,           // lent à la fin
    UIViewAnimationCurveLinear
} UIViewAnimationCurve;

// ...

enum {
    // ...
    UIViewAnimationOptionCurveEaseInOut            = 0 << 16, // par défaut
    UIViewAnimationOptionCurveEaseIn               = 1 << 16,
    UIViewAnimationOptionCurveEaseOut              = 2 << 16,
    UIViewAnimationOptionCurveLinear               = 3 << 16,
    // ...
};
typedef NSUInteger UIViewAnimationOptions;

De toute évidence, je pourrais créer une méthode de catégorie simple avec une instruction switch, comme ceci :

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

Mais y a-t-il un moyen encore plus facile/meilleur ?

44voto

Noah Witherspoon Points 35239

La méthode de catégorie que vous suggérez est la "bonne" façon de le faire - vous n'avez pas nécessairement la garantie que ces constantes conservent leur valeur. En regardant comment elles sont définies, cependant, il semble que vous pourriez simplement faire

animationOption = animationCurve << 16;

... éventuellement avec un cast en NSUInteger puis en UIViewAnimationOptions, si le compilateur se plaint de cela.

8 votes

Je recommande de protéger cela avec quelque chose comme NSAssert(UIViewAnimationCurveLinear << 16 == UIViewAnimationOptionCurveLinear, @"Implémentation inattendue de UIViewAnimationCurve");

2 votes

@lhunath 7 est utilisé pour l'animation du clavier et est privé. Mais passé avec userInfo du clavier.

36voto

David Pisoni Points 1293

On peut arguer que vous pouvez prendre votre première solution et en faire une fonction en ligne pour économiser la poussée de la pile. C'est une condition tellement serrée (liée à une constante, etc.) qu'elle devrait se compiler en un tout petit morceau d'assemblage.

Éditer: Selon @matt, voici (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;
  }
}

Swift 3:

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

0 votes

Comment puis-je faire cela? Je pensais que LLVM convertissait automatiquement les méthodes Objective-C en fonctions en ligne lorsque c'est possible.

0 votes

Il semble que quelqu'un d'autre ait déjà répondu à votre question: stackoverflow.com/questions/8194504/…

0 votes

J'ai ajouté la version en ligne à ma réponse.

19voto

Tomáš Linhart Points 1399

En Swift, vous pouvez faire

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

11voto

Antonio Nunes Points 86

Un problème avec la solution basée sur les switchs est qu'elle suppose qu'aucune combinaison d'options ne sera jamais passée. La pratique montre cependant qu'il peut y avoir des situations où cette hypothèse ne tient pas. Une instance que j'ai trouvée est (au moins sur iOS 7) lorsque vous obtenez les animations du clavier pour animer votre contenu avec l'apparition/disparition du clavier.

Si vous écoutez les notifications keyboardWillShow: ou keyboardWillHide:, puis obtenez la courbe que le clavier annonce qu'il va utiliser, par exemple :

UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];

vous aurez probablement la valeur 7. Si vous passez cette valeur dans la fonction/méthode switch, vous n'obtiendrez pas une traduction correcte de cette valeur, entraînant un comportement d'animation incorrect.

La réponse de Noah Witherspoon renverra la bonne valeur. En combinant les deux solutions, vous pourriez écrire quelque chose comme :

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

L'avertissement ici, comme l'a également noté Noah, est qu'en cas de modification des énumérations par Apple, où les deux types ne correspondent plus, cette fonction deviendra obsolète. La raison de l'utiliser de toute façon, c'est que l'option basée sur les switchs ne fonctionne pas dans toutes les situations que vous pourriez rencontrer aujourd'hui, tandis que cela fonctionne.

2 votes

Si vous obtenez une valeur de 7 pour UIKeyboardAnimationCurveUserInfoKey, cela ressemble à un bug. La documentation indique que la valeur est une UIViewAnimationCurve. UIViewAnimationCurve est défini comme un NS_ENUM, pas NS_OPTIONS. Par conséquent, les seules valeurs possibles sont 0, 1, 2 et 3. Toute autre valeur est sans signification. UIViewAnimationOption, en revanche, est défini comme NS_OPTIONS et peut avoir environ 32 000 valeurs différentes. Même la solution de Noah ne peut pas gérer une valeur de 7, elle la traitera simplement comme si UIViewAnimationCurveLinear avait été transmis.

5voto

bsod Points 3192

iOS 10+
Swift 5

Une alternative Swift pour convertir UIView.AnimationCurve en UIView.AnimationOptions, qui peut ne pas être possible, est d'utiliser UIViewPropertyAnimator (iOS 10+), qui accepte UIView.AnimationCurve et est un animateur plus moderne que UIView.animate.

Vous travaillerez probablement avec UIResponder.keyboardAnimationCurveUserInfoKey, qui renvoie un NSNumber. La documentation pour cette clé est (notation d'Apple, pas la mienne):

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

En utilisant cette approche, nous pouvons éliminer toute supposition:

if let kbTiming = notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber, // la documentation dit de déballer en tant que NSNumber
    let timing = UIView.AnimationCurve.RawValue(exactly: kbTiming), // prend un NSNumber
    let curve = UIView.AnimationCurve(rawValue: timing) { // prend une valeur brute
    let animator = UIViewPropertyAnimator(duration: duration, curve: curve) {
        // ajouter des animations
    }
    animator.startAnimation()
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X