If you're targeting iOS 9+ (as of Xcode 7 b1), there is a new method in the UIAppearance
protocol which does not use varargs:
static func appearanceWhenContainedInInstancesOfClasses(containerTypes: [AnyObject.Type]) -> Self
Which can be used like so:
UITextField.appearanceWhenContainedInInstancesOfClasses([MyViewController.self]).keyboardAppearance = .Light
If you still need to support iOS 8 or earlier, use the following original answer to this question.
These methods are not available to Swift because Obj-C varargs methods are not compatible with Swift (see http://www.openradar.me/17302764).
I wrote a non-variadic workaround which works in Swift (I repeated the same method for UIBarItem
, which doesn't descend from 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
Just be sure to #import "UIAppearance+Swift.h"
in your bridging header.
Then, to call from Swift (for example):
# Swift 2.x:
UITextField.my_appearanceWhenContainedIn(MyViewController.self).keyboardAppearance = .Light
# Swift 3.x:
UITextField.my_appearanceWhenContained(in: MyViewController.self).keyboardAppearance = .light
ios 10 swift 3
UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).title = "Kapat"
I use a category based on Alex's answer to specify multiple containers. This is a workaround until Apple officially supports appearanceWhenContainedIn
in 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
Then add #import "UIAppearance+Swift.h"
to your bridging header.
To use from Swift:
TextField.appearanceWhenContainedWithin([MyViewController.self, TableViewController.self]).keyboardAppearance = .Light
It was good if I could find a way using CVarArgType, but I found no clean solution.
Here's a less ugly, but still ugly, workaround inspired by @tdun.
AppearanceBridger
.AppearanceBridger
named +(void)setAppearance
and put the Objective-C appearance code in this method. For example:
+ (void)setAppearance {
[[UIView appearanceWhenContainedIn:[UITableViewHeaderFooterView class], nil] setBackgroundColor:[UIColor whiteColor]];
}
AppearanceBridger.setAppearance()
and you should be good to go!Hope this works well for people who see it.
Here's an ugly workaround solution I used....
Just make an Objective-C Cocoa Touch Class (UIViewController), named whatever you want.
I named mine WorkaroundViewController
...
Now in (WorkaroundViewController.m
):
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
Run the Objective-C appearance code for .appearanceWhenContainedIn()
(here's my example):
[[UITextField appearanceWhenContainedIn:[UISearchBar class], nil] setDefaultTextAttributes:@{NSFontAttributeName: [UIFont fontWithName:@"Avenir-Light" size:16.0f]}];
Then create a bridging header for your Swift project and then initialize your Objective-C ViewController in your Swift code, like this (again, just my example):
var work : WorkaroundViewController = WorkaroundViewController()
Then you're done! Let me know if it works for you... Like I said, it's ugly, but works!
This can be extended to any class that conforms to the UIAppearance protocol -- not just UIViews. So here's a more generic version:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With