谈到跨平台,我的内心是抗拒的,因为每个平台都有各自的特性,各自的使用场景,若仅仅为了代码复用而牺牲用户体验,岂不是本末倒置,开发产品是在服务用户还是服务程序员呢?当然也不能完全否定了,若只是复用一些基本UI组件组件确实是一个正确的选择。扯的有点远,我们回归正题。
这里所说的跨平台可不是iOS和Android,而是指iWatch,MacOS,TV。本文中仅使用iWatch App进行举例说明,同样的代码,不经过任何修改可以直接运行在iWatch上。

在这个简单的例子中,两个平台使用了同样的一个View,未经过任何的修改,示例代码请移步Github。
添加Watch Target
通过File -> New -> Target -> watchOS,添加Watch App for iOS App。构建视图方式选择SwiftUI。

在生成的模板目录下有一个名为HostingController的文件:
import WatchKit
import Foundation
import SwiftUI
class HostingController: WKHostingController<ContentView> {
override var body: ContentView {
return ContentView()
}
}
它便是Watch App的UI入口,它的body返回了一个ContentView,在ContentView在同一个目录下,只是一个普通的SwiftUI view,和我们在iOS App中看到的完全一样。运行起来看看,一个简单的Hello World显示在Watch的模拟器上。
看到这里,我们已经有了一个大概的思路,想要复用一个页面,其实也就是写一个SwiftUI的View,那么如何让一个文件被两个不同的Target使用呢?最简单的方式自然是在文件的右侧Target Memebership
中同时勾选多个Target。在这里,我们来换一个思路,使用一些Swift Package。
Swift Package 复用代码
创建方式File -> New -> Swift Package

创建完成后,修改一些package的配置文件Package.swift,修改其需要的各平台版本:
import PackageDescription
let package = Package(
name: "Game",
platforms: [.iOS(.v13), .macOS(.v10_15), .watchOS(.v6), .tvOS(.v13)],
products: [
.library(
name: "Game",
targets: ["Game"]),
],
dependencies: [],
targets: [
.target(
name: "Game",
dependencies: []),
.testTarget(
name: "GameTests",
dependencies: ["Game"]),
]
)
然后将需要复用的代码移至该package的Sources下,这里,我们放入了一个View和它的ViewModel
import SwiftUI
public struct BullsEyeContentView: View {
@ObservedObject private var game = BullsEyeGame()
@State private var currentValue = 50.0
@State private var showAlert = false
private var alpha: Double {
abs(Double(game.targetValue) - currentValue) / 100.0
}
public init() {}
public var body: some View {
VStack {
Text("Aim to: \(game.targetValue)")
HStack {
Text("0")
Slider(value: $currentValue, in: 1.0...100.0, step: 1.0)
.background(Color.blue)
.opacity(alpha)
Text("100")
}
.padding(.horizontal)
Button(action: {
self.showAlert = true
self.game.checkGuess(Int(self.currentValue))
}) {
Text("Hit Me!")
}.alert(isPresented: $showAlert) {
Alert(title: Text("Your Score"), message: Text(String(game.scoreRound)),
dismissButton: .default(Text("OK"), action: {
self.game.startNewRound()
self.currentValue = 50.0
}))
}
.padding()
HStack {
Text("Total: \(game.scoreTotal)")
Text("Round: \(game.round)")
}
}
}
}
需要注意的,添加访问修饰符public。那么如何让App和Watch可使用pacakge中的代码呢?和引用一个系统的Framwork一样,在该Target的General配置页中的Frameworks, Libraries, and Embedded Content
中添加该Target。
大功告成,这一节的内容非常简单,并不是在说WatchApp该如何开发,只是在聊聊使用SwiftUI开发WatchApp UI的方式,以及如何通过Swfit Package来复用代码。各位看官,我们下周继续,周末愉快。
网友评论