You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 12:47:13