我正在尝试通过 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
宏来实现所需的行为吗?我可以通过简单的组合来实现它,无需使用协议。然而,由于我的代码是面向协议的,所以我需要依赖协议。
编辑:我在这里添加了使用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())
}
通过扩展 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"))
}