<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>SwiftUI 与 iOS 开发入门 on ClearSec Wiki</title><link>https://www.clearsec.org/wikis/skills/frontend/swiftui/</link><description>Recent content in SwiftUI 与 iOS 开发入门 on ClearSec Wiki</description><generator>Hugo</generator><language>zh-cn</language><atom:link href="https://www.clearsec.org/wikis/skills/frontend/swiftui/index.xml" rel="self" type="application/rss+xml"/><item><title>Hello World</title><link>https://www.clearsec.org/wikis/skills/frontend/swiftui/hello-world/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.clearsec.org/wikis/skills/frontend/swiftui/hello-world/</guid><description>&lt;div class="td-alert td-alert--md alert alert-note" role="alert"&gt;&lt;div class="td-alert-heading alert-heading" role="heading"&gt;说明&lt;/div&gt;
 &lt;div class="td-alert-body"&gt;
 &lt;p&gt;目前（2022 年 1 月）主流的 iOS 开发使用 Xcode 项目（Xcode project）进行，本文为了支持 iPadOS 平台并简化说明，使用最新的由 Swift Package Manager 管理的项目格式，此格式在 13.2 以上版本的 Xcode 上同样支持。实际开发时建议使用更加完善的 Xcode project。&lt;/p&gt;
 &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="创建项目"&gt;创建项目&lt;/h2&gt;
&lt;h3 id="swift-playgroundsipad"&gt;Swift Playgrounds（iPad）&lt;/h3&gt;
&lt;p&gt;在 Swift Playgrounds（4.0 以上版本）中，选择底部的 &lt;code&gt;App&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="xcode-swift-playground-app"&gt;Xcode Swift Playground App&lt;/h3&gt;
&lt;p&gt;在 Xcode（13.2 以上版本）中，选择 &lt;code&gt;File &amp;gt; New &amp;gt; Project&lt;/code&gt; 中的 &lt;code&gt;Swift Playground App&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="xcode-project"&gt;Xcode Project&lt;/h3&gt;
&lt;p&gt;传统的 Xcode project，部分内容与 Swift Playground App 不同。&lt;/p&gt;
&lt;p&gt;打开 Xcode，点击 &lt;code&gt;Create a new Xcode project&lt;/code&gt;，选择 &lt;code&gt;iOS&lt;/code&gt; 栏下的 &lt;code&gt;App&lt;/code&gt;。在项目的配置界面，配置：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Product Name：应用名称&lt;/li&gt;
&lt;li&gt;Team：开发使用的 Apple ID，可以为 &lt;code&gt;None&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Organization Identifier：一个逆序的 DNS 名，如果不考虑上架可以任意&lt;/li&gt;
&lt;li&gt;Interface：选择 &lt;code&gt;SwiftUI&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Language：&lt;code&gt;Swift&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Use Core Data 和 Include Tests：本教程不涉及，可以不选&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其余步骤按照默认配置即可。&lt;/p&gt;</description></item><item><title>构建 View</title><link>https://www.clearsec.org/wikis/skills/frontend/swiftui/build-views/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.clearsec.org/wikis/skills/frontend/swiftui/build-views/</guid><description>&lt;p&gt;接下来，我们将展开介绍如何构建 View。&lt;/p&gt;
&lt;h2 id="view-的组合方式"&gt;View 的组合方式&lt;/h2&gt;
&lt;p&gt;在 &lt;code&gt;body&lt;/code&gt; 中，有一些非常方便的方式来组合不同的 View。另外，&lt;code&gt;body&lt;/code&gt; 还对其中的表达式有一定的限制。&lt;/p&gt;
&lt;h3 id="组合"&gt;组合&lt;/h3&gt;
&lt;p&gt;在 &lt;code&gt;body&lt;/code&gt; 中，你可以通过这几种方式组合多个 View，&lt;strong&gt;不需要写出 &lt;code&gt;return&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;条件控制语句&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;你可以用 &lt;code&gt;if-else&lt;/code&gt; 语句有条件地显示不同的 View：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello, world!&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;hello-world&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;或者，只有 &lt;code&gt;if&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;revealGrade&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;student&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gpa&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当然，&lt;code&gt;if let&lt;/code&gt; 语句也是可以的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;npyName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;student&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;npyName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;npyName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你还可以用 &lt;code&gt;switch&lt;/code&gt;：（实际上编译器将其转化为 &lt;code&gt;if-else&lt;/code&gt; 语句）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello, world!&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;hello-world&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;多个 View&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;你可以一次返回多个 View，这种 View 叫做 &lt;code&gt;TupleView&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello, world!&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;hello-world&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一般这种 &lt;code&gt;TupleView&lt;/code&gt; 多用于 View 的排列。比如，将几个 View 在垂直方向依次排列：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;VStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello, world!&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;hello-world&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们将在下面进一步介绍 View 的排列。&lt;/p&gt;</description></item><item><title>数据流基础</title><link>https://www.clearsec.org/wikis/skills/frontend/swiftui/data-flow/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.clearsec.org/wikis/skills/frontend/swiftui/data-flow/</guid><description>&lt;p&gt;我们之前介绍的都是一些非常简单的情况：它们都不涉及变化的状态。然而，几乎所有的应用都需要依赖于变化的数据。我们需要一种方法，使得当数据更新时及时、正确地更新我们的 UI。我们将介绍 UI 与数据绑定过程中涉及的一些最基本的常见数据流。&lt;/p&gt;
&lt;h2 id="state"&gt;State&lt;/h2&gt;
&lt;p&gt;我们来看一个简单的例子。在我们的 View 中，需要记录用户点击的次数，并显示在屏幕上。你可能想要这样写：给 &lt;code&gt;ContentView&lt;/code&gt; 加上一个变量 &lt;code&gt;count&lt;/code&gt;，然后在 &lt;code&gt;Button&lt;/code&gt; 中更改这个值：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;ContentView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;some&lt;/span&gt; &lt;span class="n"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;HStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Click&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="bp"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;\(&lt;/span&gt;&lt;span class="bp"&gt;count&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但这样是不能正常工作的。&lt;/p&gt;
&lt;p&gt;首先，这个代码并不能通过编译：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Left side of mutating operator isn&amp;#39;t mutable: &amp;#39;self&amp;#39; is immutable
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;原因是，&lt;code&gt;struct&lt;/code&gt; 是值类型，一般的函数不能够修改其变量的值。&lt;/p&gt;
&lt;p&gt;而且，有一个更加深层的原因。我们改变了 &lt;code&gt;count&lt;/code&gt;，并且希望 &lt;code&gt;Text(&amp;quot;\(count)&amp;quot;)&lt;/code&gt; 能显示最新的值。但是，&lt;code&gt;ContentView&lt;/code&gt; 并不知道当 &lt;code&gt;count&lt;/code&gt; 更新时需要更新 UI。&lt;/p&gt;
&lt;p&gt;修改的方法很简单，只需要在 &lt;code&gt;var count: Int = 0&lt;/code&gt; 前加上 &lt;code&gt;@State&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样，就能够通过编译，并且我们的界面会随着 &lt;code&gt;count&lt;/code&gt; 的更新而更新了：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.clearsec.org/wikis/skills/static/frontend/swiftui/data-flow-state-1.gif" alt=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;@State&lt;/code&gt; 是什么语法？为什么能够修改 &lt;code&gt;count&lt;/code&gt;？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;@State&lt;/code&gt; 涉及到一种叫 &lt;a href="https://docs.swift.org/swift-book/LanguageGuide/Properties.html#ID617"&gt;property wrapper&lt;/a&gt; 的语法。简单来说，property wrapper 有如下特性：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;是一个包装后的类型，但对外的表现就像是被包装前的数据一样&lt;/li&gt;
&lt;li&gt;当被包装的值被设定或被读取时，执行特定的代码&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因为我们实际上是修改 &lt;code&gt;@State&lt;/code&gt; 所包装的值而非 &lt;code&gt;@State&lt;/code&gt; 变量本身，所以是可以修改 &lt;code&gt;count&lt;/code&gt; 的值的。&lt;/p&gt;</description></item><item><title>声明式 UI 与 MVVM</title><link>https://www.clearsec.org/wikis/skills/frontend/swiftui/declaritive-mvvm/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.clearsec.org/wikis/skills/frontend/swiftui/declaritive-mvvm/</guid><description>&lt;p&gt;以上是对 SwiftUI 基础内容的介绍。在你将来学习更加高级的内容之前，有必要对 SwiftUI 的 UI 构建作一个概念上的总览。我们将讨论两个核心概念：声明式 UI，以及 MVVM 架构。&lt;/p&gt;
&lt;h2 id="声明式-ui"&gt;声明式 UI&lt;/h2&gt;
&lt;p&gt;我们先来看两段代码。&lt;/p&gt;
&lt;p&gt;一段使用 SwiftUI：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello, world!&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foregroundColor&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="n"&gt;red&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;另一段使用 UIKit：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UILabel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Hello, world!&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;font&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;systemFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ofSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;textColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;systemRed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;它们都创建了一个带有一定样式的文本 UI。然而，它们的构建方式有着本质上的不同。对于 SwiftUI，我们&lt;strong&gt;描述&lt;/strong&gt; Text 及其字体、颜色等各种样式，而对于 UIKit，我们通过&lt;strong&gt;设定&lt;/strong&gt;属性值来更改。前者称为「声明式」（declarative），而后者称为「命令式」（imperative）。&lt;/p&gt;
&lt;p&gt;你可以从这两个词中体会到两种不同代码的显著区别。在 SwiftUI 中，我们不会使用“命令”语句来告诉一个 View “怎么做”，而是通过“声明”一个 View 以及它的结构、特征来告诉这个 View “做成怎样”。&lt;/p&gt;
&lt;p&gt;当 UI 的状态需要随着状态的变化而更新时，二者的优劣变得非常明显。&lt;strong&gt;命令式 UI 构建需要在每次状态更新时都通过一定的逻辑更新所有关联的 UI，而声明式 UI 构建只需要在一开始将所有可能的 UI 状态描述出来，就可以形成状态到 UI 的一个映射。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这两种方式的复杂程度差别有多大？作个简单的计算：假如我们的 UI 有 10 种可能的状态，那么命令式 UI 最多需要根据 &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mrow&gt;&lt;mo fence="true"&gt;(&lt;/mo&gt;&lt;mstyle displaystyle="true" scriptlevel="0"&gt;&lt;mfrac linethickness="0px"&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mfrac&gt;&lt;/mstyle&gt;&lt;mo fence="true"&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;45&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;\dbinom{10}{2} = 45&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class="katex-html" aria-hidden="true"&gt;&lt;span class="base"&gt;&lt;span class="strut" style="height:2.4em;vertical-align:-0.95em;"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen delimcenter" style="top:0em;"&gt;&lt;span class="delimsizing size3"&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist" style="height:1.3214em;"&gt;&lt;span style="top:-2.314em;"&gt;&lt;span class="pstrut" style="height:3em;"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="top:-3.677em;"&gt;&lt;span class="pstrut" style="height:3em;"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist" style="height:0.686em;"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose delimcenter" style="top:0em;"&gt;&lt;span class="delimsizing size3"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace" style="margin-right:0.2778em;"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace" style="margin-right:0.2778em;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut" style="height:0.6444em;"&gt;&lt;/span&gt;&lt;span class="mord"&gt;45&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 种不同的状态&lt;strong&gt;转移&lt;/strong&gt;定义来定义 UI 更新函数，而声明式 UI 最多不过需要定义 10 种不同状态下的 UI。更糟糕的是，一旦命令式 UI 的某一步 UI 更新出现问题，那么很可能接下来的更新都会导致错误的 UI。因此，当状态复杂到一定程度的时候，命令式 UI 的正确更新变得几乎不可能（回想一下你之前写过的命令式 UI，比如贵系同学们用 Qt 写过的大作业）。&lt;/p&gt;</description></item><item><title>后续拓展</title><link>https://www.clearsec.org/wikis/skills/frontend/swiftui/extensions/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.clearsec.org/wikis/skills/frontend/swiftui/extensions/</guid><description>&lt;p&gt;本文仅对 SwiftUI 中一些最基本的知识作了介绍，而这远远不够。要想开发一个完整的应用，你还需要学习更多内容。&lt;/p&gt;
&lt;p&gt;这是一些仅供参考的建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;经常探索&lt;a href="https://developer.apple.com/documentation/swiftui"&gt;官方文档&lt;/a&gt;，看看你能使用的工具有什么&lt;/li&gt;
&lt;li&gt;看看历年 &lt;a href="https://developer.apple.com/videos/topics/"&gt;WWDC&lt;/a&gt; 有什么视频讲解的内容是你需要的，关注每年的 WWDC&lt;/li&gt;
&lt;li&gt;不要惧怕英文内容，阅读英文文档是开发的必经之路&lt;/li&gt;
&lt;li&gt;学会用英文搜索，&lt;strong&gt;尽量不用中文搜索&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="概念性知识"&gt;概念性知识&lt;/h2&gt;
&lt;p&gt;这些概念性知识能够帮助你理解 SwiftUI 是如何运行的，进而帮助你写出效率更高、更加正确的程序。SwiftUI 本身并不复杂，但其原理需要深入的学习才能理解，而对原理的理解往往会在很大程度上影响程序的质量。&lt;/p&gt;
&lt;p&gt;这些话题非常重要：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/videos/play/wwdc2020/10037/"&gt;SwiftUI 生命周期&lt;/a&gt;：&lt;code&gt;App&lt;/code&gt;、&lt;code&gt;Scene&lt;/code&gt; 和 &lt;code&gt;View&lt;/code&gt; 之间的关系&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/videos/play/wwdc2020/10040/"&gt;SwiftUI 中的数据流&lt;/a&gt;：SwiftUI 中的数据流及其使用原则&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.bilibili.com/video/BV1q64y1d7x5?p=3"&gt;MVVM 架构&lt;/a&gt;：虽然我们已经简单介绍过，但你仍应听一听 CS193p 的这节课&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/videos/play/wwdc2021/10022/"&gt;View Identity&lt;/a&gt;：在底层，不同的 View 之间是如何区分的？&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="技术性知识"&gt;技术性知识&lt;/h2&gt;
&lt;p&gt;这些技术性的知识是一些“SwiftUI 能做什么”的集合，基本上是&lt;a href="https://developer.apple.com/documentation/swiftui"&gt;官方文档&lt;/a&gt;中你能用的所有 SwiftUI 现成工具，可以需要时再查阅，也可以无聊时随便翻翻。遇到问题时，&lt;a href="https://google.com"&gt;Google&lt;/a&gt;&lt;a href="https://stackoverflow.com"&gt;、Stackoverflow&lt;/a&gt; 和 &lt;a href="https://developer.apple.com/videos/swiftui-ui-frameworks"&gt;WWDC 视频&lt;/a&gt;是你可靠的助手。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/views-and-controls"&gt;原生组件&lt;/a&gt;：文字、图片、按钮等组件&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view-layout-and-presentation"&gt;排布&lt;/a&gt;：Stack、Grid、ScrollView、NavigationView 等&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/state-and-data-flow"&gt;数据流与状态&lt;/a&gt;：State、Binding、ObservedObject 等&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/drawing-and-animation"&gt;绘画与动画&lt;/a&gt;：绘制形状、添加动画&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/gestures"&gt;触控识别&lt;/a&gt;：识别用户触摸输入&lt;/li&gt;
&lt;li&gt;View modifier
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view-appearance"&gt;外观&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view-text-and-symbols"&gt;文字相关&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view-auxiliary-views"&gt;额外的 UI 部件&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view-layout"&gt;布局&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view-graphics-and-rendering"&gt;图像与渲染&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view-input-and-events"&gt;用户输入与事件&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/view-presentation"&gt;视图的展示&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;在 SwiftUI 中使用 UIKit
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.bilibili.com/video/BV1q64y1d7x5?p=15"&gt;CS193p&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/tutorials/SwiftUI/interfacing-with-uikit"&gt;官方教程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/documentation/swiftui/framework-integration"&gt;官方文档&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="其它相关知识"&gt;其它相关知识&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/videos/play/wwdc2019/722/"&gt;Combine 框架&lt;/a&gt;：为 SwiftUI 设计的异步事件管理框架&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.bilibili.com/video/BV1Mx411L7dS"&gt;UIKit 课程&lt;/a&gt;：CS193p 的 UIKit 完整课程&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>