使用Swift进行黑客攻击-挑战7

我知道,我至少要迟到一天,但是在贝尔格莱德,除了去看牙医,还有很多事情要做! -真的很难找到时间! 无论如何…

今天是第七个项目的挑战日 ! 不过,首先让我们回顾一下我们学到的所有很棒的东西:

  • 使用Swift的Data类型进行JSON解码
  • 使用Codable协议将数据转换为我们定义的Swift对象。
  • UITabBarControllerUIStoryboard

评论

  • 从字符串创建URL可能会失败。
  • StringInt[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
}

…如果第二个数组不为空,则试图加载它,但出现索引不足错误…

发生这种情况的原因是,在viewDidLoadfilteredPetitions数组为空。 因此,我应该延迟该属性的创建。 咱们试试吧。

…几分钟后…

没什么,将filteredPetition属性放在控制流语句中没有帮助。 也就是说,应用程序加载了,过滤器显然起作用了,但是后来我无法加载结果……让我们用另一种方法再试一次……

……又过了几分钟……

看来我走错了方向…但是现在我必须离开,所以今晚我会继续…