Swift教程

[SwiftUI 100天] WorldScramble · part1

本文主要是介绍[SwiftUI 100天] WorldScramble · part1,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
译自 Word Scramble: Introduction
更多内容,欢迎关注公众号 「Swift花园」
喜欢文章?不如点赞关注吧

介绍

这个项目会是又一个游戏,不过游戏的方式是我介绍更多 Swift 和 SwiftUI 知识的伎俩 🙊。这个游戏会向玩家展示一个随机的 8 字母单词,让玩家从中拼出更多单词。举个例子,如果开始的单词是 “alarming” ,那么玩家可以拼出 “alarm”, “ring”,“main” (可以重新排列字母) 等等。在这里, “alarming” 称为 “根单词”。

在这个过程中,你会用到 List, onAppear(), Bundle, fatalError() 等等。所有将在之后的 SwiftUI 开发中经常用到的技能。你还会实践@State, Alert, NavigationView 等,趁现在享受轻松的时光 —— 因为这是 100 天挑战中最后一个简单的项目了。

创建一个新的 Single View App 项目,名字叫 WordScramble 。你需要为这个工程下载一个文件,它包含一个叫 “start.txt” 的文件,稍后会用到。

言归正传,开始编码。

介绍 List —— 你的好伙伴

这一节请移步:

https://juejin.im/post/5e54a8d9f265da57434bb917

图标

从 app bundle 中加载资源

这一节请移步:

https://juejin.im/post/5e55d1a9f265da574b791240

图标

处理字符串

这一节请移步:

https://juejin.im/post/5e571b1e518825496038df91

图标

往单词列表添加东西

这个 app 的界面主要由三部分 SwiftUI 视图构成:一个 NavigationView用以展示根单词,一个 TextField 用来给玩家输入一个单词, 一个 List展示玩家已经输入的单词。

目前为止,每当用户输入一个单词到文本框,我们会自动把它添加到已经使用过的单词列表中。不过稍后我们会增加一些检验,确保单词没有被用过,并且的确能从根单词中生成,最重要的是,确实是一个有意义的单词而不是随机的字母组合。

让我们先从一些基础的开始,我们需要一个数组来存放已经用过的单词,一个根单词以及一个可以绑定到文本框的字符串。把下面三个属性添加到ContentView

@State private var usedWords = [String]()
@State private var rootWord = ""
@State private var newWord = ""复制代码

对于视图的 body ,我们从最简单的开始:一个以 rootWord 作为标题的 NavigationView ,里面用 VStack 放文本框和单词列表:

var body: some View {
    NavigationView {
        VStack {
            TextField("Enter your word", text: $newWord)

            List(usedWords, id: \.self) {
                Text($0)
            }
        }
        .navigationBarTitle(rootWord)
    }
}复制代码

通过把 usedWords 直接传给 List ,我们让 SwiftUI 为数组里的每一个单词创建一行,用单词本身唯一标识。如果 usedWords里有很多重复的话,这样做就会有问题。但是很快我们会解决这个问题。

运行程序,你会看到文本框看起来不是很好看 —— 它相对导航栏和列表甚至不是很清晰可见。幸运的是,我们可以利用 textFieldStyle() modifier 让 SwiftUI 在它周围绘制一个浅灰色的圆角边框,再在边缘加上一些 padding 以便它不会挨到屏幕边缘。为文本框加上下面两个 modifier :

.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()复制代码

样式看起来好多了,但是还有一个问题:虽然我们可以往文本框输入,但是没有地方可以提交输入的内容 —— 没有可以添加单词的方法。

为了解决这个问题,我们需要写一个新的方法,名字可以叫 addNewWord()

  1. newWord 全部小写化,移除空白字符
  2. 确保至少有 1 个字符的长度,否则就结束
  3. 把这个单词插入到 usedWords 数组的第 0 个位置
  4. newWord 重新设置回空的字符串

稍后我们会在步骤 2 和步骤 3 之间增加一些额外的校验,确保单词是被允许的,不过目前这个方法还算一目了然:

func addNewWord() {
    // 小写并且修剪单词,确保我们不会因为大小写的不同而重复
    let answer = newWord.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)

    // 如果字符串数量为 0 则退出
    guard answer.count > 0 else {
        return
    }

    // extra validation to come

    usedWords.insert(answer, at: 0)
    newWord = ""
}复制代码

当用户点击键盘上的 return 键时,我们希望调用addNewWord() ,在 SwiftUI 中,我们可以通过为文本框提供一个 on commit 闭包来实现这一点。我知道这听取来有点高级,不过实践上其实就是给TextField提供一个拖尾闭包,它会在 return 点击时被调用。

实际上,闭包的签名 —— 它接收的参数和返回值类型,跟我们刚刚写的 addNewWord()方法是匹配的,我们可以直接传入这个方法:

TextField("Enter your word", text: $newWord, onCommit: addNewWord)复制代码

运行 app ,你会看到事情开始工作:我们可以输入单词到文本框,点击 return ,然后单词出现在列表中:

addNewWord() 中我们之所以用 usedWords.insert(answer, at: 0) 是有原因的。如果我们用的是 append(answer) ,那么新的单词会出现列表的末尾,很有可能超出屏幕,但把新单词插入到数组的头部则可以自动滑入到列表头部,这样的设置更好。

在我们把标题放进导航视图前,我要对我们的布局做两个小改动。

首先,当我们调用 addNewWord() 时,它把用户输入的单词小写化,这样可以避免用户添加 “car”, “Car”,和 “CAR” 这种重复的单词。但是,实践上有一个地方会很奇怪:文本框会自动把用户输入的任何单词的首字母变成大写,而用户提交 “Car” 的时候会在列表中 看到 “car” 。

为了解决这个问题,我们可以禁用文本框的自动大写功能,用到又一个 modifier: autocapitalization(),把这一行加到文本框控件:

.autocapitalization(.none)复制代码

第二个要做的改动 (仅仅是因为我们可以做这件事),是用 Apple 的 SF Symbols 图标在每个单词的文本旁边显示这个单词的长度。SF Symbols 提供用圆表示的从 0 到 50 的数字, 全部以 “x.circle.fill” 的格式命名,也就是 1.circle.fill,20.circle.fill 。

在这个程序我们会向用户展示 8 字母的单词,所以如果他们重新排列一个新单词,最长也不会超过 8 个字母。因此,我们用 SF Symbols 的圆形数字是肯定没问题的 —— 因为我们知道所有可能的数字长度都可以覆盖到。

如果我们在一个 List 的行里用到第二个视图, SwiftUI 会为我们自动创建一个隐式的水平 stack ,以便视图在行里面并排在一起。也就是说,我们可以直接添加一个 Image(systemName:) 到 List 里:

List(usedWords, id: \.self) {
    Image(systemName: "\($0.count).circle")
    Text($0)
}复制代码

再次运行 app ,你会发现当你在文本框里输入单词,然后点击 return 时,新单词会滑入列表,并且带有长度标识。😁

我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~



这篇关于[SwiftUI 100天] WorldScramble · part1的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!