apariciónCuandoContainedIn en Swift

125

Estoy tratando de convertir mi aplicación al lenguaje Swift.

Tengo esta línea de código:

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

¿Cómo convertirlo a Swift?

En los documentos de Apple , no existe tal método.

AlexZd
fuente
1
@LukeTheObscure mira mi respuesta a continuación ... feo, pero funciona.
tcd

Respuestas:

230

Actualización para iOS 9:

Si está apuntando a iOS 9+ (a partir de Xcode 7 b1), hay un nuevo método en el UIAppearanceprotocolo que no usa varargs:

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

Que se puede usar así:

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

Si aún necesita admitir iOS 8 o anterior, use la siguiente respuesta original a esta pregunta.

Para iOS 8 y 7:

Estos métodos no están disponibles para Swift porque los métodos de varargs Obj-C no son compatibles con Swift (consulte http://www.openradar.me/17302764 ).

Escribí una solución no variada que funciona en Swift (repetí el mismo método para UIBarItem, que no desciende 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

Solo asegúrese de hacerlo #import "UIAppearance+Swift.h"en su encabezado de puente.

Luego, para llamar desde Swift (por ejemplo):

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

# Swift 3.x:
UITextField.my_appearanceWhenContained(in: MyViewController.self).keyboardAppearance = .light
Alex Pretzlav
fuente
15
UIBarButtonItem no es una UIView y debe ampliarse por separado
Sergey Skoblikov
77
Tenga en cuenta que al menos en iOS8, UIAppearance + Swift.h debería importar UIKit / UIKit.h
Chase
UIBarButtonItem también se puede admitir si solo realiza otro método dirigido a UIBarButtonItem en lugar de UIView. @interface UIBarButtonItem (UIViewAppearance_Swift)
Michael Peterson
1
@rptwsthi: agregué un ejemplo para Swift 3, es muy ligeramente diferente
Alex Pretzlav, el
1
La versión de iOS 9 para Swift 3.2 es UIBarButtonItem.appearance (whenContainedInInstancesOf: [UISearchBar.self]). Title = "Done"
Eli Burke
31

ios 10 swift 3

UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).title = "Kapat"
Piotr Tomasik
fuente
1
No sé por qué esto es un voto negativo, ya que es la forma de hacerlo en iOS10 swift 3 ... Gracias
Vassily
14

Para iOS 8 y 7:

Utilizo una categoría basada en la respuesta de Alex para especificar varios contenedores. Esta es una solución alternativa hasta que Apple sea oficialmente compatible appearanceWhenContainedIncon 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

Luego agregue #import "UIAppearance+Swift.h"a su encabezado de puente.

Para usar desde Swift :

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

Fue bueno poder encontrar una forma de usar CVarArgType , pero no encontré una solución limpia.

Yoichi Tagaya
fuente
¡Buena solución a la fragilidad de los varargs! Es realmente una lástima que no haya otra solución general que no sea (ahora) usar el método solo para iOS 9 de Apple.
Alex Pretzlav
12

Aquí hay una solución menos fea, pero aún fea, inspirada en @tdun.

  1. Crea una clase para mantener tu apariencia de Objective-C. Para los propósitos de este ejemplo, llamémoslo AppearanceBridger.
  2. Agregue esta clase a su encabezado de puente. Si no tiene un encabezado de puente, cree uno .
  3. Cree un método de clase en AppearanceBridgernamed +(void)setAppearancey coloque el código de apariencia Objective-C en este método. Por ejemplo:


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

  1. En su código Swift donde establece la apariencia, llame AppearanceBridger.setAppearance()y ¡debería estar listo!

Espero que esto funcione bien para las personas que lo ven.

Baub
fuente
4

Aquí hay una solución fea que usé ...

Simplemente haga una clase Objective-C Cocoa Touch Class (UIViewController), llamada lo que quiera.

Yo llamé el mío WorkaroundViewController...

Ahora en ( WorkaroundViewController.m):

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

Ejecute el código de apariencia de Objective-C para .appearanceWhenContainedIn()(aquí está mi ejemplo):

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

Luego cree un encabezado de puente para su proyecto Swift y luego inicialice su Objective-C ViewController en su código Swift, así (de nuevo, solo mi ejemplo):

var work : WorkaroundViewController = WorkaroundViewController()

Entonces has terminado! Avísame si te funciona ... Como dije, es feo, ¡pero funciona!

tcd
fuente
Es una solución tmp, creo que deberíamos encontrar o esperar a una mejor manera :)
AlexZd
@AlexZd, absolutamente, espero que lo incluyan rápidamente ... Pero por ahora, si lo necesitas, ¡listo!
tcd
4

Esto se puede extender a cualquier clase que se ajuste al protocolo de apariencia UIA, no solo a UIViews. Así que aquí hay una versión más 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
fuente
3

He creado un repositorio para ustedes que quieran usar CocoaPods:

  1. Agregue esto a su Podfile:

    pod 'UIViewAppearanceSwift'
  2. Importa en tu clase:

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

Tai Le
fuente
1

Swift 4: iOS 9+

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

Parece que Swift (al menos a partir de Beta5) no puede soportarlo por razones desconocidas para mí. Quizás la función de idioma requerida todavía está en progreso, ya que solo puedo suponer que la dejaron fuera de la interfaz por una buena razón. Como dijiste, según los documentos todavía está disponible en ObjC. Realmente decepcionante.

hlfcoding
fuente
Esto funcionó para mí en Beta 5 usando el método @spinillos.
Jason Crump
Correcto, pero appearanceWhenContainedIn:todavía está fuera de la mesa.
hlfcoding
-3

Puedes usar esto:

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

Editar: aparienciaWhenContainedIn se eliminó en Swift. Esta respuesta fue para la Beta 5 para cambiar la apariencia del texto de todos los botones de barra.

spinillos
fuente
1
Además, no es necesario usarlo UIControlState.Normal, solo puede usar .NormalSwift conoce el tipo por inferencia.
Ben Lachman
12
esto cambiará la apariencia de todos los UIBarButtonItems, no para uno específico
Kostiantyn Koval
@KonstantinKoval Exactamente - ese es el propósito de los representantes de apariencia. Puede diseñar toda su aplicación sin necesidad de contener el código repetitivo / llamadas al método en cada controlador de vista.
Craig Otis
3
Esto en realidad no responde la pregunta, en Swift appearanceWhenContainedInse ha eliminado.
Tim Windsor Brown
-7

Debería poder traducir la Objective-Csintaxis a Swiftsintaxis.

En rápido los métodos deben declararse así:

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

Entonces puedes probar esto:

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

Todavía tengo que averiguar si esta es la forma limpia de llamar a un método de clase Swift.

Espero que esto ayude,

Zedenem
fuente
1
No existe la apariencia de este método cuando Contiene In no se compila rápidamente. Error: 'UIBarButtonItem.Type' no tiene un miembro llamado 'aparienciaWhenContainedIn'
AlexZd
Hola @AlexZd, tu comentario se basa en el XCode 6 Beta real, pero si miras la UIAppearancedocumentación del protocolo (a la que se UIBarButtonItemajusta), existe el método `aparienciaWhenContainedIn (_ :) (todavía no está implementado en Swift): developer.apple .com / library / prerelease / ios / documentation / UIKit / ...
Zedenem
@ Zedenem Lo sé, es realmente tonto y no quieres creernos, pero este método literalmente no se puede llamar desde Swift, consulta la documentación: developer.apple.com/library/ios/documentation/UIKit/Reference/… - el método está oculto de Swift
powerj1984
Hola @ powerj1984, no es que no quiera creerte. En el momento en que escribí mi respuesta, esperaba que esto se debiera al estado beta de Swift. El hecho de que el método no haya quedado en desuso, sino que esté oculto a Swift es realmente extraño, y no tener ninguna explicación de Apple lo hace aún peor ... Pero tienes razón, incluso si no entiendo por qué, mi respuesta Está Mal. Tal vez hay una característica en Swift en la que no estoy pensando que nos permita hacer precisamente eso ...
Zedenem
Jaja, lo siento, quiero decir que no quiero creerme. Es una tontería que Apple se equivoque. Es raro como diablos!
powerj1984