I'm trying to convert my app to the Swift language.
I have this line of code:
[[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil]
setTitleTextAttributes:textDictionary
forState:UIControlStateNormal];
How to convert it to Swift?
In Apple's docs, there is no such method.
Update for iOS 9:
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.
For iOS 8 & 7:
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"
For iOS 8 & 7:
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.
- Create a class to hold your Objective-C appearance. For the purposes of this example, let's call it
AppearanceBridger
. - Add this class to your bridging header. If you don't have a bridging header, create one.
- Create a class method in
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]];
}
- In your Swift code where you set the appearance, call
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
I have created a repo for you guys who wanna use CocoaPods
:
Add this into your
Podfile
:pod 'UIViewAppearanceSwift'
Import in your class:
import UIViewAppearanceSwift func layout() { UINavigationBar.appearanceWhenContainedWithin(MFMailComposeViewController.self).barStyle = .Black UIBarButtonItem.appearanceWhenContainedWithin(UISearchBar.self).setTitleTextAttributes([NSFontAttributeName: UIFont.systemFontOfSize(15)], forState: UIControlState.Normal) }
Reference: https://github.com/levantAJ/UIViewAppearanceSwift
Swift 4: iOS 9+
UIProgressView.appearance(whenContainedInInstancesOf: [LNPopupBar.self]).tintColor = .red
It seems Swift (at least as of Beta5) isn't able to support it for reasons unknown to me. Perhaps the language feature required is still in progress, as I can only assume they left it out of the interface for a good reason. Like you said, according to the docs it's still available in ObjC. Really disappointing.
You can use this:
UIBarButtonItem.appearance().setTitleTextAttributes(textDictionary, forState: UIControlState.Normal)
Edit: appearanceWhenContainedIn was removed in Swift. This answer was for the Beta 5 to change the appearance of the text of all bar buttons.
You should be able to just translate the Objective-C
syntax into Swift
syntax.
In swift the methods should be declared like this:
func appearanceWhenContainedIn(containerClass : <UIAppearanceContainer>)
func setTitleTextAttributes(_ attributes: NSDictionary!, forState state: UIControlState)
So you can try this:
UIBarButtonItem.appearanceWhenContainedIn(UINavigationBar).setTitleTextAttributes(textDictionary, forState: UIControlStateNormal)
I still have to figure out if this is the clean way to call a class method in Swift
though.
Hope this helps,
来源:https://stackoverflow.com/questions/24136874/appearancewhencontainedin-in-swift