AppearanceWhenContainIn в Swift


125

Я пытаюсь перевести свое приложение на язык Swift.

У меня есть такая строка кода:

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

Как преобразовать его в Swift?

В документации Apple такого метода нет.


1
@LukeTheObscure, посмотрите мой ответ ниже ... уродливо, но работает.
— tcd

Ответы:


230

Обновление для iOS 9:

Если вы ориентируетесь на iOS 9+ (начиная с Xcode 7 b1), в UIAppearanceпротоколе есть новый метод , который не использует varargs:

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

Что можно использовать так:

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

Если вам по-прежнему требуется поддержка iOS 8 или более ранней версии, используйте следующий исходный ответ на этот вопрос.

Для iOS 8 и 7:

Эти методы недоступны для Swift, поскольку методы varargs Obj-C несовместимы со Swift (см. Http://www.openradar.me/17302764 ).

Я написал обходной путь без вариации, который работает в Swift (я повторил тот же метод UIBarItem, который не происходит от 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

Обязательно #import "UIAppearance+Swift.h"укажите это в заголовке моста.

Затем, чтобы позвонить из Swift (например):

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

# Swift 3.x:
UITextField.my_appearanceWhenContained(in: MyViewController.self).keyboardAppearance = .light

15
UIBarButtonItem не является UIView и требует отдельного расширения
— Сергей Скобликов

7
Обратите внимание, что, по крайней мере, в iOS8 UIAppearance + Swift.h должен импортировать UIKit / UIKit.h
— Чейз

UIBarButtonItem может поддерживаться также, если вы просто создадите другой метод, ориентированный на UIBarButtonItem вместо UIView. @interface UIBarButtonItem (UIViewAppearance_Swift)
— Майкл Петерсон

1
@rptwsthi: Я добавил пример для Swift 3, он немного отличается
— Alex Pretzlav

1
Версия iOS 9 для Swift 3.2 - это UIBarButtonItem.appearance (whenContainInstancesOf: [UISearchBar.self]). Title = "Done"
— Эли Берк

31

ios 10 swift 3

UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).title = "Kapat"

1
Не знаю, почему это отрицательное голосование, ведь это способ сделать это в iOS10 swift 3 ... Спасибо
— Василий

14

Для iOS 8 и 7:

Я использую категорию, основанную на ответе Алекса, чтобы указать несколько контейнеров. Это обходной путь до тех пор, пока Apple официально не поддержит appearanceWhenContainedInSwift.

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

Затем добавьте #import "UIAppearance+Swift.h"в свой заголовок моста.

Для использования из Swift :

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

Было бы хорошо, если бы я смог найти способ использовать CVarArgType , но я не нашел чистого решения.


Хороший способ обхода поломки varargs! Очень жаль, что нет общего решения, кроме (сейчас) использования метода Apple только для iOS 9.
— Alex Pretzlav

12

Вот менее уродливый, но все же уродливый обходной путь, вдохновленный @tdun.

  1. Создайте класс, чтобы сохранить вашу внешность Objective-C. Для целей этого примера назовем его AppearanceBridger.
  2. Добавьте этот класс в свой заголовок моста. Если у вас нет связующего заголовка, создайте его .
  3. Создайте метод класса в AppearanceBridgernamed +(void)setAppearanceи поместите в этот метод код появления Objective-C. Например:


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

  1. В вашем коде Swift, где вы устанавливаете внешний вид, звоните, AppearanceBridger.setAppearance()и все должно быть в порядке!

Надеюсь, это сработает для людей, которые это видят.


4

Вот уродливое решение, которое я использовал ...

Просто создайте сенсорный класс какао Objective-C (UIViewController) с любым именем.

Я назвал свое WorkaroundViewController...

Сейчас в ( WorkaroundViewController.m):

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

Запустите код появления Objective-C для .appearanceWhenContainedIn()(вот мой пример):

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

Затем создайте заголовок моста для вашего проекта Swift, а затем инициализируйте свой Objective-C ViewController в своем коде Swift, как это (опять же, только мой пример):

var work : WorkaroundViewController = WorkaroundViewController()

Тогда все готово! Дайте мне знать, если это сработает для вас ... Как я уже сказал, это некрасиво, но работает!


Это временное решение, я думаю, мы должны найти или подождать лучшего способа :)
— AlexZd

@AlexZd, конечно, я надеюсь, что они включат его в кратчайшие сроки ... Но пока, если вам это нужно, вот и все!
— tcd

4

Это может быть распространено на любой класс, соответствующий протоколу UIAppearance, а не только на UIViews. Итак, вот более общая версия:

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

3

Я создал репо для вас, ребята, которые хотят использовать CocoaPods:

  1. Добавьте это в свой Podfile:

    pod 'UIViewAppearanceSwift'
  2. Импортируйте в свой класс:

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



0

Кажется, Swift (по крайней мере, с Beta5) не может его поддерживать по неизвестным мне причинам. Возможно, необходимая языковая функция все еще находится в разработке, поскольку я могу только предположить, что они не включили ее в интерфейс по уважительной причине. Как вы сказали, согласно документации, он все еще доступен в ObjC. Действительно разочаровывает.


Это сработало для меня в бета-версии 5 с использованием метода @spinillos.
— Джейсон Крамп

Верно, но appearanceWhenContainedIn:все еще не на столе.
— hlfcoding

-3

Вы можете использовать это:

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

Изменить: внешний видWhenContainIn был удален в Swift. Этот ответ был для Beta 5, чтобы изменить внешний вид текста всех кнопок панели.


1
Кроме того, нет необходимости использовать, UIControlState.Normalвы можете просто использовать .NormalSwift, который знает тип по выводу.
— Бен Лахман

12
это изменит внешний вид для всех UIBarButtonItems, а не для конкретного
— Константин Коваль

@KonstantinKoval Точно - в этом и предназначение появления прокси. Вы можете стилизовать все свое приложение без необходимости содержать шаблонный код / ​​вызовы методов в каждом контроллере представления.
— Крейг Отис,

3
Это на самом деле не отвечает на вопрос, в Swift appearanceWhenContainedInбыл удален.
— Тим Виндзор Браун

-7

Вы сможете просто перевести Objective-Cсинтаксис в Swiftсинтаксис.

В быстром темпе методы должны быть объявлены так:

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

Итак, вы можете попробовать это:

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

Мне все еще нужно выяснить, является ли это чистым способом вызова метода класса Swift.

Надеюсь это поможет,


1
Нет этого метода appearanceWhenContainIn в быстром, он не компилируется. Ошибка: «UIBarButtonItem.Type» не имеет члена с именем «appearanceWhenContainIn»
— AlexZd

Привет @AlexZd, ваш комментарий основан на фактической бета-версии XCode 6, но если вы посмотрите UIAppearanceдокументацию протокола (которому UIBarButtonItemсоответствует), существует метод `appearanceWhenContainIn (_ :) (он еще не реализован в Swift): developer.apple .com / library / prerelease / ios / documentation / UIKit /…
— Zedenem

@Zedenem Я знаю, это действительно глупо, и вы не хотите нам верить, но этот метод буквально нельзя вызвать из Swift, проверьте документацию: developer.apple.com/library/ios/documentation/UIKit/Reference/… - метод скрыт от Swift
— powerj1984

Привет @ powerj1984, дело не в том, что я не хочу тебе верить. В то время, когда я писал свой ответ, я просто надеялся, что это связано только с бета-статусом Swift. Тот факт, что метод не устарел, а просто скрыт от Swift, действительно странный, а отсутствие объяснений со стороны Apple делает его еще хуже ... Но вы правы, даже если я не понимаю, почему, мой ответ неправильно. Может быть, в Swift есть функция, о которой я не думаю, которая позволила бы нам сделать именно это ...
— Zedenem

Ха-ха, извини - я действительно хочу сказать, что не хочу мне верить. Для Apple это такая глупость - облажаться. Это чертовски странно!
— powerj1984
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.