快捷搜索:  汽车  科技

swiftui 打开新页面方式(SwiftWebUI详细介绍示例图解)

swiftui 打开新页面方式(SwiftWebUI详细介绍示例图解).padding(.all) Text(" #\(counter)") func countUp() { counter = 1 } var body: some View { VStack {

swiftWebUI

那么SwiftWebUI到底是 什么?它允许您编写 在Web浏览器中显示的SwiftUI 视图:

import SwiftWebUI

struct MainPage: View {

@State var counter = 0

func countUp() { counter = 1 }

var body: some View {

VStack {

Text(" #\(counter)")

.padding(.all)

.background(.green cornerRadius: 12)

.foregroundColor(.white)

.tapAction(self.countUp)

}

}

}

结果是:

swiftui 打开新页面方式(SwiftWebUI详细介绍示例图解)(1)

与其他一些努力不同,这不仅仅是将SwiftUI Views渲染为HTML。它还在浏览器和Swift服务器中托管的代码之间建立了一个连接,允许交互 - 按钮,选择器,步进器,列表,导航,你得到它!

换句话说: SwiftWebUI 是浏览器的SwiftUI API的(很多但不是所有部分)的实现。

重复 免责声明:这是一个玩具项目!不要用于生产。使用它来了解有关SwiftUI及其内部工作的更多信息。

学习一次,随处使用

SwiftUI的既定目标不是“ 一次编写,随处运行 ”,而是“ 一次学习,随处使用 ”。不要期望能够为iOS购买漂亮的SwiftUI应用程序,将代码放入SwiftWebUI项目并使其在浏览器中呈现完全相同。这不是重点。

关键是能够重用 knoff-hoff 并在不同平台之间共享它。在这种情况下,网络✔️

但是让我们深入了解细节并编写一个简单的SwiftWebUI应用程序。本着“一次学习,随处使用”的精神,首先观看这两个WWDC会议: 介绍SwiftUI 和 SwiftUI Essentials。我们在这个博客条目中并没有那么深入,但也建议使用这个(并且SwiftWebUI中主要支持这些概念): 通过SwiftUI的数据流。

要求

运行SwiftWebUI有三个选项:

macOS Catalina

可以使用 macOS Catalina 安装来运行SwiftWebUI。确保Catalina版本与您的Xcode 11测试版匹配!(“Swift ABI”♀️)

幸运的是,将Catalina安装在单独的APFS卷上非常容易 。并且需要安装 Xcode 11 才能获得SwiftUI大量使用的新Swift 5.1功能。了解?很好!

为什么需要Catalina?SwiftUI使用新的Swift 5.1运行时功能(例如不透明的结果类型)。这些功能在Mojave附带的Swift 5运行时中不可用。(另一个原因是使用仅在Catalina中可用的Combine,尽管可以使用OpenCombine修复该部分 )

tuxOS

SwiftWebUI现在使用OpenCombine在Linux上运行(也可以在没有它的情况下运行 ,但是有些东西不起作用,例如NavigationView)。

需要Swift 5.1快照。我们还提供了一个包含5.1快照的Docker镜像: helje5 / swift。

莫哈韦

Xcode 11beta iOS 13模拟器可以在Mojave上运行。您可以在iOS应用程序中运行SwiftWebUI。

第一个应用程序入门

创建SwiftWebUI项目

启动Xcode 11,选择“File> New> Project ...”或按Cmd-Shift-N:

swiftui 打开新页面方式(SwiftWebUI详细介绍示例图解)(2)

选择“macOS /命令行工具”项目模板:

swiftui 打开新页面方式(SwiftWebUI详细介绍示例图解)(3)

给它一些好名字,让我们选择“AvocadoToast”:

swiftui 打开新页面方式(SwiftWebUI详细介绍示例图解)(4)

然后我们将SwiftWebUI添加 为Swift Package Manager依赖项。该选项隐藏在“File / Swift Packages”菜单组中:

swiftui 打开新页面方式(SwiftWebUI详细介绍示例图解)(5)

输入https://GitHub.com/SwiftWebUI/SwiftWebUI.git包URL:

swiftui 打开新页面方式(SwiftWebUI详细介绍示例图解)(6)

使用“分支” master选项始终获取最新和最好的(您也可以使用修订版或develop分支):

swiftui 打开新页面方式(SwiftWebUI详细介绍示例图解)(7)

最后将SwiftWebUI库添加到工具目标:

swiftui 打开新页面方式(SwiftWebUI详细介绍示例图解)(8)

而已。你现在有一个可以的工具项目import SwiftWebUI。(Xcode可能需要一些时间来获取和构建依赖项。)

SwiftWebUI Hello World

让我们开始使用SwiftWebUI。打开main.swift文件并将其内容替换为:

import SwiftWebUI

SwiftWebUI.serve(Text("Holy Cow!"))

在Xcode中编译并运行应用程序,打开Safari并点击 http://localhost:1337/:

swiftui 打开新页面方式(SwiftWebUI详细介绍示例图解)(9)

这里发生了什么:首先导入SwiftWebUI模块(不要意外导入macOSSwiftUI)

然后我们调用SwiftWebUI.serve哪个接受一个闭包返回一个视图,或者只是一个直视图 - 如下所示:一个 Text 视图(又名“UILabel”,它可以显示普通或格式化的文本)。

在幕后

在内部,该 serve 函数创建一个非常简单的 SwiftNIO HTTP服务器,侦听端口1337.当浏览器访问该服务器时,它会创建一个 会话 并将我们的(文本)视图传递给该会话。

最后,从View中,SwiftWebUI在服务器上创建一个“Shadow DOM”,将其呈现为HTML并将结果发送到浏览器。“Shadow DOM”(和状态对象保持在一起)存储在会话中。

这是SwiftWebUI应用程序与watchOS或iOS SwiftUI应用程序之间的区别。单个SwiftWebUI应用程序为一组用户提供服务,而不仅仅是一个用户。

添加一些交互

作为第一步,让我们更好地组织代码。在项目中创建一个新的Swift文件并调用它MainPage.swift。并为其添加一个简单的SwiftUI View定义:

import SwiftWebUI

struct MainPage: View {

var body: some View {

Text("Holy Cow!")

}

}

调整main.swift以提供我们的自定义视图:

SwiftWebUI.serve(MainPage())

我们现在可以main.swift独自完成所有工作并完成我们的习惯 View。让我们添加一些互动:

struct MainPage: View {

@State var counter = 3

func countUp() { counter = 1 }

var body: some View {

Text("Count is: \(counter)")

.tapAction(self.countUp)

}

}

我们 View 得到了一个持久 State 变量命名counter (不确定这是什么?再看一下SwiftUI的介绍)。还有一个小功能来撞击计数器。

然后我们使用SwiftUI tapAction 修饰符将事件处理程序附加到我们的Text。最后,我们在标签中显示当前值:

swiftui 打开新页面方式(SwiftWebUI详细介绍示例图解)(10)

♀️ MAGIC

在幕后

这是如何运作的?当浏览器点击我们的端点时,SwiftWebUI在其中创建了会话和我们的“Shadow DOM”。然后它将描述我们View的HTML发送到浏览器。将tapAction通过增加工程onclick处理程序的HTML。SwiftWebUI还向浏览器发送(少量,没有大框架!)JavaScript,处理点击并将其转发到我们的Swift服务器。

然后通常的SwiftUI魔法开始.SwiftWebUI将click事件与我们的“Shadow DOM”中的事件处理程序相关联并调用该countUp 函数。通过修改counter State 变量,该函数使View的呈现无效。SwiftWebUI启动并扩展“Shadow DOM”中的更改。然后将这些更改发送回浏览器。

“更改”作为JSON数组发送,我们在页面中的小JavaScript可以处理这些数组。如果整个子树发生了变化(例如,如果用户导航到一个全新的视图),则更改可以是应用于innerHTML或的更大的HTML片段outerHTML。但通常情况下,这些变化都是小事,比如add class( set HTML attribute和浏览器DOM修改)。

优秀,基础工作。让我们带来更多的互动性。以下是基于用于在SwiftUI Essentials 演讲中演示SwiftUI的“Avocado Toast App” 。

HTML / CSS样式不是很完美也不漂亮。您可以想象我们不是网页设计师,可以在这里使用一些帮助。PR欢迎!

想要跳过细节,观看应用程序的GIF并在GitHub上下载:。

订单

谈话开始于此(~6:00),我们可以将其添加到新OrderForm.swift 文件中:

struct Order {

var includeSalt = false

var includeRedPepperFlakes = false

var quantity = 0

}

struct OrderForm: View {

@State private var order = Order()

func submitOrder() {}

var body: some View {

VStack {

Text("Avocado Toast").font(.title)

Toggle(isOn: $order.includeSalt) {

Text("Include Salt")

}

Toggle(isOn: $order.includeRedPepperFlakes) {

Text("Include Red Pepper Flakes")

}

Stepper(value: $order.quantity in: 1...10) {

Text("Quantity: \(order.quantity)")

}

Button(action: submitOrder) {

Text("Order")

}

}

}

}

对于直接测试SwiftWebUI.serve()中main.swift的新 OrderForm景观。

这就是它在浏览器中的样子:

swiftui 打开新页面方式(SwiftWebUI详细介绍示例图解)(11)

SemanticUI用于在SwiftWebUI中设置一些样式。操作并不是严格要求的,它只是完成了一些看似体面的小部件。注意:仅使用CSS /字体,而不是JavaScript组件。

间歇:一些SwiftUI布局

在SwiftUI Essentials中大约16:00, 他们将获得SwiftUI布局和View修改器排序:

var body: some View {

HStack {

Text("")

.background(.green cornerRadius: 12)

.padding(.all)

Text(" => ")

Text("")

.padding(.all)

.background(.green cornerRadius: 12)

}

}

结果如下,请注意修饰符的排序是如何相关的:

swiftui 打开新页面方式(SwiftWebUI详细介绍示例图解)(12)

SwiftWebUI尝试复制常见的SwiftUI布局,但还没有完全成功。毕竟它必须处理浏览器提供的布局系统。求助,Flexbox专家欢迎!

订单历史

回到应用程序,谈话(~19:50)介绍了 列表 视图,用于显示鳄梨土司订单历史记录。这就是它在Web上的外观:

swiftui 打开新页面方式(SwiftWebUI详细介绍示例图解)(13)

该List视图散步完成的订单阵列之上,并为每一个(创建一个子视图OrderCell),通过在当前项目在列表中。

这是我们使用的代码:

struct OrderHistory: View {

let previousOrders : [ CompletedOrder ]

var body: some View {

List(previousOrders) { order in

OrderCell(order: order)

}

}

}

struct OrderCell: View {

let order : CompletedOrder

var body: some View {

HStack {

VStack(alignment: .leading) {

Text(order.summary)

Text(order.purchaseDate)

.font(.subheadline)

.foregroundColor(.secondary)

}

Spacer()

if order.includeSalt {

SaltIcon()

}

else {}

if order.includeRedPepperFlakes {

RedPepperFlakesIcon()

}

else {}

}

}

}

struct SaltIcon: View {

let body = Text("")

}

struct RedPepperFlakesIcon: View {

let body = Text("")

}

// Model

struct CompletedOrder: Identifiable {

var id : Int

var summary : String

var purchaseDate : String

var includeSalt = false

var includeRedPepperFlakes = false

}

SwiftWebUI列表视图效率非常低,它总是渲染整个子集。没有单元重用,没有任何东西在Web应用程序中有多种方法可以处理,例如通过使用分页或更多客户端逻辑。

所以你不必从谈话中输入样本数据,我们为你做了这样的事情:

let previousOrders : [ CompletedOrder ] = [

.init(id: 1 summary: "Rye with Almond Butter" purchaseDate: "2019-05-30")

.init(id: 2 summary: "Multi-Grain with Hummus" purchaseDate: "2019-06-02"

includeRedPepperFlakes: true)

.init(id: 3 summary: "Sourdough with Chutney" purchaseDate: "2019-06-08"

includeSalt: true includeRedPepperFlakes: true)

.init(id: 4 summary: "Rye with Peanut Butter" purchaseDate: "2019-06-09")

.init(id: 5 summary: "Wheat with Tapenade" purchaseDate: "2019-06-12")

.init(id: 6 summary: "Sourdough with Vegemite" purchaseDate: "2019-06-14"

includeSalt: true)

.init(id: 7 summary: "Wheat with Féroce" purchaseDate: "2019-06-31")

.init(id: 8 summary: "Rhy with Honey" purchaseDate: "2019-07-03")

.init(id: 9 summary: "Multigrain Toast" purchaseDate: "2019-07-04"

includeSalt: true)

.init(id: 10 summary: "Sourdough with Chutney" purchaseDate: "2019-07-06")

]

点差选择器

Picker控件以及如何使用它与enum's演示~43:00。首先是各种吐司选项的枚举:

enum AvocadoStyle {

case sliced mashed

}

enum BreadType: CaseIterable Hashable Identifiable {

case wheat white rhy

var name: String { return "\(self)".capitalized }

}

enum Spread: CaseIterable Hashable Identifiable {

case none almondButter peanutButter honey

case almou tapenade hummus mayonnaise

case kyopolou adjvar pindjur

case vegemite chutney cannedCheese feroce

case kartoffelkase tartarSauce

var name: String {

return "\(self)".map { $0.isUppercase ? " \($0)" : "\($0)" }

.joined().capitalized

}

}

我们可以将它们添加到我们的Orderstruct:

struct Order {

var includeSalt = false

var includeRedPepperFlakes = false

var quantity = 0

var avocadoStyle = AvocadoStyle.sliced

var spread = Spread.none

var breadType = BreadType.wheat

}

然后使用不同的Picker类型显示它们。如何循环枚举值非常简洁:

Form {

Section(header: Text("Avocado Toast").font(.title)) {

Picker(selection: $order.breadType label: Text("Bread")) {

ForEach(BreadType.allCases) { breadType in

Text(breadType.name).tag(breadType)

}

}

.pickerStyle(.radioGroup)

Picker(selection: $order.avocadoStyle label: Text("Avocado")) {

Text("Sliced").tag(AvocadoStyle.sliced)

Text("Mashed").tag(AvocadoStyle.mashed)

}

.pickerStyle(.radioGroup)

Picker(selection: $order.spread label: Text("Spread")) {

ForEach(Spread.allCases) { spread in

Text(spread.name).tag(spread) // there is no .name?!

}

}

}

}

结果:

swiftui 打开新页面方式(SwiftWebUI详细介绍示例图解)(14)

再次,这需要一些CSS的爱,使它看起来更好......

“完成”应用程序

不,我们与原版略有不同,也没有真正完成它。它看起来并不那么棒,但它毕竟是一个演示

swiftui 打开新页面方式(SwiftWebUI详细介绍示例图解)(15)

完成的应用程序可在GitHub上获得: AvocadoToast。

HTML和SemanticUI

UIViewRepresentable SwiftWebUI中的 对等体正在发出原始HTML。

提供了两种变体,HTML输出为String-as,或者通过HTML转义内容:

struct MyHTMLView: View {

var body: some View {

VStack {

HTML("<blink>Blinken Lights</blink>")

HTML("42 > 1337" escape: true)

}

}

}

使用此原语,您基本上可以构建所需的任何HTML。

更高一级甚至内部使用 HTMLContainer。例如,这是我们Stepper控件的实现:

var body: some View {

HStack {

HTMLContainer(classes: [ "ui" "icon" "buttons" "small" ]) {

Button(self.decrement) {

HTMLContainer("i" classes: [ "minus" "icon" ] body: {EmptyView()})

}

Button(self.increment) {

HTMLContainer("i" classes: [ "plus" "icon" ] body: {EmptyView()})

}

}

label

}

}

该HTMLContainer如果类,风格或属性的变化是“反应性”,也就是说,它会散发出常规的DOM变化(而不是重新绘制整个事情)

SemanticUI

SwiftWebUI还带有一些 预先设置的SemanticUI控件:

VStack {

SUILabel(Image(systemName: "mail")) { Text("42") }

HStack {

SUILabel(Image(...)) { Text("Joe") } ...

}

HStack {

SUILabel(Image(...)) { Text("Joe") } ...

}

HStack {

SUILabel(Image(...) Color("blue")

detail: Text("Friend"))

{

Text("Veronika")

} ...

}

}

......呈现这样的:

swiftui 打开新页面方式(SwiftWebUI详细介绍示例图解)(16)

请注意,SwiftWebUI还支持一些SFSymbols图像名称(via Image(systemName:))。这些都得到了SemanticUI 对Font Awesome的 支持。

还有SUISegment,SUIFlag和SUICard:

SUICards {

SUICard(Image.unsplash(size: UXSize(width: 200 height: 200)

"Zebra" "Animal")

Text("Some Zebra")

meta: Text("Roaming the world since 1976"))

{

Text("A striped animal.")

}

SUICard(Image.unsplash(size: UXSize(width: 200 height: 200)

"Cow" "Animal")

Text("Some Cow")

meta: Text("Milk it"))

{

Text("Holy cow!.")

}

}

...渲染那些:

swiftui 打开新页面方式(SwiftWebUI详细介绍示例图解)(17)

添加此类视图非常简单,非常有趣。可以使用WOComponent的 SwiftUI Views 快速构建相当复杂和美观的布局。

Image.unsplash根据运行的Unsplash API构造图像查询http://source.unsplash.com。只需给它一些查询术语,您想要的大小和可选范围。

注意:有时,特定的Unsplash服务似乎有点慢且不可靠。

文章英文版出处:

http://www.alwaysrightinstitute.com/swiftwebui/

猜您喜欢: