问题
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.
回答1:
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
回答2:
ios 10 swift 3
UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).title = "Kapat"
回答3:
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.
回答4:
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.
回答5:
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!
回答6:
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
回答7:
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
回答8:
Swift 4: iOS 9+
UIProgressView.appearance(whenContainedInInstancesOf: [LNPopupBar.self]).tintColor = .red
回答9:
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.
回答10:
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.
回答11:
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