AussehenWenn in Swift enthalten

125

Ich versuche meine App in die Swift-Sprache zu konvertieren.

Ich habe diese Codezeile:

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

Wie konvertiere ich es in Swift?

In Apples Dokumenten gibt es keine solche Methode.

AlexZd
quelle
1
@LukeTheObscure schau dir meine Antwort unten an ... hässlich, aber funktioniert.
tcd

Antworten:

230

Update für iOS 9:

Wenn Sie auf iOS 9+ (ab Xcode 7 b1) abzielen, gibt es eine neue Methode im UIAppearanceProtokoll, die keine varargs verwendet:

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

Welches kann so verwendet werden:

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

Wenn Sie iOS 8 oder früher noch unterstützen müssen, verwenden Sie die folgende Originalantwort auf diese Frage.

Für iOS 8 & 7:

Diese Methoden stehen Swift nicht zur Verfügung, da Obj-C-Varargs-Methoden nicht mit Swift kompatibel sind (siehe http://www.openradar.me/17302764 ).

Ich habe eine nicht-variadische Problemumgehung geschrieben, die in Swift funktioniert (ich habe dieselbe Methode für wiederholt UIBarItem, von der nicht abstammt 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

Stellen Sie einfach sicher, dass Sie #import "UIAppearance+Swift.h"in Ihrem Bridging-Header.

Um dann von Swift aus anzurufen (zum Beispiel):

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

# Swift 3.x:
UITextField.my_appearanceWhenContained(in: MyViewController.self).keyboardAppearance = .light
Alex Pretzlav
quelle
15
UIBarButtonItem ist kein UIView und muss separat erweitert werden
Sergey Skoblikov
7
Beachten Sie, dass zumindest in iOS8 UIAppearance + Swift.h UIKit / UIKit.h
Chase
UIBarButtonItem kann auch unterstützt werden, wenn Sie nur eine andere Methode für UIBarButtonItem anstelle von UIView erstellen. @interface UIBarButtonItem (UIViewAppearance_Swift)
Michael Peterson
1
@rptwsthi: Ich habe ein Beispiel für Swift 3 hinzugefügt, es ist etwas anders
Alex Pretzlav
1
Die iOS 9-Version für Swift 3.2 ist UIBarButtonItem.appearance (whenContainedInInstancesOf: [UISearchBar.self]). Title = "Done"
Eli Burke
31

ios 10 swift 3

UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).title = "Kapat"
Piotr Tomasik
quelle
1
Ich weiß nicht, warum dies eine Abwertung ist, da dies in iOS10 Swift 3 der Fall ist ... Danke
Vassily
14

Für iOS 8 & 7:

Ich verwende eine Kategorie basierend auf Alex 'Antwort, um mehrere Container anzugeben. Dies ist eine Problemumgehung, bis Apple appearanceWhenContainedInSwift offiziell unterstützt .

UIAPerance + Swift.h

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

UIAPerance + 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

Fügen Sie #import "UIAppearance+Swift.h"dann Ihrem Bridging-Header hinzu.

Verwendung von Swift :

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

Es war gut, wenn ich mit CVarArgType einen Weg finden konnte , aber ich fand keine saubere Lösung.

Yoichi Tagaya
quelle
Schöne Problemumgehung für die Brüche von Varargs! Es ist wirklich schade, dass es keine andere allgemeine Lösung gibt als (jetzt) ​​die Verwendung der Nur-iOS 9-Methode von Apple.
Alex Pretzlav
12

Hier ist eine weniger hässliche, aber immer noch hässliche Problemumgehung, die von @tdun inspiriert wurde.

  1. Erstellen Sie eine Klasse für Ihr Objective-C-Erscheinungsbild. Nennen wir es für die Zwecke dieses Beispiels AppearanceBridger.
  2. Fügen Sie diese Klasse Ihrem Bridging-Header hinzu. Wenn Sie keinen Bridging-Header haben, erstellen Sie einen .
  3. Erstellen Sie eine Klassenmethode in AppearanceBridgernamed +(void)setAppearanceund fügen Sie den Objective-C-Erscheinungscode in diese Methode ein. Beispielsweise:


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

  1. Rufen AppearanceBridger.setAppearance()Sie in Ihrem Swift-Code, in dem Sie das Erscheinungsbild festlegen, an und Sie sollten bereit sein!

Hoffe, das funktioniert gut für Leute, die es sehen.

Baub
quelle
4

Hier ist eine hässliche Problemumgehungslösung, die ich verwendet habe ...

Erstellen Sie einfach eine Objective-C Cocoa Touch-Klasse (UIViewController) mit einem beliebigen Namen.

Ich habe meine benannt WorkaroundViewController...

Jetzt in ( WorkaroundViewController.m):

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

Führen Sie den Objective-C-Erscheinungscode für aus .appearanceWhenContainedIn()(hier ist mein Beispiel):

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

Dann erstellen Sie eine Überbrückung Header für Ihr Swift Projekt und dann initialisieren Ihren Objective-C Viewcontroller in Ihrem Swift Code wie folgt (auch hier nur mein Beispiel):

var work : WorkaroundViewController = WorkaroundViewController()

Dann bist du fertig! Lassen Sie mich wissen, ob es für Sie funktioniert ... Wie ich schon sagte, es ist hässlich, aber funktioniert!

tcd
quelle
Es ist tmp Lösung, ich denke wir sollten einen besseren Weg finden oder warten :)
AlexZd
@AlexZd, absolut, ich hoffe, sie schließen es schnell ein ... Aber jetzt, wenn Sie es brauchen, los geht's!
tcd
4

Dies kann auf jede Klasse erweitert werden, die dem UIAppearance-Protokoll entspricht - nicht nur auf UIViews. Hier ist eine allgemeinere Version:

UIAPerance + Swift.h

#import <UIKit/UIKit.h>

@interface NSObject (UIAppearance_Swift)

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

@end

UIAPerance + 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.
quelle
3

Ich habe ein Repo für euch erstellt, die Folgendes verwenden möchten CocoaPods:

  1. Fügen Sie dies in Ihre Podfile:

    pod 'UIViewAppearanceSwift'
  2. Import in deine Klasse:

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

Tai Le
quelle
1

Swift 4: iOS 9+

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

Es scheint, dass Swift (zumindest ab Beta5) es aus mir unbekannten Gründen nicht unterstützen kann. Vielleicht ist die erforderliche Sprachfunktion noch in Bearbeitung, da ich nur davon ausgehen kann, dass sie aus gutem Grund aus der Benutzeroberfläche herausgelassen wurde. Wie Sie sagten, ist es laut den Dokumenten immer noch in ObjC verfügbar. Wirklich enttäuschend.

hlfcoding
quelle
Dies funktionierte bei mir in Beta 5 mit der @ spinillos-Methode.
Jason Crump
Richtig, ist aber appearanceWhenContainedIn:immer noch vom Tisch.
hlfcoding
-3

Sie können dies verwenden:

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

Bearbeiten: AussehenWennContainedIn in Swift entfernt wurde. Diese Antwort war für die Beta 5, um das Erscheinungsbild des Textes aller Balkenschaltflächen zu ändern.

Spinillos
quelle
1
Außerdem müssen UIControlState.NormalSie nicht einfach .NormalSwift verwenden , sondern kennen den Typ durch Inferenz.
Ben Lachman
12
Dies wird das Erscheinungsbild für alle UIBarButtonItems ändern, nicht für bestimmte
Kostiantyn Koval
@KonstantinKoval Genau - das ist der Zweck der Erscheinungsproxies. Sie können Ihre gesamte Anwendung formatieren, ohne in jedem View-Controller Code- / Methodenaufrufe für Boilerplates enthalten zu müssen.
Craig Otis
3
Dies beantwortet die Frage nicht wirklich, in Swift appearanceWhenContainedInwurde entfernt.
Tim Windsor Brown
-7

Sie sollten in der Lage sein, die Objective-CSyntax einfach in SwiftSyntax zu übersetzen.

In Kürze sollten die Methoden wie folgt deklariert werden:

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

Sie können dies also versuchen:

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

Ich muss noch herausfinden, ob dies der saubere Weg ist, eine Klassenmethode aufzurufen Swift.

Hoffe das hilft,

Zedenem
quelle
1
Es gibt kein Erscheinungsbild dieser MethodeWhenContainedIn in Swift wird es nicht kompiliert. Fehler: 'UIBarButtonItem.Type' hat kein Mitglied mit dem Namen 'ErscheinungsbildWhenContainedIn'
AlexZd
Hallo @AlexZd, Ihr Kommentar basiert auf der tatsächlichen XCode 6 Beta, aber wenn Sie sich die UIAppearanceProtokolldokumentation ansehen (die UIBarButtonItementspricht), existiert die Methode `ErscheinungsbildWhenContainedIn (_ :) (sie ist nur noch nicht in Swift implementiert): developer.apple .com / Bibliothek / Vorabversion / ios / Dokumentation / UIKit /…
Zedenem
@Zedenem Ich weiß, es ist wirklich dumm und Sie wollen uns nicht glauben - aber diese Methode kann buchstäblich nicht von Swift aus aufgerufen werden. Überprüfen Sie die Dokumentation: developer.apple.com/library/ios/documentation/UIKit/Reference/… - Die Methode ist vor Swift verborgen
powerj1984
Hi @ powerj1984, es ist nicht so, dass ich dir nicht glauben will. Ich hatte gerade gehofft, als ich meine Antwort schrieb, dass dies nur auf Swifts Beta-Status zurückzuführen ist. Die Tatsache, dass die Methode nicht veraltet ist, sondern nur vor Swift verborgen ist, ist wirklich seltsam, und keine Erklärung von Apple macht es noch schlimmer ... Aber Sie haben Recht, auch wenn ich nicht verstehe, warum, meine Antwort ist falsch. Vielleicht gibt es eine Funktion in Swift, über die ich nicht nachdenke, die es uns ermöglichen würde, genau das zu tun ...
Zedenem
Haha, sorry - ich meine wirklich, ich will mir nicht glauben. Es ist einfach so dumm für Apple, es zu vermasseln. Es ist verdammt komisch!
powerj1984