问题描述
我想在我的 .cpp 函数中发生变化或触发事件时通知 swift 代码。
我的应用程序遵循以下结构。
(.cpp)[.hpp] ->(.mm)[.h] -> .swift
我可以通过以下方式处理 swift 代码中的 changeThisstring:
let swiftString = CPPWrapper().MyMethodWrapper()
按钮单击/viewDidLoad 没问题,但每当我从 C++ 设置它时,我都想更新此值。
如果 C++ 将新字符串传递给 swift,它不应该等待按钮点击它应该像监听器一样工作。
我会很高兴为您提供任何帮助,谢谢。
示例:
my.cpp:
std::string changeThisstring = "";
...
virtual void myCallBack(MyCallBackParam ¶mter){
changeThisstring = "I WANT TO SEE THIS MESSAGE ON MY APP!"
}
std::string MyClass::MyMethod() {
return changeThisstring;
}
my.hpp:
#include <string>
class MyClass{
public:
std::string MyMethod();
};
wrapper.mm
#import "wrapper.h"
#import "my.hpp"
@implementation CPPWrapper
MyClass myClass;
- (Nsstring*) MyMethodWrapper {
Nsstring* result = [Nsstring stringWithUTF8String:myClass.MyMethod().c_str()];
return result;
}
@end
Wrapper.h
#import <Foundation/Foundation.h>
@interface CPPWrapper : NSObject
-(Nsstring*) MyMethodWrapper;
@end
.swift
let swiftString = CPPWrapper().MyMethodWrapper()
解决方法
这是触发合并通知的 C 回调示例。
(在 GitHub 上移至此处:https://github.com/moosefactory/C-callback-to-Swift)
此示例更改 C 库中 C 字符串的值并将其显示在 SwiftUI 视图的字段中。
你不需要通过 Objective-C。
-
第一部分是 C 库(很少改动以使其适用于 C++)
-
第二部分是 CSwift 类
我认为有一个对象可以在 C 代码和 swift 应用程序之间架起桥梁,从应用程序代码中删除深奥的语法,这很好。在本例中,文件MyCLibraryInterface
完成了这项工作。
这个类是一个可观察对象,它将使用 combine 发布值更改,因此它有点超出问题 - 一旦您进入回调块,您可以停在那里并做您想做的事情。请注意,我们无法在 c 调用中捕获 swift 上下文(没有调用 self 或在堆上声明的变量)
- 第三部分是一个简单的 SwiftUI 应用程序,用于接收更改和更新界面
C 库
- MyCLibrary.h
#ifndef MyCLibrary_h
#define MyCLibrary_h
#include <stdio.h>
#include <dispatch/dispatch.h>
#include <stdlib.h>
/// The callback to call when the string is changed
typedef void callback_t(const char* string);
void setCallBack(callback_t _callback);
/// A function that will change the string
void setString(const char* string);
void startTimer(void);
void cancelTimer(void);
#endif /* MyCLibrary_h */
- MyCLibrary.c
#include "MyCLibrary.h"
const char* myString;
dispatch_queue_t queue;
dispatch_source_t timer;
bool running;
callback_t* callback;
void setCallBack(callback_t _callback) {
callback = _callback;
}
void setString(const char* string) {
myString = string;
callback(myString);
}
/// A function that will start a timer that changes string
int ticks = 0;
void idle(dispatch_source_t timer)
{
ticks++;
char ticksStr[32];
sprintf(ticksStr,"Time : %ds",ticks);
setString(ticksStr);
}
void startTimer() {
if (running) { cancelTimer(); sleep(1); }
queue = dispatch_queue_create("timerQueue",0);
// Create dispatch timer source
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,queue);
dispatch_source_set_event_handler(timer,^{idle(timer);});
dispatch_source_set_cancel_handler(timer,^{
dispatch_release(timer);
dispatch_release(queue);
});
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW,0);
// Set timer
dispatch_source_set_timer(timer,start,NSEC_PER_SEC,0);
ticks = 0;
running = true;
dispatch_resume(timer);
}
void cancelTimer() {
running = false;
dispatch_source_cancel(timer);
char ticksStr[32];
sprintf(ticksStr,"Canceled after %ds",ticks);
setString(ticksStr);
}
CSwift 部分
- MyApp-Bridging-Header.h
#import "MyCLibrary.h"
- MyCLibraryInterface.swift
import Foundation
class MyCLibraryInterface: ObservableObject {
@Published var string: String = "This is a string"
static let shared = MyCLibraryInterface()
init() {
setCallBack { stringPtr in
let newString = CFStringCreateWithCString(kCFAllocatorDefault,stringPtr,kCFStringEncodingASCII) ?? "" as CFString
DispatchQueue.main.async {
MyCLibraryInterface.shared.string = newString as String
}
}
}
func setLibString(string: String) {
string.withCString { stringPointer in
setString(stringPointer)
}
}
func startLibTimer() {
startTimer()
}
func cancelLibTimer() {
cancelTimer()
}
}
SwiftUI 示例
此示例应用程序显示初始字符串和按钮。单击或点击时,会在 CLibrary 中调用 setString
函数,调用 swift 回调并在 ObservableObject
修改后更新视图
import SwiftUI
struct ContentView: View {
@ObservedObject var myCLibInterface: MyCLibraryInterface = MyCLibraryInterface.shared
var body: some View {
VStack {
Text(myCLibInterface.string).frame(width:150).padding()
Button("Reset") {
myCLibInterface.setLibString(string: "C Timer Example")
}.padding()
Button("Start Timer") {
myCLibInterface.startLibTimer()
}.padding()
Button("Cancel") {
myCLibInterface.cancelLibTimer()
}.padding()
}.padding(20)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}