将 Swift `#Observable` 与 Objective-C 模型结合使用

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

我正在尝试通过 SwiftUI 代码使用 Objective-C 模型。这是我制作的一个简单的演示:

@protocol CarProtocol <NSObject>
@property (strong, readwrite) NSString * color;
- (void)changeColor;
@end

@protocol PersonProtocol <NSObject>
@property (strong, readwrite) NSString * name;
- (void)changeName;
@end

@interface Car : NSObject <CarProtocol>

@property (strong, readwrite) NSString * color;

- (instancetype)initWithColor:(NSString *)color;
- (void)changeColor;

@end


@implementation Car

- (instancetype)init {
    if ((self = [super init])) {
        self.color = @"";
    }
    return self;
}


- (instancetype)initWithColor:(NSString *)color {
    if ((self = [super init])) {
        self.color = color;
    }
    return self;
}

- (void)changeColor {
    NSArray *colors = @[@"Red", @"Blue", @"Green", @"Yellow", @"Black", @"White"];
    NSString *newColor = self.color;
    
    while (self.color == nil || [newColor isEqualToString:self.color]) {
        newColor = colors[arc4random_uniform((uint32_t)colors.count)];
    }
    
    self.color = newColor;
    NSLog(@"The car color has been changed to %@", self.color);
}

@end

然后,我尝试通过 Objective-C 中的

Car
访问
CarProtocol
对象。我的目标是能够从 Swift(例如,从文本字段)和 ObjC 代码(例如,通过调用
Car
)修改
car.changeColor()
实例属性。

我尝试过以下方法:

import SwiftUI
import Combine

extension Car: ObservableObject {
    public var objectWillChange: AnyPublisher<Void, Never> {
        publisher(for: \.color, options: .prior)
            .map { _ in }
            .eraseToAnyPublisher()
    }
    
    public static func randomCar() -> Car {
        let colors = ["Red", "Blue", "Green", "Yellow", "Purple", "Orange"]
        return Car(color: colors.randomElement() ?? "Red")
    }
}


@Observable
class ObservableCar<CarT>
    where CarT: CarProtocol {
    public var car: CarT
    
    public init(_ car: CarT) {
        self.car = car
    }
}

struct SwiftObservableView<CarT>: View
    where CarT: CarProtocol & ObservableObject {
    @State var car: ObservableCar<CarT>
    
    public init(car: CarT) {
        self.car = ObservableCar(car)
    }
    
    var body: some View {
        NavigationStack {
            Form {
                Section(header: Text("Modification Form")) {
                    VStack {
                        TextField("Color", text: $car.car.color)
                    }
                }
                Section(header: Text("Results")) {
                    VStack {
                        HStack() {
                            Text("Color")
                            Spacer()
                            Text(car.car.color)
                        }
                    }
                }
                Section(header: Text("Actions")) {
                    Button("Random Car") {
                        self.car = ObservableCar<CarT>(Car.randomCar() as! CarT)
                    }
                    Button("Assign from Objective-C") {
                        self.car.car.changeColor()
                    }
                }
            }
            .navigationTitle("Car Data (Swift)")
        }
    }
}

#Preview {
    SwiftObservableView(car: Car(color: "Green"))
}

我可以看到快速修改的反映,但看不到对

changeColor
的调用。您知道如何使用 WWDC23 中引入的新
#Observable
宏来实现所需的行为吗?我可以通过简单的组合来实现它,无需使用协议。然而,由于我的代码是面向协议的,所以我需要依赖协议。 screenshot of the created UI

编辑:我在这里添加了使用Combine实现它的方式

import SwiftUI
import Combine

extension PersonCombine: ObservableObject {
    public var objectWillChange: AnyPublisher<Void, Never> {
        publisher(for: \.name, options: .prior)
            .map { _ in }
            .eraseToAnyPublisher()
    }
}

extension CarCombine: ObservableObject {
    public var objectWillChange: AnyPublisher<Void, Never> {
        Publishers.Merge3(
              owner.objectWillChange,
              publisher(for: \.owner, options: .prior).map { _ in },
              publisher(for: \.color, options: .prior).map { _ in }
          )
          .eraseToAnyPublisher()
    }
    
    public static func randomCar() -> CarCombine {
        let colors = ["Red", "Blue", "Green", "Yellow", "Purple", "Orange"]
        return CarCombine(color: colors.randomElement() ?? "Red")
    }
}

struct CombineView: View {
    @StateObject var car: CarCombine
    
    var body: some View {
        NavigationStack {
            Form {
                Section(header: Text("Modification Form")) {
                    VStack {
                        TextField("Color", text: $car.color)
                    }
                    VStack {
                        TextField("Owner Name", text: $car.owner.name)
                    }
                }
                Section(header: Text("Results")) {
                    VStack {
                        HStack {
                            Text("Color")
                            Spacer()
                            Text(car.color)
                        }
                    }
                    VStack {
                        HStack {
                            Text("Owner")
                            Spacer()
                            Text(car.owner.name)
                        }
                    }
                }
                Section(header: Text("Actions")) {
                    Button("Random Car") {
                        let randomCar = CarCombine.randomCar()
                        car.color = randomCar.color
                    }
                    Button("Assign Car.Color from Objective-C") {
                        car.changeColor()
                    }
                    Button("Assign Person.Name from Objective-C") {
                        car.owner.changeName()
                    }
                }
                Section(header: Text("Owner Details")) {
                    NavigationLink(destination: PersonDetailCombineView(person: car.owner)) {
                        HStack {
                            Text("Owner")
                            Spacer()
                            Text(car.owner.name)
                        }
                    }
                }
            }
            .navigationTitle("Car Data (Combine)")
        }
    }
}

#Preview {
    CombineView(car: CarCombine())
}
swift objective-c swiftui observable
1个回答
0
投票

通过扩展 Car 类以符合 ObservableObject,我们可以从 SwiftUI 和 Objective-C 更新汽车的颜色。 UI 使用组合的 objectWillChange 自动反映更改,保持一切同步。

import SwiftUI
import Combine

extension Car: ObservableObject {
    public var objectWillChange: ObservableObjectPublisher {
        let willChange = ObservableObjectPublisher()
        let originalChangeColor = self.changeColor
        self.changeColor = {
            willChange.send()
            originalChangeColor()
        }
        return willChange
    }
    
    public static func randomCar() -> Car {
        let colors = ["Red", "Blue", "Green", "Yellow", "Purple", "Orange"]
        return Car(color: colors.randomElement() ?? "Red")
    }
}

@Observable
class ObservableCar<CarT>: ObservableObject where CarT: CarProtocol {
    @Published public var car: CarT
    
    public init(_ car: CarT) {
        self.car = car
    }
}

struct SwiftObservableView<CarT>: View where CarT: CarProtocol & ObservableObject {
    @StateObject var car: ObservableCar<CarT>
    
    public init(car: CarT) {
        _car = StateObject(wrappedValue: ObservableCar(car))
    }
    
    var body: some View {
        NavigationStack {
            Form {
                Section(header: Text("Modification Form")) {
                    VStack {
                        TextField("Color", text: Binding(get: { car.car.color }, set: { car.car.color = $0 }))
                    }
                }
                Section(header: Text("Results")) {
                    VStack {
                        HStack() {
                            Text("Color")
                            Spacer()
                            Text(car.car.color)
                        }
                    }
                }
                Section(header: Text("Actions")) {
                    Button("Random Car") {
                        self.car = ObservableCar(Car.randomCar() as! CarT)
                    }
                    Button("Assign from Objective-C") {
                        self.car.car.changeColor()
                    }
                }
            }
            .navigationTitle("Car Data (Swift)")
        }
    }
}

#Preview {
    SwiftObservableView(car: Car(color: "Green"))
}
© www.soinside.com 2019 - 2024. All rights reserved.