在学习Swift 2.0 正式来临之前,我决心先体验一下使用 SwiftUI 写个小软件。本文基于目前的 SwiftUI 开发并没有使用任何2.0的新特性。
熟悉基本操作
首先多出来这个界面叫做 Canvas 不小心关掉的话可以在 Editor > Canvas 打开
左下角的 可以在切换文件固定这个界面,很方便的功能
右下角是很普通的缩放功能 右侧有两个按钮▶️可以让 preview 运行起来, 可以让 preview 之间在设备上运行
效果图
App 整体上就是一个文章列表,每个文章点进去就是一个 web view,所以组件就三个:
- List 文章列表
- Cell 列表元素显示-图片,标题和副标题
- WebView 网页-文章内容
数据模型
根据 API 接口 https://sspai.com/api/v1/article/index/page/get 返回的数据创建数据模型
struct NetworkResponse : Codable {let msg: Stringlet error: Intlet data: T
}
struct ItemBean: Codable, Identifiable {let id: Intlet title: Stringlet summary: Stringlet banner: String//... more
}
List组件需要 item 满足 Identifiable 协议
List
创建文件ItemList.swift
struct ItemList: View {@State var items: [ItemBean] = []var body: some View {List(0 ...self, from: data).data else {// Errorreturn}DispatchQueue.main.async {self.items = items}}.resume()}
}
@State修饰符可以关联View的状态,当item变化会重新创建视图 self.items = items
这行代码会刷新界面需要放到DispatchQueue.main
Cell
Cell就很普通,后面在加图片。
struct ItemCell: View {let item: ItemBeanvar body: some View {VStack {Text("https://cdn.sspai.com/" + item.banner)Text(item.title).font(.headline)Spacer().frame(height: 8)Text(item.summary).font(.subheadline).foregroundColor(Color.secondary)}}
}
WebView
SwiftUI的组件中是没有WebView的,但是提供了UIViewRepresentable
来使用UIKit中的组件,只需要实现协议即可。
struct WebView: UIViewRepresentable {let urlString: Stringfunc makeUIView(context: Context) -> WKWebView {guard let url = URL(string: self.urlString) else {return WKWebView()}let requeset = URLRequest(url: url)let wk = WKWebView()wk.isOpaque = falsewk.load(requeset)return wk}func updateUIView(_ uiView: WKWebView, context: Context) {}
}
导航
把列表放到 NavigationView 中,Cell 放到 NavigationLink 中。
如果要改导航栏,在NavigationView里面的view改 直接使用NavigationLink会有一个箭头,可以放到ZStack中 导航出来的有SafeArea,如果想要全屏显示可以用edgesIgnoringSafeArea(Edge.Set.all)
NavigationView {List(0 ..}
网络图片
SwiftUI 目前还没有支持简单网络图片加载,不过有特别好用的第三方库,也可以自己写一个简单的用。不过还是建议使用成熟的第三方解决方案。
struct NetWorkImage: View {init(url: URL) {self.imageLoader = Loader(url)}@ObservedObject private var imageLoader: Loadervar image: UIImage? {imageLoader.data.flatMap(UIImage.init)}var body: some View {VStack {if image != nil {Image(uiImage: image!).resizable()} else {EmptyView()}}}}final class Loader: ObservableObject {var task: URLSessionDataTask!@Published var data: Data? = nilinit(_ url: URL) {task = URLSession.shared.dataTask(with: url, completionHandler: { data, _, _ inDispatchQueue.main.async {self.data = data}})task.resume()}deinit {task.cancel()}
}
改造之前的 Cell,让它显示图片
struct ItemCell: View {let item: ItemBeanvar body: some View {VStack {GeometryReader { geometry inNetWorkImage(url:URL(string: "https://cdn.sspai.com/(self.item.banner)")!).scaledToFill().frame(width: geometry.size.width, height: geometry.size.width/2).clipShape(RoundedRectangle(cornerRadius: 16))}.aspectRatio(2, contentMode: .fit).clipped()Text(item.title).font(.headline)Spacer().frame(height: 8)Text(item.summary).font(.subheadline).foregroundColor(Color.secondary)}}
}
刷新 加载更多
下拉刷新暂时没有比较简单的方案,我就直接导航上加一个刷新按钮。 加载更多就比较简单,给 Cell 加onAppear {self.fetchMoreItemsIfNeed(current: index)}
根据index判断是否加载更多。ItemList最终代码就设这样的:
struct ItemList: View {&#64;State var items: [ItemBean] &#61; []var body: some View {NavigationView {List(0 ...self, from: data).data else {// Errorreturn}DispatchQueue.main.async {self.items &#61; items}}.resume()}func fetchMoreItemsIfNeed(current: Int) {guard current &#61;&#61; self.items.count - 1 else {return}let url &#61; URL(string: "https://sspai.com/api/v1/article/index/page/get?limit&#61;10&offset&#61;(items.count)")!URLSession.shared.dataTask(with: url) { (data, response, error) inDispatchQueue.main.async {guard let data &#61; data else {// Errorreturn}guard let items &#61; try? JSONDecoder().decode(NetworkResponse<[ItemBean]>.self, from: data).data else {// Errorreturn}self.items.append(contentsOf: items)}}.resume()}
}
总结
这是我第一个使用 SwiftUI 完成的 App&#xff0c;支持 iOS 和 Mac。熟悉 Flutter 的我用 SwiftUI 刚开始的感到很难受&#xff0c;Apple 的工具链还非常不完善&#xff0c;不过熟悉的之后会好一些&#xff0c;不过还是希望下个版本有所改进吧。
- 缺点
- 在布局的时候不要妄图让Xcode告诉你改怎么写&#xff0c;代码提示真的真的真的会让人崩溃。
- 调试的时候要不要太在意错误提示的信息&#xff0c;真的没用。
- 文档很简陋&#xff0c;也没有示例代码&#xff0c;全靠 Google
- 优点
- 调试界面不用运行整个项目&#xff0c;改了界面也能马上生效&#xff0c;我觉得比 Flutter 还好用
- SwiftUI 代码很直观
Githubgithub.com