apparenceWhenContainedIn dans Swift

125

J'essaye de convertir mon application dans la langue Swift.

J'ai cette ligne de code:

[[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil]
                     setTitleTextAttributes:textDictionary
                                   forState:UIControlStateNormal];

Comment le convertir en Swift?

Dans la documentation d'Apple , il n'y a pas de telle méthode.

AlexZd
la source
1
@LukeTheObscure consultez ma réponse ci-dessous ... moche, mais fonctionne.
tcd le

Réponses:

230

Mise à jour pour iOS 9:

Si vous ciblez iOS 9+ (à partir de Xcode 7 b1), il existe une nouvelle méthode dans le UIAppearanceprotocole qui n'utilise pas de varargs:

static func appearanceWhenContainedInInstancesOfClasses(containerTypes: [AnyObject.Type]) -> Self

Qui peut être utilisé comme ceci:

UITextField.appearanceWhenContainedInInstancesOfClasses([MyViewController.self]).keyboardAppearance = .Light

Si vous devez toujours prendre en charge iOS 8 ou une version antérieure, utilisez la réponse originale suivante à cette question.

Pour iOS 8 et 7:

Ces méthodes ne sont pas disponibles pour Swift car les méthodes varargs Obj-C ne sont pas compatibles avec Swift (voir http://www.openradar.me/17302764 ).

J'ai écrit une solution de contournement non variadique qui fonctionne dans Swift (j'ai répété la même méthode pour UIBarItem, qui ne descend pas de UIView):

// UIAppearance+Swift.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIView (UIViewAppearance_Swift)
// appearanceWhenContainedIn: is not available in Swift. This fixes that.
+ (instancetype)my_appearanceWhenContainedIn:(Class<UIAppearanceContainer>)containerClass;
@end
NS_ASSUME_NONNULL_END

-

// UIAppearance+Swift.m
#import "UIAppearance+Swift.h"
@implementation UIView (UIViewAppearance_Swift)
+ (instancetype)my_appearanceWhenContainedIn:(Class<UIAppearanceContainer>)containerClass {
    return [self appearanceWhenContainedIn:containerClass, nil];
}
@end

Assurez-vous simplement de le faire #import "UIAppearance+Swift.h"dans votre en-tête de pontage.

Ensuite, pour appeler depuis Swift (par exemple):

# Swift 2.x:
UITextField.my_appearanceWhenContainedIn(MyViewController.self).keyboardAppearance = .Light

# Swift 3.x:
UITextField.my_appearanceWhenContained(in: MyViewController.self).keyboardAppearance = .light
Alex Pretzlav
la source
15
UIBarButtonItem n'est pas un UIView et doit être étendu séparément
Sergey Skoblikov
7
Notez qu'au moins dans iOS8, UIAppearance + Swift.h devrait importer UIKit / UIKit.h
Chase
UIBarButtonItem peut également être pris en charge si vous créez simplement une autre méthode ciblant UIBarButtonItem au lieu de UIView. @interface UIBarButtonItem (UIViewAppearance_Swift)
Michael Peterson
1
@rptwsthi: J'ai ajouté un exemple pour Swift 3, c'est très légèrement différent
Alex Pretzlav
1
La version iOS 9 pour Swift 3.2 est UIBarButtonItem.appearance (whenContainedInInstancesOf: [UISearchBar.self]). Title = "Done"
Eli Burke
31

ios 10 swift 3

UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).title = "Kapat"
Piotr Tomasik
la source
1
Je ne sais pas pourquoi c'est un vote négatif puisque c'est la façon de le faire dans iOS10 swift 3 ... Merci
Vassily
14

Pour iOS 8 et 7:

J'utilise une catégorie basée sur la réponse d'Alex pour spécifier plusieurs conteneurs. Il s'agit d'une solution de contournement jusqu'à ce qu'Apple prenne officiellement en charge appearanceWhenContainedInSwift.

UIAapparence + Swift.h

@interface UIView (UIAppearance_Swift)
/// @param containers An array of Class<UIAppearanceContainer>
+ (instancetype)appearanceWhenContainedWithin: (NSArray *)containers;
@end

UIAppearance + Swift.m

@implementation UIView (UIAppearance_Swift)

+ (instancetype)appearanceWhenContainedWithin: (NSArray *)containers
{
    NSUInteger count = containers.count;
    NSAssert(count <= 10, @"The count of containers greater than 10 is not supported.");
    
    return [self appearanceWhenContainedIn:
            count > 0 ? containers[0] : nil,
            count > 1 ? containers[1] : nil,
            count > 2 ? containers[2] : nil,
            count > 3 ? containers[3] : nil,
            count > 4 ? containers[4] : nil,
            count > 5 ? containers[5] : nil,
            count > 6 ? containers[6] : nil,
            count > 7 ? containers[7] : nil,
            count > 8 ? containers[8] : nil,
            count > 9 ? containers[9] : nil,
            nil];
}
@end

Ajoutez ensuite #import "UIAppearance+Swift.h"à votre en-tête de pontage.

À utiliser depuis Swift :

TextField.appearanceWhenContainedWithin([MyViewController.self, TableViewController.self]).keyboardAppearance = .Light

C'était bien si je pouvais trouver un moyen d'utiliser CVarArgType , mais je n'ai trouvé aucune solution propre.

Yoichi Tagaya
la source
Belle solution de contournement à la cassure des varargs! C'est vraiment dommage qu'il n'y ait pas de solution générale autre que (maintenant) d'utiliser la méthode iOS 9 d'Apple uniquement.
Alex Pretzlav
12

Voici une solution de contournement moins laide, mais toujours laide, inspirée par @tdun.

  1. Créez une classe pour tenir votre apparence Objective-C. Pour les besoins de cet exemple, appelons-le AppearanceBridger.
  2. Ajoutez cette classe à votre en-tête de pontage. Si vous n'avez pas d'en-tête de pontage, créez-en un .
  3. Créez une méthode de classe dans AppearanceBridgernamed +(void)setAppearanceet placez le code d'apparence Objective-C dans cette méthode. Par exemple:


+ (void)setAppearance {
    [[UIView appearanceWhenContainedIn:[UITableViewHeaderFooterView class], nil] setBackgroundColor:[UIColor whiteColor]];
}

  1. Dans votre code Swift où vous définissez l'apparence, appelez AppearanceBridger.setAppearance()et vous devriez être prêt à partir!

J'espère que cela fonctionne bien pour les personnes qui le voient.

Baub
la source
4

Voici une solution de contournement laide que j'ai utilisée ...

Créez simplement une classe Objective-C Cocoa Touch (UIViewController), nommée comme vous le souhaitez.

J'ai nommé le mien WorkaroundViewController...

Maintenant en (WorkaroundViewController.m ):

-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

Exécutez le code d'apparence Objective-C pour .appearanceWhenContainedIn() (voici mon exemple):

[[UITextField appearanceWhenContainedIn:[UISearchBar class], nil] setDefaultTextAttributes:@{NSFontAttributeName: [UIFont fontWithName:@"Avenir-Light" size:16.0f]}];

ensuite Créez un en-tête de pontage pour votre projet Swift, puis initialisez votre ViewController Objective-C dans votre code Swift, comme ceci (encore une fois, juste mon exemple):

var work : WorkaroundViewController = WorkaroundViewController()

Alors vous avez terminé! Faites-moi savoir si cela fonctionne pour vous ... Comme je l'ai dit, c'est moche, mais ça marche!

tcd
la source
C'est une solution tmp, je pense que nous devrions trouver ou attendre un meilleur moyen :)
AlexZd
@AlexZd, absolument, j'espère qu'ils l'incluront rapidement ... Mais pour l'instant, si vous en avez besoin, c'est parti!
tcd le
4

Cela peut être étendu à n'importe quelle classe conforme au protocole UIAppearance - pas seulement à UIViews. Voici donc une version plus générique:

UIAapparence + Swift.h

#import <UIKit/UIKit.h>

@interface NSObject (UIAppearance_Swift)

+ (instancetype)appearanceWhenContainedWithin:(Class<UIAppearanceContainer>)containerClass;

@end

UIAppearance + Swift.m

#import "UIAppearance+Swift.h"

@implementation NSObject (UIAppearance_Swift)

+ (instancetype)appearanceWhenContainedWithin:(Class<UIAppearanceContainer>)containerClass {
    if ([self conformsToProtocol:@protocol(UIAppearance)]) {
        return [(id<UIAppearance>)self appearanceWhenContainedIn:containerClass, nil];
    }
    return nil;
}

@end
Eddie K
la source
3

J'ai créé un repo pour vous qui souhaitez utiliser CocoaPods:

  1. Ajoutez ceci dans votre Podfile:

    pod 'UIViewAppearanceSwift'
  2. Importez dans votre classe:

    import UIViewAppearanceSwift
    
    func layout() {
        UINavigationBar.appearanceWhenContainedWithin(MFMailComposeViewController.self).barStyle = .Black
        UIBarButtonItem.appearanceWhenContainedWithin(UISearchBar.self).setTitleTextAttributes([NSFontAttributeName: UIFont.systemFontOfSize(15)], forState: UIControlState.Normal)
    }
  3. Référence: https://github.com/levantAJ/UIViewAppearanceSwift

Tai Le
la source
1

Swift 4: iOS 9+

UIProgressView.appearance(whenContainedInInstancesOf: [LNPopupBar.self]).tintColor = .red
Maksim Kniazev
la source
0

Il semble que Swift (du moins à partir de Beta5) ne soit pas en mesure de le supporter pour des raisons que je ne connais pas. Peut-être que la fonctionnalité de langue requise est toujours en cours, car je ne peux que supposer qu'ils l'ont laissée en dehors de l'interface pour une bonne raison. Comme vous l'avez dit, selon la documentation, il est toujours disponible dans ObjC. Vraiment décevant.

hlfcoding
la source
Cela a fonctionné pour moi dans la bêta 5 en utilisant la méthode @spinillos.
Jason Crump
Bien, mais il appearanceWhenContainedIn:est toujours hors de la table.
hlfcoding
-3

Vous pouvez utiliser ceci:

UIBarButtonItem.appearance().setTitleTextAttributes(textDictionary, forState: UIControlState.Normal)

Edit: apparenceWhenContainedIn a été supprimé dans Swift. Cette réponse était pour la Bêta 5 de changer l'apparence du texte de tous les boutons de la barre.

spinilles
la source
1
En outre, pas besoin d'utiliser, UIControlState.Normalvous pouvez simplement utiliser .NormalSwift connaît le type par inférence.
Ben Lachman le
12
cela changera d'apparence pour tous les UIBarButtonItems, pas pour un spécifique
Kostiantyn Koval
@KonstantinKoval Exactement - c'est le but des proxys d'apparence. Vous pouvez styliser l'ensemble de votre application sans avoir à contenir des appels de code / méthode standard dans chaque contrôleur de vue.
Craig Otis
3
Cela ne répond pas vraiment à la question, dans Swift appearanceWhenContainedIna été supprimé.
Tim Windsor Brown
-7

Vous devriez pouvoir simplement traduire la Objective-Csyntaxe enSwift syntaxe.

Dans swift, les méthodes doivent être déclarées comme ceci:

func appearanceWhenContainedIn(containerClass : <UIAppearanceContainer>)
func setTitleTextAttributes(_ attributes: NSDictionary!, forState state: UIControlState)

Vous pouvez donc essayer ceci:

UIBarButtonItem.appearanceWhenContainedIn(UINavigationBar).setTitleTextAttributes(textDictionary, forState: UIControlStateNormal)

Je dois encore déterminer si c'est la manière propre d'appeler une méthode de classe dans Swift .

J'espère que cela t'aides,

Zedenem
la source
1
Il n'y a pas d'apparence de cette méthode. Erreur: «UIBarButtonItem.Type» n'a pas de membre nommé «apparitionWhenContainedIn»
AlexZd
Salut @AlexZd, votre commentaire est basé sur le XCode 6 Beta réel mais si vous regardez la UIAppearancedocumentation du protocole (à laquelle UIBarButtonItemest conforme), la méthode `apparenceWhenContainedIn (_ :) existe (elle n'est tout simplement pas encore implémentée dans Swift): developer.apple .com / library / prerelease / ios / documentation / UIKit /…
Zedenem
@Zedenem Je sais, c'est vraiment stupide et vous ne voulez pas nous croire - mais cette méthode ne peut littéralement pas être appelée depuis Swift, consultez la documentation: developer.apple.com/library/ios/documentation/UIKit/Reference/… - la méthode est cachée à Swift
powerj1984
Salut @ powerj1984, ce n'est pas que je ne veux pas te croire. J'espérais juste au moment où j'ai écrit ma réponse que cela était juste dû au statut bêta de Swift. Le fait que la méthode ne soit pas obsolète mais qu'elle soit simplement cachée à Swift est vraiment étrange, et ne pas avoir d'explication d'Apple rend les choses encore pires ... Mais vous avez raison, même si je ne comprends pas pourquoi, ma réponse est faux. Peut-être y a-t-il une fonctionnalité dans Swift à laquelle je ne pense pas qui nous permettrait de faire exactement cela ...
Zedenem
Haha, désolé - je veux vraiment dire que je ne veux pas me croire. C'est tellement stupide pour Apple de se tromper. C'est bizarre comme diable!
powerj1984