appearanceWhenContainedIn in Swift

IosSwiftUiappearance

Ios Problem Overview


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.

Ios Solutions


Solution 1 - Ios

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

Solution 2 - Ios

ios 10 swift 3

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

Solution 3 - Ios

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.

Solution 4 - Ios

Here's a less ugly, but still ugly, workaround inspired by @tdun.

  1. Create a class to hold your Objective-C appearance. For the purposes of this example, let's call it AppearanceBridger.
  2. Add this class to your bridging header. If you don't have a bridging header, create one.
  3. 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]];
}
  1. 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.

Solution 5 - Ios

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!

Solution 6 - Ios

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

Solution 7 - Ios

I have created a repo for you guys who wanna use CocoaPods:

  1. Add this into your Podfile:

     pod 'UIViewAppearanceSwift'
    
  2. 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)
     }
    
  3. Reference: https://github.com/levantAJ/UIViewAppearanceSwift

Solution 8 - Ios

Swift 4: iOS 9+

UIProgressView.appearance(whenContainedInInstancesOf: [LNPopupBar.self]).tintColor = .red

Solution 9 - Ios

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.

Solution 10 - Ios

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.

Solution 11 - Ios

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,

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlexZdView Question on Stackoverflow
Solution 1 - IosAlex PretzlavView Answer on Stackoverflow
Solution 2 - Iosuser5681291View Answer on Stackoverflow
Solution 3 - IosYoichi TagayaView Answer on Stackoverflow
Solution 4 - IosBaubView Answer on Stackoverflow
Solution 5 - IostcdView Answer on Stackoverflow
Solution 6 - IosEddie KView Answer on Stackoverflow
Solution 7 - IosTai LeView Answer on Stackoverflow
Solution 8 - IosYaroslav DukalView Answer on Stackoverflow
Solution 9 - IoshlfcodingView Answer on Stackoverflow
Solution 10 - IosspinillosView Answer on Stackoverflow
Solution 11 - IosZedenemView Answer on Stackoverflow