SwiftUI实战一:从入门到精通

系统: Mac OS 10.15.1, XCode 11.2.1,swift 5.0
写作时间:2019-11-22

说明

Swift 5以后 ABI 稳定了。(参考: ABI Stability and MoreEvolving Swift On Apple Platforms After ABI Stability )

什么是 ABI 稳定?

就是 binary 接口稳定,也就是在运行的时候只要是用 Swift 5 (或以上) 的编译器编译出来的 binary,就可以跑在任意的 Swift 5 (或以上) 的 runtime 上。这样,我们就不需要像以往那样在 app 里放一个 Swift runtime 了,Apple 会把它弄到 iOS 和 macOS 系统里。
在这里插入图片描述

app 尺寸会变小?

是的,但是这是 Apple 通过 App Thinning 帮我们完成的,不需要你操心。在提交 app 时,Apple 将会按照 iOS 系统创建不同的下载包。对于 iOS 12.2 的系统,因为它们预装了 Swift 5 的 runtime,所以不再需要 Swift 的库,它们会被从 app bundle 中删掉。对于 iOS 12.2 以下的系统,外甥打灯笼,照旧。

一个新创建的空 app,针对 iOS 12.2 打包出来压缩后的下载大小是 26KB,而对 iOS 12.0 则是 2.4MB。如果你使用了很多标准库里的东西,那这个差距会更大 (因为没有用到的标准库的符号会被 strip 掉),对于一个比较有规模的 app 来说,一般可以减小 10M 左右的体积。

还有什么其他好处么?

因为系统集成了 Swift,所以大家都用同一个 Swift 了,app 启动的时候也就不需要额外加载 Swift,所以在新系统上会更快更省内存。当然啦,只是针对新系统。

ABI稳定后Apple憋了个一统江湖的新框架SwiftUI.
  1. 这个框架相当简洁,拖拽控件跟写代码实现达到一致性,不需要额外的关联(去掉了@IBAction, @IBOutlet).
  2. 写一套代码在Apple所有产品通用(iPhone, iPad, AppleWatch, AppleTV).
  3. 可见即可得,写完代码,点击右边的Resume按钮,或者用快捷键command+option+p, 就可以看到运行起来的样子。
  4. 模拟运行:如果点击右下角的预览按钮,跟模拟器运行的效果是一样的。
    在这里插入图片描述

下面就从0开始,打造上面的效果的例子。
实战的例子来源于:SwiftUI: Getting Started

创建工程

创建Xcode project (Shift-Command-N), 选择 iOS ▸ Single View App, 名字叫RGBullsEye, User Interface选择 SwiftUI。
在这里插入图片描述

SwiftUI是怎么启动的

打开分组RGBullsEye,将看到: 以前的AppDelegate.swift,现在分为AppDelegate.swiftSceneDelegate.swiftSceneDelegatewindow设置:
在这里插入图片描述
SceneDelegate 没有明确配置SwiftUI, 但是下面代码有:

let contentView = ContentView()

// Use a UIHostingController as window root view controller.
// ...
    window.rootViewController = UIHostingController(rootView: contentView)
// ...

当app启动, window显示实例ContentView, 它定义在文件ContentView.swift. 它是一个结构体struct遵循协议View protocol(老逻辑是View对象):

struct ContentView: View {
  var body: some View {
    Text("Hello World")
  }
}

SwiftUI 定义了ContentViewbodybody包含了一个Text view 显示文字Hello World.

预览结构体ContentView_Previews 生产一个view包含了ContentView实例.

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

如何显示预览效果?点击右边视图的Resume按钮
在这里插入图片描述
预览如下:
在这里插入图片描述
如果没有看到Resume按钮,选择editor options, 选择Canvas,或者快捷键command+option+enter
在这里插入图片描述

编写UI占位结构

Text("Hello World") 替换为如下:

Text("Target Color Block")

点击Resume按钮,看看文字是否已经改变。

Command+点击在右侧预览界面的Text view, 选择 Embed in HStack:
在这里插入图片描述
看到左侧代码已经更新(实际上手动写代码,点击Resume视图也跟着更新)

HStack {
  Text("Target Color Block")
}

拷贝粘贴Text 代码, 更新HStack 如下.

HStack {
  Text("Target Color Block")
  Text("Guess Color Block")
}

发现不需要逗号,效果是两个Text View在同一行:
Notice you don’t separate the two statements with a comma — just write each on its own line:
在这里插入图片描述
准备添加滑动块视图,在横向容器HStack里加入纵向容器VStack. — 在代码里Command-点击HStack, 选择Embed in VStack:
在这里插入图片描述

HStack闭包下面换行,点击右上角**+**按钮,打开控件库Library, 拖拽一个Vertical Stack 在换行的位置:在这里插入图片描述
添加Text View代码如下,预览如下:
在这里插入图片描述
完成以上UI布局,代码如下。(实际上,直接写代码就好,跟拖拽控件一样的效果)

VStack {
  HStack {
    Text("Target Color Block")
    Text("Guess Color Block")
  }

  Text("Hit me button")

  VStack {
    Text("Red slider")
    Text("Green slider")
    Text("Blue slider")
  }
}

在横向容器里,添加两个纵向容器VStack, 里面都有长方形Rectangle。 左边的纵向容器表示目标颜色,右边的纵向容器表示用户可以改变的颜色。

HStack {
  // Target color block
  VStack {
    Rectangle()
    Text("Match this color")
  }
  // Guess color block
  VStack {
    Rectangle()
    HStack {
      Text("R: xxx")
      Text("G: xxx")
      Text("B: xxx")
    }
  }
}

@State修饰属性

@State修饰的属性,表示属性更新,界面也同步更新。
在结构体ContentView里面,在body闭包的上面添加如下属性:

let rTarget = Double.random(in: 0..<1)
let gTarget = Double.random(in: 0..<1)
let bTarget = Double.random(in: 0..<1)
@State var rGuess: Double
@State var gGuess: Double
@State var bGuess: Double

R, G, B 的值在区间[0,1). 初始化target的值. 需要初始化Guess的值为0.5.

向下滚动到结构体ContentView_Previews, 实例化ContentView 需要改为带参数,修改如下:

ContentView(rGuess: 0.5, gGuess: 0.5, bGuess: 0.5)

同样需要修改在类SceneDelegate, 的方法scene(_:willConnectTo:options:) — 替换 ContentView()为:

let contentView = ContentView(rGuess: 0.5, gGuess: 0.5, bGuess: 0.5)

给目标矩形添加颜色

Rectangle()
  .foregroundColor(Color(red: rTarget, green: gTarget, blue: bTarget, opacity: 1.0))

给猜测矩形添加颜色

Rectangle()
  .foregroundColor(Color(red: rGuess, green: gGuess, blue: bGuess, opacity: 1.0))

点击Resume按钮预览如下:
在这里插入图片描述

滑块控件复用View

在没有重用之前, 在滑块在VStack, 替换代码 Text("Red slider") 如下:

HStack {
  Text("0")
    .foregroundColor(.red)
  Slider(value: $rGuess)
  Text("255")
    .foregroundColor(.red)
}

  1. 更新Text views的颜色为红色. 初始化滑块的值(0.5) . 滑块的值范围为[0, 1].
  2. $修饰变量rGuess,表示可读可写绑定 — 当修改滑块的值的时候,需要同步更新猜测矩形的颜色.

为了看出是否用修饰符$的区别,把如下代码添加到猜测矩形的下面

HStack {
  Text("R: \(Int(rGuess * 255.0))")
  Text("G: \(Int(gGuess * 255.0))")
  Text("B: \(Int(bGuess * 255.0))")
}

预览效果如下:
在这里插入图片描述
上面的滑块有点挤,添加一点空间,用.padding() 如下

HStack {
  Text("0")
    .foregroundColor(.red)
  Slider(value: $rGuess)
  Text("255")
    .foregroundColor(.red)
}
  .padding()

预览效果会宽一些
在这里插入图片描述
上面实现了红色滑块,实际上绿色、蓝色模块都是相同的逻辑,这里就要重构为公用方法。

Command-点击 红色滑块的HStack, 选择 Extract Subview:
在这里插入图片描述
右键Refactor ▸ Extract to Function一样可以达到上面重构的效果.
ExtractedView命名为ColorSlider, 在body闭包的上面添加代码:

@Binding var value: Double
var textColor: Color

替换 $rGuess$value, 替换 .redtextColor:

Text("0")
  .foregroundColor(textColor)
Slider(value: $value)
Text("255")
  .foregroundColor(textColor)


回到VStack调用方法ColorSlider()的地方, 添加参数:

ColorSlider(value: $rGuess, textColor: .red)

就是上面👆代码的下面,添加绿色滑块,蓝色滑块。

ColorSlider(value: $gGuess, textColor: .green)
ColorSlider(value: $bGuess, textColor: .blue)

预览效果如下:
在这里插入图片描述

点击Resume按钮,或者command + option + p 是静态预览,也就是没法拖动滑块。如果想要动态预览,需要点击预览界面,右下角的播放按钮。
在这里插入图片描述

动态预览成功如下:在这里插入图片描述

提交数据,展示分数

当两边矩形颜色差不多的时候,点击Hit Me按钮,弹出分数。
body的上面添加处理分数的方法

func computeScore() -> Int {
  let rDiff = rGuess - rTarget
  let gDiff = gGuess - gTarget
  let bDiff = bGuess - bTarget
  let diff = sqrt(rDiff * rDiff + gDiff * gDiff + bDiff * bDiff)
  return Int((1.0 - diff) * 100.0 + 0.5)
}

diff值表示两个点之间的差距,求3种颜色的空间距离。最后用1 - diff就是相同比例.

接着,替换Text("Hit me button") 为如下:

Button(action: {

}) {
  Text("Hit Me!")
}

按钮有事件和文字。如果在Button点击事件里面加Alert View, 将会没有响应.

反而, 创建Alert作为ContentViewsubview, 并添加@State修饰的Bool类型变量. 在Button点击事件里把这个变量值修改为trueAlert将会显示. 当用户点击Alert里面的按钮,Alert消失后,这个变量就自动变成false.

增加@State 修饰的遍历,初始化为false:

@State var showAlert = false

在Button的事件里添加

self.showAlert = true

最后添加Alert的实现方法,最终代码如下:

Button(action: {
  self.showAlert = true
}) {
  Text("Hit Me!")
}
.alert(isPresented: $showAlert) {
  Alert(title: Text("Your Score"), message: Text("\(computeScore())"))
}

动态预览效果如下:
在这里插入图片描述
如果在模拟器中运行,还可以翻转查看:
在这里插入图片描述

总结

恭喜你!已经学会SwiftUI的应用。
想要入门 SwiftUI 的使用,那 Apple 这次给出的官方教程绝对给力。

代码下载

https://github.com/zgpeace/RGBullsEye

参考

https://www.raywenderlich.com/3715234-swiftui-getting-started

https://onevcat.com/2019/02/swift-abi/

https://developer.apple.com/tutorials/swiftui/tutorials

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【课程特点】1、231节大容量课程:包含了SwiftUI的大部分知识点,详细讲解SwiftUI的方方面面;2、15个超级精彩的实例:包含美食、理财、健身、教育、电子商务等各行业的App实例;3、创新的教学模式:手把手教您SwiftUI用户界面开发技术,一看就懂,一学就会;4、贴心的操作提示:让您的眼睛始终处于操作的焦点位置,不用再满屏找光标;5、语言简洁精练:瞄准问题的核心所在,减少对思维的干扰,并节省您宝贵的时间;6、视频短小精悍:即方便于您的学习和记忆,也方便日后对功能的检索;7、齐全的学习资料:提供所有课程的源码,在Xcode 11 + iOS 13环境下测试通过; 更好的应用,更少的代码!SwiftUI是苹果主推的下一代用户界面搭建技术,具有声明式语法、实时生成界面预览等特性,可以为苹果手机、苹果平板、苹果电脑、苹果电视、苹果手表五个平台搭建统一的用户界面。SwiftUI是一种创新、简单的iOS开发中的界面布局方案,可以通过Swift语言的强大功能,在所有的Apple平台上快速构建用户界面。 仅使用一组工具和API为任何Apple设备构建用户界面。SwiftUI具有易于阅读和自然编写的声明式Swift语法,可与新的Xcode设计工具无缝协作,使您的代码和设计**同步。自动支持动态类型、暗黑模式、本地化和可访问性,意味着您的**行SwiftUI代码已经是您编写过的非常强大的UI代码了。 

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值