wyglądWhenContainedIn w języku Swift

125

Próbuję przekonwertować moją aplikację na język Swift.

Mam ten wiersz kodu:

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

Jak przekonwertować go na Swift?

W dokumentach Apple nie ma takiej metody.

AlexZd
źródło
1
@LukeTheObscure sprawdź moją odpowiedź poniżej ... brzydka, ale działa.
tcd

Odpowiedzi:

230

Aktualizacja dla iOS 9:

Jeśli celujesz w system iOS 9+ (od Xcode 7 b1), w UIAppearanceprotokole jest nowa metoda , która nie korzysta z funkcji varargs:

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

Które można wykorzystać w ten sposób:

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

Jeśli nadal potrzebujesz obsługi systemu iOS 8 lub starszego, użyj następującej oryginalnej odpowiedzi na to pytanie.

W przypadku iOS 8 i 7:

Te metody nie są dostępne dla Swift, ponieważ metody Obj-C varargs nie są kompatybilne z Swift (patrz http://www.openradar.me/17302764 ).

Napisałem niezróżnicowane obejście, które działa w Swift (powtórzyłem tę samą metodę dla UIBarItem, która nie pochodzi od 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

Po prostu upewnij się, że #import "UIAppearance+Swift.h"w nagłówku mostkowania.

Następnie, aby zadzwonić z Swift (na przykład):

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

# Swift 3.x:
UITextField.my_appearanceWhenContained(in: MyViewController.self).keyboardAppearance = .light
Alex Pretzlav
źródło
15
UIBarButtonItem nie jest UIView i należy go osobno rozszerzyć
Sergey Skoblikov
7
Zauważ, że przynajmniej w iOS8, UIAppearance + Swift.h powinny importować UIKit / UIKit.h
Chase
UIBarButtonItem może być obsługiwany również, jeśli utworzysz inną metodę przeznaczoną dla UIBarButtonItem zamiast UIView. @interface UIBarButtonItem (UIViewAppearance_Swift)
Michael Peterson
1
@rptwsthi: Dodałem przykład dla Swift 3, jest bardzo nieznacznie inny
Alex Pretzlav
1
Wersja iOS 9 dla Swift 3.2 to UIBarButtonItem.appearance (whenContainedInInstancesOf: [UISearchBar.self]). Title = "Gotowe"
Eli Burke
31

ios 10 swift 3

UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).title = "Kapat"
Piotr Tomasik
źródło
1
Nie wiem, dlaczego jest to głos negatywny, ponieważ jest to sposób na zrobienie tego w iOS10 swift 3 ... Dzięki
Vassily
14

W przypadku iOS 8 i 7:

Używam kategorii opartej na odpowiedzi Alexa, aby określić wiele kontenerów. Jest to obejście, dopóki Apple oficjalnie nie będzie wspierać appearanceWhenContainedInw Swift.

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

Następnie dodaj #import "UIAppearance+Swift.h"do swojego nagłówka mostkowania.

Aby użyć z Swift :

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

Dobrze, gdybym mógł znaleźć sposób za pomocą CVarArgType , ale nie znalazłem czystego rozwiązania.

Yoichi Tagaya
źródło
Niezłe obejście zepsutego działania varargów! Naprawdę szkoda, że ​​nie ma ogólnego rozwiązania innego niż (teraz) przy użyciu metody Apple iOS 9.
Alex Pretzlav
12

Oto mniej brzydkie, ale wciąż brzydkie obejście inspirowane @tdun.

  1. Utwórz klasę, aby zachować swój wygląd Objective-C. Na potrzeby tego przykładu nazwijmy to AppearanceBridger.
  2. Dodaj tę klasę do swojego nagłówka mostkowania. Jeśli nie masz nagłówka mostkującego, utwórz go .
  3. Utwórz metodę klasy w AppearanceBridgernamed +(void)setAppearancei umieść kod wyglądu Objective-C w tej metodzie. Na przykład:


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

  1. W swoim kodzie Swift, w którym ustawiasz wygląd, zadzwoń AppearanceBridger.setAppearance()i powinieneś być gotowy!

Mam nadzieję, że to działa dobrze dla osób, które to widzą.

Baub
źródło
4

Oto brzydkie rozwiązanie obejścia, którego użyłem ....

Po prostu utwórz klasę dotykową kakao Objective-C (UIViewController), o dowolnej nazwie.

Nazwałem moje WorkaroundViewController...

Teraz w ( WorkaroundViewController.m):

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

Uruchom kod wyglądu Objective-C dla .appearanceWhenContainedIn()(oto mój przykład):

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

Następnie utwórz nagłówek mostkujący dla swojego projektu Swift, a następnie zainicjuj kontroler widoku Objective-C w swoim kodzie Swift, w ten sposób (ponownie, tylko mój przykład):

var work : WorkaroundViewController = WorkaroundViewController()

Gotowe! Daj mi znać, czy to działa dla Ciebie ... Tak jak powiedziałem, jest brzydkie, ale działa!

tcd
źródło
Jest to rozwiązanie tmp, myślę, że powinniśmy znaleźć lub poczekać na lepszy sposób :)
AlexZd
@AlexZd, oczywiście, mam nadzieję, że dołączą to szybko ... Ale na razie, jeśli tego potrzebujesz, gotowe!
tcd
4

Można to rozszerzyć na dowolną klasę zgodną z protokołem UIAppearance - nie tylko na UIViews. Oto bardziej ogólna wersja:

UIAppearance + 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.
źródło
3

Stworzyłem repozytorium dla was, którzy chcą używać CocoaPods:

  1. Dodaj to do Podfile:

    pod 'UIViewAppearanceSwift'
  2. Importuj w swojej klasie:

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

Tai Le
źródło
1

Swift 4: iOS 9+

UIProgressView.appearance(whenContainedInInstancesOf: [LNPopupBar.self]).tintColor = .red
Maksim Kniazev
źródło
0

Wygląda na to, że Swift (przynajmniej od wersji Beta5) nie jest w stanie go obsługiwać z nieznanych mi powodów. Być może wymagana funkcja języka jest nadal w toku, ponieważ mogę tylko założyć, że nie ma jej w interfejsie z dobrego powodu. Jak powiedziałeś, zgodnie z dokumentacją jest nadal dostępny w ObjC. Naprawdę rozczarowujące.

hlfcoding
źródło
To zadziałało dla mnie w Beta 5 przy użyciu metody @spinillos.
Jason Crump,
Racja, ale appearanceWhenContainedIn:nadal jest poza stołem.
hlfcoding
-3

Możesz użyć tego:

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

Edycja: wygląd WhenContainedIn został usunięty w języku Swift. Ta odpowiedź dotyczyła zmiany wyglądu tekstu wszystkich przycisków paska w wersji Beta 5.

spinillos
źródło
1
Ponadto nie ma potrzeby używania UIControlState.Normal, możesz po prostu użyć .NormalSwift zna typ przez wnioskowanie.
Ben Lachman
12
zmieni to wygląd wszystkich elementów UIBarButtonItem, a nie konkretnego
Kostiantyn Koval
@KonstantinKoval Dokładnie - taki jest cel wyglądu proxy. Możesz stylizować całą aplikację bez konieczności umieszczania standardowych wywołań kodu / metod w każdym kontrolerze widoku.
Craig Otis
3
To właściwie nie odpowiada na pytanie, w Swift appearanceWhenContainedInzostał usunięty.
Tim Windsor Brown
-7

Powinieneś być w stanie po prostu przetłumaczyć Objective-Cskładnię na Swiftskładnię.

W szybkim tempie metody powinny być zadeklarowane w następujący sposób:

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

Możesz więc spróbować tego:

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

Nadal muszę się dowiedzieć, czy jest to czysty sposób wywołania metody klasy Swift.

Mam nadzieję że to pomoże,

Zedenem
źródło
1
Nie ma tej metody pojawiania sięWhenContainedIn w swift nie jest kompilowana. Błąd: „UIBarButtonItem.Type” nie ma elementu członkowskiego o nazwie „pojawianie
sięWhenContainedIn
Cześć @AlexZd, Twój komentarz jest oparty na aktualnej wersji XCode 6 Beta, ale jeśli spojrzysz na UIAppearancedokumentację protokołu (która UIBarButtonItemjest zgodna), metoda `lookWhenContainedIn (_ :) istnieje (po prostu nie jest jeszcze zaimplementowana w Swift): developer.apple .com / library / prerelease / ios / dokumentacja / UIKit /…
Zedenem
@Zedenem Wiem, to naprawdę głupie i nie chcesz nam wierzyć - ale tej metody dosłownie nie można wywołać ze Swift, sprawdź dokumentację: developer.apple.com/library/ios/documentation/UIKit/Reference/ ... - metoda jest ukryta przed Swift
powerj1984
Cześć @ powerj1984, nie chodzi o to, że nie chcę ci wierzyć. Kiedy pisałem swoją odpowiedź, miałem tylko nadzieję, że jest to spowodowane statusem wersji beta Swift. Fakt, że metoda nie została uznana za przestarzałą, ale jest po prostu ukryta przed Swiftem, jest naprawdę dziwny, a brak wyjaśnienia od Apple sprawia, że ​​jest jeszcze gorszy ... Ale masz rację, nawet jeśli nie rozumiem dlaczego, moja odpowiedź jest źle. Może jest w Swift jakaś funkcja, o której nie myślę, która pozwoliłaby nam to zrobić ...
Zedenem
Haha, przepraszam - naprawdę to znaczy, że nie chcę mi wierzyć. To takie głupie, że Apple schrzanił sprawę. To dziwne, jak cholera!
powerj1984