aparênciaWhenContainedIn em Swift

125

Estou tentando converter meu aplicativo para o idioma Swift.

Eu tenho esta linha de código:

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

Como convertê-lo em Swift?

No documentos da Apple , não existe esse método.

AlexZd
fonte
1
@LukeTheObscure confira minha resposta abaixo ... feia, mas funciona.
tcd

Respostas:

230

Atualização para iOS 9:

Se você está segmentando o iOS 9 ou superior (a partir do Xcode 7 b1), há um novo método no UIAppearanceprotocolo que não usa varargs:

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

Que pode ser usado assim:

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

Se você ainda precisar oferecer suporte ao iOS 8 ou anterior, use a seguinte resposta original para esta pergunta.

Para iOS 8 e 7:

Esses métodos não estão disponíveis para o Swift porque os métodos de varargs do Obj-C não são compatíveis com o Swift (consulte http://www.openradar.me/17302764 ).

Eu escrevi uma solução alternativa não-variável que funciona em Swift (repeti o mesmo método para UIBarItem, que não desce 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

Apenas certifique-se de #import "UIAppearance+Swift.h" no cabeçalho da ponte.

Em seguida, para ligar de Swift (por exemplo):

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

# Swift 3.x:
UITextField.my_appearanceWhenContained(in: MyViewController.self).keyboardAppearance = .light
Alex Pretzlav
fonte
15
UIBarButtonItem não é um UIView e deve ser prorrogado separadamente
Sergey Skoblikov
7
Observe que pelo menos no iOS8, UIAppearance + Swift.h deve importar o UIKit / UIKit.h
Chase
O UIBarButtonItem também pode ser suportado se você apenas criar outro método visando o UIBarButtonItem em vez do UIView. @ interface UIBarButtonItem (UIViewAppearance_Swift)
Michael Peterson
1
@rptwsthi: Adicionei um exemplo para o Swift 3, é um pouco diferente #
Alex Pretzlav #
1
A versão do iOS 9 para o Swift 3.2 é UIBarButtonItem.appearance (whenContainedInInstancesOf: [UISearchBar.self]). Title = "Concluído"
Eli Burke
31

ios 10 swift 3

UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).title = "Kapat"
Piotr Tomasik
fonte
1
Não sei por que isso é downvote uma vez que é a maneira de fazê-lo em iOS10 rápidas 3 ... Obrigado
Vassily
14

Para iOS 8 e 7:

Eu uso uma categoria com base na resposta de Alex para especificar vários contêineres. Esta é uma solução alternativa até que a Apple suporte oficialmente appearanceWhenContainedInno 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

Em seguida, adicione #import "UIAppearance+Swift.h"ao seu cabeçalho de ponte.

Para usar do Swift :

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

Foi bom encontrar uma maneira de usar o CVarArgType , mas não encontrei uma solução limpa.

Yoichi Tagaya
fonte
Solução agradável para a quebra de varargs! É realmente muito ruim que não haja outra solução geral além do (agora) usando o método apenas para iOS da Apple 9.
Alex Pretzlav
12

Aqui está uma solução menos feia, mas ainda feia, inspirada em @tdun.

  1. Crie uma classe para manter sua aparência em Objective-C. Para os propósitos deste exemplo, vamos chamá-lo AppearanceBridger.
  2. Adicione esta classe ao seu cabeçalho de ponte. Se você não tiver um cabeçalho de ponte, crie um .
  3. Crie um método de classe em AppearanceBridgernomeado +(void)setAppearancee coloque o código de aparência Objective-C nesse método. Por exemplo:


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

  1. No seu código Swift, onde você define a aparência, ligue AppearanceBridger.setAppearance()e você deve estar pronto !

Espero que isso funcione bem para as pessoas que a veem.

Baub
fonte
4

Aqui está uma solução alternativa feia que eu usei ...

Basta criar uma classe de toque cacau Objective-C (UIViewController), com o nome que você desejar.

Eu nomeei o meu WorkaroundViewController...

Agora em ( WorkaroundViewController.m):

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

Execute o código de aparência do Objective-C para .appearanceWhenContainedIn()(aqui está o meu exemplo):

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

Em seguida, crie um cabeçalho de ponte para o seu projeto Swift e, em seguida, inicialize seu Objective-C ViewController no seu código Swift, desta forma (novamente, apenas meu exemplo):

var work : WorkaroundViewController = WorkaroundViewController()

Então você está pronto! Deixe-me saber se funciona para você ... Como eu disse, é feio, mas funciona!

tcd
fonte
É tmp solução, eu acho que deveríamos encontrar ou esperar para alguma maneira melhor :)
AlexZd
@AlexZd, absolutamente, espero que incluam rapidamente ... Mas por enquanto, se você precisar, pronto!
tcd
4

Isso pode ser estendido a qualquer classe que esteja em conformidade com o protocolo UIAppearance - não apenas UIViews. Então, aqui está uma versão mais genérica:

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
fonte
3

Eu criei um repositório para vocês que querem usar CocoaPods:

  1. Adicione isso ao seu Podfile:

    pod 'UIViewAppearanceSwift'
  2. Importe na sua classe:

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

Tai Le
fonte
1

Swift 4: iOS 9 ou superior

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

Parece que o Swift (pelo menos a partir do Beta5) não é capaz de suportá-lo por razões desconhecidas para mim. Talvez o recurso de idioma necessário ainda esteja em andamento, pois posso apenas assumir que eles o deixaram de fora da interface por um bom motivo. Como você disse, de acordo com os documentos, ele ainda está disponível no ObjC. Realmente decepcionante.

hlfcoding
fonte
Isso funcionou para mim no Beta 5 usando o método @spinillos.
Jason Crump
Certo, mas appearanceWhenContainedIn:ainda está fora da mesa.
Hlfcoding 26/08/14
-3

Você pode usar isto:

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

Edit: a aparênciaWhenContainedIn foi removida no Swift. Essa resposta foi para o Beta 5 alterar a aparência do texto de todos os botões da barra.

spinillos
fonte
1
Além disso, não há necessidade de usar, UIControlState.Normalvocê pode simplesmente usar .NormalSwift conhece o tipo por inferência.
Ben Lachman
12
isso vai mudar a aparência para todos os UIBarButtonItems, não para específicas um
Kostiantyn Koval
@ KonstantinKoval Exatamente - esse é o objetivo dos proxies de aparência. É possível estilizar seu aplicativo inteiro sem precisar conter código / método de clichê em todos os controladores de exibição.
Craig Otis
3
Na verdade, isso não responde à pergunta, pois o Swift appearanceWhenContainedInfoi removido.
Tim Windsor Brown
-7

Você deve conseguir apenas traduzir a Objective-Csintaxe paraSwift sintaxe.

No swift, os métodos devem ser declarados assim:

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

Então você pode tentar isso:

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

Eu ainda tenho que descobrir se esta é a maneira limpa de chamar um método de classe Swift.

Espero que isto ajude,

Zedenem
fonte
1
Não existe esse método aparecimento; quando ContainedIn in swift, ele não é compilado. Erro: 'UIBarButtonItem.Type' não tem um membro chamado 'aparecimentoWhenContainedIn'
AlexZd
Olá @AlexZd, seu comentário é baseado no XCode 6 Beta real, mas se você olhar para a UIAppearancedocumentação do protocolo ( UIBarButtonItemconforme a qual está em conformidade), o método `appearWhenContainedIn (_ :) existe (ainda não foi implementado no Swift): developer.apple .com / library / pré-lançamento / ios / documentation / UIKit /…
Zedenem
@ Zedenem eu sei, é realmente idiota e você não quer acreditar em nós - mas esse método literalmente não pode ser chamado pela Swift, verifique a documentação: developer.apple.com/library/ios/documentation/UIKit/Reference/… - o método é escondido da Swift
powerj1984
Olá @ powerj1984, não é que eu não queira acreditar em você. Eu só esperava, no momento em que escrevi minha resposta, que isso se devia apenas ao status beta de Swift. O fato de o método não ter sido descontinuado, mas estar oculto no Swift é realmente estranho, e não ter explicação da Apple o torna ainda pior ... Mas você está certo, mesmo que eu não entenda o porquê, minha resposta está errado. Talvez há um recurso no Swift não estou pensando sobre isso nos permitiria fazer isso ...
Zedenem
Haha, desculpe - eu realmente quero dizer que não quero acreditar em mim. É uma coisa tão boba para a Apple estragar tudo. É estranho como o diabo!
powerj1984