我们可以从一个 NativeScript 项目开始,或者在 Xcode 中创建一个 SwiftUI 文件。因为我们随时可以选择,所以我们首先使用 Xcode 来演示 NativeScript 如何使技能变得可互换(以及受到赞赏)。
以下方法适用于您喜欢的任何 JavaScript 框架(Angular、React、Solid、Svelte、Vue 等)。这里我们将只使用纯 TypeScript(无框架)。
您可以参考这个 GitHub 仓库获取完整示例。
要在 Xcode 中创建复选框视图,请按照以下步骤操作
对于复选框视图,我们需要一个数据模型来包含复选框的状态。状态是一个boolean
值,指示复选框是否被选中。
在 Xcode 为我们创建的默认 .swift 类中,我们可以创建一个名为CheckboxData
的数据模型类来保存复选框状态和按钮点击回调方法。为了通知视图数据模型中的更改,我们使数据模型类符合ObservableObject协议。
class CheckboxData: ObservableObject {
}
添加一个 checked 属性来保存复选框的状态。checked
属性是一个boolean
值,指示复选框是否被选中。
class CheckboxData: ObservableObject {
var checked: Bool = false
}
为了将checked
属性中的更改发布到视图,我们需要将checked
属性包装在@Published
属性包装器中。
class CheckboxData: ObservableObject {
@Published var checked: Bool = false
}
我们需要声明一个属性来保存点击回调,当按钮被点击时,该回调会切换checked
属性。我们将属性声明为一个函数类型,该函数不接受参数也不返回值。
class CheckboxData: ObservableObject {
@Published var checked: Bool = false
var toggleChecked: (() -> Void)?
}
我们需要将该属性声明为可选(?
)函数类型,因为在声明时我们没有点击回调。稍后,当我们创建复选框视图时,我们将定义并分配回调。我们将使用点击回调将checked
属性值发送到 NativeScript。
要创建 SwiftUI 复选框视图,请按照以下步骤操作
struct
的复选框视图块中,在body
属性之前,声明一个属性(在本例中为data
)来保存数据模型的实例。struct Checkbox: View {
var data: CheckboxData
var body: some View {
Text("Hello, World!")
}
}
为了使复选框视图在数据模型发生更改时接收更改,我们将视图data
属性包装在@ObservedObject
属性包装器中。
struct Checkbox: View {
@ObservedObject var data: CheckboxData
var body: some View {
Text("Hello, World!")
}
}
Text
视图。将按钮的action
设置为toggleChecked
方法的调用。对于按钮的标签,我们使用Label
视图,并将systemImage
属性设置为复选标记图标。systemImage
属性接受要显示的系统图标的名称。当复选框被选中时,我们将图标设置为"checkmark.circle.fill"
,来自 Apple 的内置系统字体,未选中时设置为"circle"
。struct Checkbox: View {
@ObservedObject var data: CheckboxData
var body: some View {
Button(action: {
self.data.toggleChecked?()
} label: {
Label("",systemImage: self.data.checked ? "checkmark.circle.fill" : "circle")
.labelStyle(.iconOnly)
.foregroundColor(self.data.checked ? .yellow : .yellow)
})
}
}
Label
视图是一个容器视图,显示文本和图标。对于复选框,我们只需要图标。要仅显示图标,我们应用labelStyle
修饰符,其值为.iconOnly
struct Checkbox: View {
@ObservedObject var data: CheckboxData
var body: some View {
Button(action: {
self.data.toggleChecked?()
} label: {
Label("",systemImage: self.data.checked ? "checkmark.circle.fill" : "circle")
.labelStyle(.iconOnly)
})
}
}
.foregroundColor
修饰符并使用所需的颜色struct Checkbox: View {
@ObservedObject var data: CheckboxData
var body: some View {
Button(action: {
self.data.toggleChecked?()
} label: {
Label("",systemImage: self.data.checked ? "checkmark.circle.fill" : "circle")
.labelStyle(.iconOnly)
.foregroundColor(self.data.checked ? .yellow : .yellow)
})
}
}
要在 NativeScript 中使用复选框,请按照以下步骤操作
要在 NativeScript 中使用 SwiftUI 视图,我们可以使用@nativescript/swift-ui。
npm i @nativescript/swift-ui
App_Resources/iOS/Checkbox.swift
中。我们甚至可以在项目运行后根据需要继续使用 Xcode 编辑该文件,方法是打开platforms/ios/{project}.xcodeproj
,在NSNativeSource
文件夹中查看该文件。App_Resources/iOS/CheckboxViewProvider.swift
文件,该文件具有以下结构,声明用于保存 Checkbox 实例的属性@objc
class CheckboxProvider: UIViewController, SwiftUIProvider {
var checkbox: Checkbox!
// Allow sending data to NativeScript
var onEvent: ((NSDictionary) -> ())?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
required public init() {
super.init(nibName: nil, bundle: nil)
}
public override func viewDidLoad() {
super.viewDidLoad()
}
// (Optionally) Receive data from NativeScript
func updateData(data: NSDictionary) {
}
}
CheckboxData
实例化为一个属性,以便在checked
属性更改时可以更新它。然后,我们在viewDidLoad
中将其提供给Checkbox
视图,并使用它调用setupSwiftUIView
。@objc
class CheckboxProvider: UIViewController, SwiftUIProvider {
var checkboxData = CheckboxData()
var checkbox: Checkbox!
// Allow sending data to NativeScript
var onEvent: ((NSDictionary) -> ())?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
required public init() {
super.init(nibName: nil, bundle: nil)
}
public override func viewDidLoad() {
super.viewDidLoad()
checkbox = Checkbox(checkboxProps: checkboxData)
setupSwiftUIView(content: checkbox)
}
// (Optionally) Receive data from NativeScript
func updateData(data: NSDictionary) {
}
}
checked
属性值发送到 NativeScript。@objc
class CheckboxProvider: UIViewController, SwiftUIProvider {
var checkboxData = CheckboxData()
var checkbox: Checkbox!
// Allow sending data to NativeScript
var onEvent: ((NSDictionary) -> ())?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
required public init() {
super.init(nibName: nil, bundle: nil)
}
public override func viewDidLoad() {
super.viewDidLoad()
checkbox = Checkbox(checkboxProps: checkboxData)
setupSwiftUIView(content: checkbox)
registerObservers()
}
private func registerObservers() {
self.checkbox.checkboxProps.changeChecked = {
// notify NativeScript
self.onEvent?(["checked": self.checkbox.checkboxProps.checked])
}
}
// (Optionally) Receive data from NativeScript
func updateData(data: NSDictionary) {
}
}
app.ts
或main.ts
)中,使用 NativeScript 注册复选框视图// you can optionally run `ns typings ios` to include types if desired here
declare const CheckboxProvider: any;
import {
registerSwiftUI,
UIDataDriver
} from "@nativescript/swift-ui";
registerSwiftUI("checkbox", (view) => new UIDataDriver(CheckboxProvider.alloc().init(), view)
);
Application.run({ moduleName: 'app-root' })
这将使用swiftId
= checkbox
注册一个新的视图元素/组件,您现在可以在布局中的任何位置使用它。
要将复选框视图添加到 NativeScript 标记中,请添加SwiftUI
组件并设置其swiftId
属性。
<Page xmlns="http://schemas.nativescript.org/tns.xsd"
xmlns:ui="nativescript-swiftui">
<ui:SwiftUI swiftId="checkbox" />
</Page>
注意:每个 JavaScript 框架都有自己的视图标记语法,但是您可以在任何您喜欢的框架中使用相同的原理。
让我们在 ListView 中使用复选框视图来选择将开始比赛的球员。用以下代码替换<ui:SwiftUI swiftId="checkbox" />
。
<ListView items="{{ items }}">
<ListView.itemTemplate>
<GridLayout columns="auto,*">
<ui:SwiftUI swiftId="checkbox"
id="{{ id }}"
data="{{ nativeCheckboxData }}"
swiftUIEvent="{{ onEvent }}"
width="50" height="50"
loaded="loadedSwiftUI"/>
<Label col="1" text="{{ player }}" class="t-18 m-l-10" />
</GridLayout>
</ListView.itemTemplate>
</ListView>
data
属性保存发送到 SwiftUI 的 JS 数据。export class HelloWorldModel extends Observable {
nativeCheckboxData: ICheckboxNativeData = {
checkboxOutlineType: "circle",
color: new Color("#77588C"),
buttonType: "checkmark",
checked: false
};
}
swiftUIEvent
在SwiftUI
向 NativeScript 发送数据时触发。import { Observable } from '@nativescript/core';
export class HelloWorldModel extends Observable {
onEvent(evt: SwiftUIEventData<CheckboxData>) {
const view = evt.object as View;
const viewId = view.id;
// handle our data
}
Android 和 iOS 都通过行循环优化列表控件。这意味着当用户滚动时,要在屏幕上添加更多行,列表会重复使用已创建的行,以避免为长列表创建多余的视图,从而保持移动设备上的内存效率。这意味着当用户滚动时,SwiftUI
组件会被重复使用。要查看其工作原理,请选择第一个和第二个复选框并向下滚动。您将看到有一个您未选择的复选框被选中。这是因为第一个SwiftUI
组件被重复使用。再次向上和向下滚动,您将看到您未选择的复选框被选中。
问题发生的原因是,复选框对 ObservableObject 实例所做的更改未传达给驱动列表视图行的的数据源。因此,列表视图项会使用旧数据进行循环使用。
要解决此问题,我们可以采取以下步骤
id
属性来标识复选框。public class CheckboxData: ObservableObject {
@Published var id: Int = 0
@Published var checked: Bool = false
...
}
id
以在我们的CheckboxProvider
中将 ListView 数据链接到复选框func updateData(data: NSDictionary) {
if let itemId = data.value(forKey: "id") as? Int {
checkbox.checkboxProps.id = itemId
}
}
checked
值,启用由 ListView 数据驱动的复选框状态func updateData(data: NSDictionary) {
if let checkedValue = data.value(forKey: "checked") as? Bool {
checkbox.checkboxProps.checked = checkedValue
}
}
onEvent(evt: SwiftUIEventData<ICheckboxNativeData>) {
const viewId = evt.data.id;
const rowItem = this.items.find((item) => item.id === viewId);
rowItem.nativeCheckboxData.checked = evt.data.checked;
}
到目前为止,只有您选择的复选框才会被选中。非常棒。