Objective-C中实现自定义UIKit Trait:解决UIMutableTraits扩展难题
在Objective-C中实现UIKit自定义Trait(iOS 17)
WWDC 2023的《Unleash the UIKit trait system》视频介绍了iOS 17新增的自定义Trait Collection相关API,但官方示例均为Swift实现。在Objective-C项目中转换代码时,遇到了无法给UIMutableTraits协议添加读写属性的问题——Objective-C不支持为协议添加带setter的分类扩展属性。
原Swift示例代码
enum MyAppTheme: Int { case standard, pastel, bold, monochrome } struct MyAppThemeTrait: UITraitDefinition { static let defaultValue = MyAppTheme.standard static let affectsColorAppearance = true static let name = "Theme" static let identifier = "com.myapp.theme" } extension UITraitCollection { var myAppTheme: MyAppTheme { self[MyAppThemeTrait.self] } } extension UIMutableTraits { var myAppTheme: MyAppTheme { get { self[MyAppThemeTrait.self] } set { self[MyAppThemeTrait.self] = newValue } } }
已完成的Objective-C代码
MyTraits.h
@import UIKit; NS_ASSUME_NONNULL_BEGIN typedef enum : NSUInteger { MyAppThemeStandard, MyAppThemePastel, MyAppThemeBold, MyAppThemeMonochrome } MyAppTheme; @interface MyAppThemeTrait : NSObject<UINSIntegerTraitDefinition> @end @interface UITraitCollection (MyTraits) @property (nonatomic, readonly) MyAppTheme myAppTheme; @end NS_ASSUME_NONNULL_END
MyTraits.m
#import "MyTraits.h" @implementation MyAppThemeTrait + (NSInteger)defaultValue { return MyAppThemeStandard; } + (BOOL)affectsColorAppearance { return YES; } + (NSString *)name { return @"Theme"; } + (NSString *)identifier { return @"com.myapp.theme"; } @end @implementation UITraitCollection (MyTraits) - (MyAppTheme)myAppTheme { return [self valueForNSIntegerTrait:MyAppThemeTrait.class]; } @end
核心问题
Objective-C不支持通过分类为UIMutableTraits协议添加读写属性,如何实现自定义Trait的修改功能?
解决方案
由于Objective-C的语言限制,无法直接给协议添加带setter的分类属性,可通过以下两种方式实现可变Trait的修改:
方法1:给遵循UIMutableTraits的具体类添加分类方法
UIMutableTraits协议主要被UITraitCollection的可变实例(通过mutableCopy获取)、UIViewController、UIView等类遵循。我们可以给这些常用类添加分类,提供设置自定义Trait的方法:
在MyTraits.h中添加声明:
// 给UITraitCollection(可变实例)添加设置方法 @interface UITraitCollection (MyMutableTraits) - (void)setMyAppTheme:(MyAppTheme)theme; @end // 给UIViewController添加设置方法 @interface UIViewController (MyMutableTraits) - (void)setMyAppTheme:(MyAppTheme)theme; @end // 给UIView添加设置方法 @interface UIView (MyMutableTraits) - (void)setMyAppTheme:(MyAppTheme)theme; @end
在MyTraits.m中实现:
@implementation UITraitCollection (MyMutableTraits) - (void)setMyAppTheme:(MyAppTheme)theme { if ([self conformsToProtocol:@protocol(UIMutableTraits)]) { id<UIMutableTraits> mutableSelf = (id<UIMutableTraits>)self; [mutableSelf setValue:@(theme) forNSIntegerTrait:MyAppThemeTrait.class]; } } @end @implementation UIViewController (MyMutableTraits) - (void)setMyAppTheme:(MyAppTheme)theme { [self setValue:@(theme) forNSIntegerTrait:MyAppThemeTrait.class]; } @end @implementation UIView (MyMutableTraits) - (void)setMyAppTheme:(MyAppTheme)theme { [self setValue:@(theme) forNSIntegerTrait:MyAppThemeTrait.class]; } @end
使用示例:
// 修改当前ViewController的Trait [self setMyAppTheme:MyAppThemePastel]; // 修改可变UITraitCollection实例 UITraitCollection *mutableTraits = [[UITraitCollection currentTraitCollection] mutableCopy]; [mutableTraits setMyAppTheme:MyAppThemeBold];
方法2:封装全局工具类
如果不想给多个类添加分类,可以封装一个工具类来处理任意遵循UIMutableTraits的对象:
在MyTraits.h中添加工具类声明:
@interface MyTraitUtils : NSObject + (void)setMyAppTheme:(MyAppTheme)theme forMutableTraits:(id<UIMutableTraits>)traits; @end
在MyTraits.m中实现:
@implementation MyTraitUtils + (void)setMyAppTheme:(MyAppTheme)theme forMutableTraits:(id<UIMutableTraits>)traits { [traits setValue:@(theme) forNSIntegerTrait:MyAppThemeTrait.class]; } @end
使用示例:
// 修改ViewController的Trait [MyTraitUtils setMyAppTheme:MyAppThemePastel forMutableTraits:self]; // 修改可变UITraitCollection实例 id<UIMutableTraits> mutableTraits = [[UITraitCollection currentTraitCollection] mutableCopy]; [MyTraitUtils setMyAppTheme:MyAppThemeBold forMutableTraits:mutableTraits];
补充说明
- 对于
UINSIntegerTraitDefinition类型的自定义Trait,使用setValue:forNSIntegerTrait:方法完成赋值,对应Swift中的直接赋值操作。 - 可以在设置方法内添加
conformsToProtocol判断,避免给不遵循UIMutableTraits的对象调用方法导致运行时错误。
内容的提问来源于stack exchange,提问作者HangarRash
相关产品推荐
相关产品推荐

