【问题标题】:Updating SwiftUI view from an Objective C Class从 Objective C 类更新 SwiftUI 视图
【发布时间】:2020-06-05 22:16:07
【问题描述】:

我在 IOS 开发方面没有经验,但通过阅读文档和教程了解了一些基本知识。

我想从 Swift 调用 Objective C 代码,它工作正常,现在我想做相反的事情,有点困惑。

基本上,我首先在 SwiftUI 中的按钮操作中调用一个 Objective C 函数,然后我希望该函数在同一个 SwiftUI 视图中更新 ObservedObject 并希望视图重新呈现。

我已经找到并关注了一些资源,它们是

https://medium.com/@iainbarclay/adding-swiftui-to-objective-c-apps-63abc3b26c33

https://pinkstone.co.uk/how-to-use-swift-classes-in-objective-c/

Swift UI 视图看起来像

class Foo : ObservableObject {
    @Published var bar = ""
}

struct ContentView: View {
    @ObservedObject var baz = Foo();
    
    // Then access later as self.baz.bar as a parameter somewhere..

在此处更新bar 的正确方法是什么?

我进行了正确的构建设置并添加了@objc 标签并导入了project_name-swift.h。 实施并修改了示例 https://medium.com/@iainbarclay/adding-swiftui-to-objective-c-apps-63abc3b26c33 但由于我在这些环境中缺乏经验而迷路了。

也许有人可以把我推向正确的方向。

谢谢。

假设我的项目名称是Project

示例代码: (与此非常相似的代码,可以正常编译并调用 Objective C 函数,但在快速方面,我没有输出到控制台,文本也没有呈现。如果您在此指出我的错误,我将不胜感激,因为我很少参与 iOS 开发。)

ContentView.swift

import Foundation
import SwiftUI

var objectivec_class = Objectivec_Class()

class Foo : ObservableObject {
    @Published var bar = ""
}

@objc
class BridgingClass: NSObject {

    @ObservedObject var baz = Foo();
    @objc func updateString(_ content: NSMutableString) {
        print("This function is called from Objective C")
        self.baz.bar += content as String
    }

}

struct ContentView: View {
    /** 
     * This part seems fishy to me, 
     * It would have been better to inject the instance of Foo here in 
     * BridgingClass but, couldn't figure out how to.
     * This is only for showing my intention. 
     */
    @ObservedObject var baz = Foo();
    var body: some View {
        Button(action: {
            objectivec_class.updateSwiftUi()
        })
        {
            Text(self.baz.bar)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Objective C 桥接头,

项目-Bridging-Header.h

#import "Objectivec_Class.h"

Objectivec_Class.h

#ifndef Objectivec_Class_h
#define Objectivec_Class_h

#import <Foundation/Foundation.h>
#import "Project-Swift.h"

@interface Objectivec_Class : NSObject

    @property (strong, nonatomic) NSMutableString* stringWhichWillBeRendered;
    @property  BridgingClass *bridgingClass;

    - (id) init;

    - (void) updateSwiftUi;

@end

#endif /* Objectivec_Class_h */

Objectivec_Class.m


#import <Foundation/Foundation.h>
#import "Project-Swift.h"
#import "Objectivec_Class.h"

@implementation Objectivec_Class

    - (id)init{
        if( self = [super init] ){
            _stringWhichWillBeRendered  = [NSMutableString stringWithString:@""];
            BridgingClass *bridgingClass = [BridgingClass new];
        }
        return self;
    }

    - (void) updateSwiftUi {
        NSString *thisWillBeRendered = @"Render this string.";
        [_stringWhichWillBeRendered appendString:thisWillBeRendered];
        [[self bridgingClass] updateString:_stringWhichWillBeRendered];
    }

@end


【问题讨论】:

  • 一般来说,你应该在视图和objective-c对象中注入foo的相同实例。如果你展示你的代码,我会在上面添加一个演示修改。

标签: ios objective-c swift swiftui


【解决方案1】:

试试下面的

@objc
class BridgingClass: NSObject {

    var baz = Foo()  // here !!
...

struct ContentView: View {

    @ObservedObject var baz = objectivec_class.bridgingClass.baz    // << this !!

    var body: some View {
        Button(action: {
            objectivec_class.updateSwiftUi()
        })
        {
            Text(self.baz.bar)
        }
    }
}

Objectivec_Class.m

@implementation Objectivec_Class

    - (id)init{
        if( self = [super init] ){
            _stringWhichWillBeRendered  = [NSMutableString stringWithString:@""];
            self.bridgingClass = [BridgingClass new]; // here !!

...

【讨论】:

  • 谢谢,我现在就试试,但您是否也知道为什么print("This function is called from Objective C") 也不打电话?
  • Objective C 基本上不调用 Swift 函数,它可以编译并且没有运行时错误。实际上,这是我遇到死胡同的地方。
  • 另外,objectivec_class 没有名为 baz 的属性。
  • 非常感谢!有效!你的回答很有帮助,所以我会将其标记为已接受,但也为了让其他人更清楚,我也会回答我自己的问题。
【解决方案2】:

我想回答我自己的问题,因为我还想分享我是如何在 Asperi 的帮助下实现这一目标的。

作为一个附带问题,我不得不切换回旧版构建系统,因为我从 Xcode 中得到了循环依赖错误。这也意味着我应该有更好的方法来做这一切:)

话虽如此,并假设您已经完成了在 Swift ObjC 之间进行双向桥接的先决条件,

ContentView.swift

import Foundation
import SwiftUI

var objectivec_class = Objectivec_Class()

class Foo : ObservableObject {
    @Published var bar = ""
}

@objc
class BridgingClass: NSObject {

    @ObservedObject var sharedObj = Foo()
    @objc func updateString(_ content: NSMutableString) {
        print("This function is called from Objective C (update String)")
        sharedObj.bar += content as String
    }

}

struct ContentView: View {

    @State var stringToBeUpdated = ""

    var body: some View {
        Button(action: {
            objectivec_class!.updateSwiftUi()
            self.stringToBeUpdated = objectivec_class!.bridgingClass.sharedObj.bar
        })
        {
            Text(self.stringToBeUpdated.isEmpty ? "tap me" : self.stringToBeUpdated)
        }
        .background(Color.green)
        .frame(height: 100)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Objective C 桥接头,

项目-Bridging-Header.h

#import "Objectivec_Class.h"

Objectivec_Class.h

#ifndef Objectivec_Class_h
#define Objectivec_Class_h

#import <Foundation/Foundation.h>
// #import "Project-Swift.h"

/** Forward declaring the class and not including the "Project-Swift.h" file 
in this header is important if you are using Xcode's legacy build system */ 
@class BridgingClass;

@interface Objectivec_Class : NSObject

    @property (strong, nonatomic) NSMutableString* stringWhichWillBeRendered;
    @property  BridgingClass *bridgingClass;

    - (id) init;

    - (void) updateSwiftUi;

@end

#endif /* Objectivec_Class_h */

Objectivec_Class.m


#import <Foundation/Foundation.h>
#import "Project-Swift.h"
#import "Objectivec_Class.h"

@implementation Objectivec_Class

    - (id)init{
        if( self = [super init] ){
            _stringWhichWillBeRendered  = [NSMutableString stringWithString:@""];
            self.bridgingClass = [BridgingClass new];
        }
        return self;
    }

    - (void) updateSwiftUi {
        // Probably you did something there to update the string.
        NSString *thisWillBeRendered = @"New information appended to string";
        [_stringWhichWillBeRendered appendString:thisWillBeRendered];
        [[self bridgingClass] updateString:_stringWhichWillBeRendered];
    }

@end


欢迎任何cmets,新手在这里;)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-15
    • 2021-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多