使用Swift进行黑客攻击-挑战7
我知道,我至少要迟到一天,但是在贝尔格莱德,除了去看牙医,还有很多事情要做! -真的很难找到时间! 无论如何…
今天是第七个项目的挑战日 ! 不过,首先让我们回顾一下我们学到的所有很棒的东西:
- 使用Swift的
Data
类型进行JSON解码 - 使用
Codable
协议将数据转换为我们定义的Swift对象。 -
UITabBarController
,UIStoryboard
…
评论
- 从字符串创建
URL
可能会失败。 -
String
,Int
,[String]
等都符合Codable
。 - Swift的
Data
类型可以保存任何类型的二进制数据。 -
Codable
协议可以将Swift类型与JSON相互转换。 - 情节提要标识符可以让我们用代码创建情节提要视图控制器。
-
UITabBarController
能够存储多个视图控制器供用户选择。 -
UIStoryboard
可以从我们的包中加载情节UIStoryboard
并从那里创建视图控制器。 - Apple提供了一些内置的
UITabBarItem
类型,以供一般使用。 -
JSONDecoder
类型完成了将JSON转换为Swift值的艰苦工作。 - 我们可以直接从
URL
创建一个Data
实例。 - JSON是一种存储和发送数据的轻量级方法。
- 带有字幕的表格视图单元格显示两个不同的文本标签。
评论
当然,我只是在这里插入了正确的答案,但是,如果您有时间,请转到“利用Swift黑客”倡议的评论页面,然后看看其他答案。 许多人很坦率,但其他许多人却不……这就是保罗成为一位出色的老师的原因! 它给了您一些提示,但您必须从地下挖出金块!
挑战性
挑战1:使用UIBarButtonItem
右上角添加一个Credits按钮。 轻按此按钮后,显示警报,告知用户数据来自Whitehouse的We The People API
首先,让我们创建一个按钮。 在这里,我不得不再次感谢保罗,因为我不费吹灰之力就知道该去哪里,怎么写以及怎么做! 这是我放入viewDidLoad()
的代码:
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Credits", style: .plain, target: self, action: #selector(showCredits))
这将创建一个具有.plain
样式的按钮(即,仅带有文本的按钮),以self
(视图控制器)为目标,而作为操作,我们仍然需要编写一种方法。
这是showCredits
动作:
@objc func showCredits() {
let ac = UIAlertController(title: "Data source", message: "These petitions come from the \nWe The People API of the Whitehouse", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(ac, animated: true)
}
我承认,我想创建更复杂的东西,在完成其他两个挑战之后,我可能会再谈谈。 我的想法是添加警报控制器的另一个操作(因为文本标签相反,警报不直接支持链接),以便当用户单击它时,新的视图控制器将加载包含请愿书主要网站的webView。 这似乎在我的掌握范围内,但我现在有零时间…稍后再尝试!
在我撰写本文时,该应用已构建并加载。 真诚的说,“ OK”标题看起来相当……所以我将其替换为“谢谢!”,因为听起来更爱国!
挑战2:让用户过滤他们看到的请愿书。 这涉及创建第二个过滤项数组,其中仅包含与用户输入的字符串匹配的请愿书。 使用带有文本字段的UIAlertController
,让他们输入该字符串。
所以……这是一个艰难的广告。 我很着急,我想直接跳入代码并编写数十行,但让我们一次做一件事。 首先,我们仍然有可用的左栏按钮项插槽,因此让我们使用它。
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Filter", style: .plain, target: self, action: #selector(filterPetitions))
这看起来是一个好的开始。 现在,我需要复习如何将文本字段输入到警报控制器中,因此我将返回并查看Word Scrambler项目。
在这里插入20分钟!
我在这里,为您服务,这是我所取得的成就(虽然什么都没有,但至少是一个开始!)
var filteredPetitions = [Petition]()
这是已过滤项目的第二个数组,其中将包含与输入字符串匹配的请愿书。 然后,这是filterPetitions
动作。
@objc func filterPetitions() {
let ac = UIAlertController(title: "Filter petitions", message: "Type in to filter...", preferredStyle: .alert)
ac.addTextField()
let filterAction = UIAlertAction(title: "Filter", style: .default) {
[weak self, weak ac] _ in
guard let filterWord = ac?.textFields?[0].text else { return }
self?.showPetitions(for: filterWord)
}
ac.addAction(filterAction)
present(ac, animated: true)
}
func showPetitions(for filter: String) {
}
我创建了一个警报控制器,添加了一个文本字段,并编写了filterAction
以使用传入的单词来调用showPetitions(for:)
方法。 然后,我将操作添加到警报控制器并进行了介绍。
现在,我需要编写showPetitions(for :)方法…
我这样写:
func showPetitions(for filter: String) {
for petition in petitions {
if petition.title.contains(filter) {
filteredPetitions.append(petition)
} else {
continue
}
}
tableView.reloadData()
}
这应该将第二个数组附加到原始请求数组中的每个项目,该数组在标题中包含过滤字符串。 我写了else continue
,以防万一,但可能不需要,也可能不需要reloadData()
调用。 我不确定。 我担心的是接下来要发生的事情:我必须在表视图中显示实际数据,我猜应该在cellForRowAt
方法中完成,但是,如果这样做的话……
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let petition = petitions[indexPath.row]
let filteredPetition = filteredPetitions[indexPath.row]
if !filteredPetitions.isEmpty {
cell.textLabel?.text = filteredPetition.title
cell.detailTextLabel?.text = filteredPetition.body
} else {
cell.textLabel?.text = petition.title
cell.detailTextLabel?.text = petition.body
}
return cell
}
…如果第二个数组不为空,则试图加载它,但出现索引不足错误…
发生这种情况的原因是,在viewDidLoad
, filteredPetitions
数组为空。 因此,我应该延迟该属性的创建。 咱们试试吧。
…几分钟后…
没什么,将filteredPetition
属性放在控制流语句中没有帮助。 也就是说,应用程序加载了,过滤器显然起作用了,但是后来我无法加载结果……让我们用另一种方法再试一次……
……又过了几分钟……
看来我走错了方向…但是现在我必须离开,所以今晚我会继续…