AppearanceWhenContainIn в Swift

125

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

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

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

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

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

AlexZd
источник
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()и все должно быть в порядке!

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

Baub
источник
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()

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

TCD
источник
Это временное решение, я думаю, мы должны найти или подождать лучшего способа :)
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

Тай Ле
источник
1

Swift 4: iOS 9+

UIProgressView.appearance(whenContainedInInstancesOf: [LNPopupBar.self]).tintColor = .red
Максим Князев
источник
0

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

hlfcoding
источник
Это сработало для меня в бета-версии 5 с использованием метода @spinillos.
Джейсон Крамп
Верно, но appearanceWhenContainedIn:все еще не на столе.
hlfcoding
-3

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

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

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

spinillos
источник
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.

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

Zedenem
источник
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