如何在 Objective-C 中实现自定义特征?

问题描述 投票:0回答:1

WWDC 2023 视频释放 UIKit 特征系统讨论了 iOS 17 中添加的与特征集合中的自定义特征相关的新 UIKit API。该视频及其相关代码均使用 Swift 编写,但我希望在较旧的 Objective-C 应用程序中使用这些 API。我在将示例代码从 Swift 转换为 Objective-C 时遇到了一些问题。

首先,这里是视频的“代码”选项卡(时间戳 11:00)上提供的一些示例 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

我的特征.m

#import "Traits.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

这里的难点是如何在

UIMutableTraits
协议上实现扩展。 Objective-C 不支持通过扩展/类别向协议添加计算属性。当 Objective-C 语言不支持所需内容时,如何完成自定义特征的实现?

swift objective-c uikit uitraitcollection
1个回答
0
投票

事实证明,您不需要

UIMutableTraits
上的计算属性即可实现此功能。示例 Swift 代码中添加的属性只是为了方便以后使用。在 Objective-C 中,你仍然可以让它工作,只是最终的代码稍微麻烦一些。

例如,在 Swift 代码中,您可以使用以下代码创建具有示例自定义特征的

UITraitCollection

let myTraits = UITraitCollection { mutableTraits in
    mutableTraits.myAppTheme = .pastel // <-- custom trait here
    mutableTraits.horizontalSizeClass = .regular
}

在 Objective-C 中,代码如下所示:

UITraitCollection *myTraits = [UITraitCollection traitCollectionWithTraits:^(id<UIMutableTraits>  _Nonnull mutableTraits) {
    [mutableTraits setNSIntegerValue:MyAppThemePastel forTrait:MyAppThemeTrait.class];
    mutableTraits.horizontalSizeClass = UIUserInterfaceSizeClassRegular;
}];

由于

myAppTheme
上没有名为
UIMutableTraits
的便利属性,我们需要使用更详细的语法来调用
setNSIntegerValue:forTrait:
(或根据给定特征的需要使用
setObjectValue:forTrait:
setCGFloatValue:forTrait:
)。

但是,

myAppTheme
上添加了属性
UITraitCollection
,因此我们现在可以读取该值,如下所示:

MyAppTheme theme = myTraits.myAppTheme;

要查看 Objective-C 中完整工作的示例,请创建一个新的 iOS 应用程序项目。选择 Storyboard 作为用户界面,选择 Objective-C 作为语言。

添加 MyTraits.h 和 MyTraits.m,如上面的问题所示。

要使用主题,我们需要添加自定义动态颜色。将以下内容添加到 MyTraits.h

@interface UIColor (MyTraits)

@property (nonatomic, readonly, class) UIColor *customBackgroundColor;

@end

将以下内容添加到 MyTraits.m

@implementation UIColor (MyTraits)

+ (UIColor *)customBackgroundColor {
    return [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {
        // This example uses some randomly chosen colors. Pick your own to suit your needs
        switch (traitCollection.myAppTheme) {
            case MyAppThemeStandard:
                return [UIColor colorWithRed:0 green:1 blue:0 alpha:1];
            case MyAppThemePastel:
                return [UIColor colorWithRed:0.39 green:0.58 blue:0.93 alpha:1];
            case MyAppThemeBold:
                return [UIColor colorWithRed:1 green:0 blue:1 alpha:1];
            case MyAppThemeMonochrome:
                return [UIColor colorWithRed:0.3 green:0.3 blue:0.3 alpha:1];
            default: // make the compiler happy
                return UIColor.systemBackgroundColor; // This shouldn't happen
        }
    }];
}

@end

ViewController.m中,将以下代码添加到

viewDidLoad
。这将创建一个使用随机主题覆盖
myAppTheme
特征的按钮。

// Let's see when the theme changes
[self registerForTraitChanges:@[ MyAppThemeTrait.class ] withHandler:^(__kindof id<UITraitEnvironment>  _Nonnull traitEnvironment, UITraitCollection * _Nonnull previousCollection) {
    NSLog(@"VC change myAppTheme: %ld", traitEnvironment.traitCollection.myAppTheme);
}];

// Set the background to the custom theme color
self.view.backgroundColor = UIColor.customBackgroundColor;

// Setup a button to randomly change the theme
__weak typeof(self) weakSelf = self;
UIButtonConfiguration *cfg = [UIButtonConfiguration grayButtonConfiguration];
cfg.title = @"Change";
UIButton *btn = [UIButton buttonWithConfiguration:cfg primaryAction:[UIAction actionWithHandler:^(__kindof UIAction * _Nonnull action) {
    NSInteger theme = arc4random() % 4; // Pick a random theme
    // Set the new theme
    [weakSelf.traitOverrides setNSIntegerValue:theme forTrait:MyAppThemeTrait.class];
    // If we could add the convenience property to UIMutableTrait then this line would simply be:
    // weakSelf.traitOverrides.myAppTheme = theme;
}]];
btn.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:btn];

// Put the button in the center of the screen
[NSLayoutConstraint activateConstraints:@[
    [btn.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
    [btn.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
]];

构建并运行应用程序。每次点击按钮时,视图控制器的背景颜色都会根据选择的随机主题而改变。您还将在控制台中看到一条日志消息,显示已选择的主题。

© www.soinside.com 2019 - 2024. All rights reserved.