我正在使用此Ray Wenderlich tutorial作为正在从事的项目的指南。但是,我正在尝试将此模板扩展为包含子目录,子子目录,子子目录等。
例如,如果您单击主目录/主目录屏幕中的“ Candy Cane”单元格,它将带您进入具有表视图的“新”视图控制器(又称子目录屏幕),该控制器将显示各种糖果手杖供应商,以及它们的价格是昂贵还是便宜(这将是每行的字幕)。请参见下面的subCategory
... txt文件。
在这篇文章中,我交替使用类别和目录一词。
这是第一个陷阱。过去,我做了一个仅能做到这一点的项目,但是我还是通过许多情节提要板完成了该任务,并查看了我导航的每个子类别的控制器文件。从子类别屏幕切换到子类别屏幕等等,都是相同的原理。
我不想尝试广告:但是,具体来说,我正在使用的应用称为iEngineering,并且可以在AppStore上免费使用。请查看应用程序的库部分,因为我相信它将为理解我的最终目标提供更多帮助。请记住,当前版本的应用程序是使用无数个情节提要和视图控制器文件构建的,每次我在Xcode中运行模拟器时都必须构建该文件。无需下载应用程序即可查看显示库功能的应用程序屏幕截图。
这是第二个陷阱。我也有这个应用程序中的子子类别。例如,如果您在子类别屏幕中单击一个单元格,其中显示了甘蔗供应商和价格的列表,它将引导您转到新屏幕(子类别),以找到该特定供应商的所有不同的甘蔗糖销售。因此,在我下面的虚构文本文件subCandyCaneTextFile.txt
中,如果您单击带有标签“ Candy Cane King”的单元格,它将带您到一个显示“ Candy Cane King”作为导航标题的屏幕,并显示/加载列表(通过阅读文本文件导入),该列表显示了Candy Cane King提供的所有糖果棒。 我没有提供我认为属于子类别的此txt文件和其他txt文件。我希望大家在下问以下问题的根本/重点时,请记住这一点。
对于这个项目,我想远离使用带有多个视图控制器的多个故事板。这是因为每次我在模拟器中进行构建时,我以前的项目将花费约5至10分钟的时间进行编译/构建。因此,我试图在项目运行时为应用程序中的每个新/类别屏幕读取一个文本文件。我希望这会给我带来更快得多的运行时间(〜10秒)。
在下面的示例中,类别/主视图控制器通过读取txt文件candyTextFile.txt
我的问题:如何为子类别屏幕处理(转换|删除)旧数据和(加载|替换)新数据?我考虑过尝试将其作为Ray Wenderlich tutorial教程中的“细节视图控制器”屏幕;但是,我不确定如何导航到子子类别屏幕。因此,我认为我的另一种选择可能是一次又一次地使用UITableView(主视图控制器)重用一个视图控制器。我不确定执行此操作的正确方法,因为我对学习Swift和Xcode还是比较陌生。
此帖子的current project via Xcode概述。
在我的MasterViewController.swift中,
import UIKit
class MasterViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
// MARK: - Properties
@IBOutlet var tableView: UITableView!
@IBOutlet var searchFooter: SearchFooter!
var detailViewController: DetailViewController? = nil
var candies = [Candy]()
var filteredCandies = [Candy]()
let searchController = UISearchController(searchResultsController: nil)
// MARK: - View Setup
override func viewDidLoad() {
super.viewDidLoad()
// Setup the Search Controller
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search Candies"
navigationItem.searchController = searchController
definesPresentationContext = true
// Setup the Scope Bar
searchController.searchBar.scopeButtonTitles = ["All", "Chocolate","Hard", "Other"]
searchController.searchBar.delegate = self
// Setup the search footer
tableView.tableFooterView = searchFooter
setupArray()
if let splitViewController = splitViewController {
let controllers = splitViewController.viewControllers
detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
}
}
private func setupArray() {
if let filepath = Bundle.main.path(forResource: "candyTextFile", ofType: "txt") {
do {
let contents = try String(contentsOfFile: filepath)
let lines_separatedBy_n : [String] = contents.components(separatedBy: "\n")
let string = lines_separatedBy_n.map { String($0) }.joined(separator: ", ")
var lines_separatedBy_comma : [String] = string.components(separatedBy: ", ")
// I've put this in to remove the last bit of the file that was causing the count to be one too high.
// I'm guessing that's why you had something similar previously?
lines_separatedBy_comma.removeLast()
for (index, element) in lines_separatedBy_comma.enumerated() {
if index % 2 == 0 {
let newCategory = element
let newName = lines_separatedBy_comma[index + 1]
let newCandy = Candy(category: newCategory, name: newName)
candies.append(newCandy)
}
}
for candy in candies {
print("category: \(candy.category), name: \(candy.name)")
}
//("\ncandies: \(candies)")
} catch let error as NSError {
print(error.localizedDescription)
}
}
print("\ncandies: \(candies)")
}
override func viewWillAppear(_ animated: Bool) {
print("splitViewController!.isCollapsed: \(splitViewController!.isCollapsed)")
if splitViewController!.isCollapsed {
if let selectionIndexPath = tableView.indexPathForSelectedRow {
tableView.deselectRow(at: selectionIndexPath, animated: animated)
}
}
super.viewWillAppear(animated)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table View
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isFiltering() {
print("Is filtering")
searchFooter.setIsFilteringToShow(filteredItemCount: filteredCandies.count, of: candies.count)
return filteredCandies.count
}
print("Is not filtering")
searchFooter.setNotFiltering()
return candies.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let candy: Candy
if isFiltering() {
candy = filteredCandies[indexPath.row]
} else {
candy = candies[indexPath.row]
}
cell.textLabel!.text = candy.name
cell.detailTextLabel!.text = candy.category
return cell
}
// MARK: - Segues
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
if let indexPath = tableView.indexPathForSelectedRow {
let candy: Candy
if isFiltering() {
candy = filteredCandies[indexPath.row]
} else {
candy = candies[indexPath.row]
}
let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
controller.detailCandy = candy
controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
func filterContentForSearchText(_ searchText: String, scope: String = "All") {
filteredCandies = candies.filter({
(candy : Candy) -> Bool in
let doesCategoryMatch = (scope == "All") || (candy.category == scope)
if searchBarIsEmpty(){
return doesCategoryMatch
}else {
return doesCategoryMatch && candy.name.lowercased().contains(searchText.lowercased())
}
})
tableView.reloadData()
}
func searchBarIsEmpty() -> Bool {
return searchController.searchBar.text?.isEmpty ?? true
}
func isFiltering() -> Bool {
let searchBarScoperIsFiltering = searchController.searchBar.selectedScopeButtonIndex != 0
return searchController.isActive && (!searchBarIsEmpty() || searchBarScoperIsFiltering)
}
}
extension MasterViewController: UISearchBarDelegate {
// MARK: - UISearchBar Delegate
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
filterContentForSearchText(searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
}
}
extension MasterViewController: UISearchResultsUpdating {
// MARK: - UISearchBar Delegate
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
filterContentForSearchText(searchController.searchBar.text!, scope: scope)
}
}
在candyTextFile.txt中,
Chocolate, Chocolate Bar
Chocolate, Chocolate Chip
Chocolate, Dark Chocolate
Hard, Lollipop
Hard, Candy Cane
Hard, Jaw Breaker
Other, Caramel
Other, Sour Chew
Other, Gummi Bear
Other, Candy Floss
Chocolate, Chocolate Coin
Chocolate, Chocolate Egg
Other, Jelly Beans
Other, Liquorice
Hard, Toffee Apple
在subCandyCaneTextFile.txt中,
Cheap, Brachs
Expensive, Spangler
Expensive, Bobs
Cheap, Candy Cane King
Expensive, Jelly Belly
其他子类别文件:
在subDarkChocolateTextFile.txt中,
Cheap, Ghirardelli
Expensive, Dove
Expensive, Lindt
Cheap, Hersheys
Expensive, Hu Dark
在subLollipopTextFile.txt中,
Cheap, Zollipops
Cheap, YumEarth
Expensive, Dum Dums
谢谢大家的时间,以及大家可能提供的任何指导。非常感谢。
答案 0 :(得分:1)
您可以在同一故事板上重复使用viewDidAppear:
。给它分配一个diffs = df.ne(df2).reset_index().melt(id_vars=['index'])
>>> diffs[diffs['value'].eq(True)].iloc[:, :-1].reset_index(drop=True)
index variable
0 1 b
,然后可以从当前实例中创建DetailViewController
类的新实例。您可以保留新VC的索引,例如增加它的索引,并使用它例如从预定义的数组中获取下一个类别文件名并将其加载到Storyboard ID
在DetailViewController
类中:
viewDidLoad
编辑
如何使用索引
使用文件名声明数组:DetailViewController
在 var index = 0;
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let st = UIStoryboard(name: "Main", bundle: nil)
let vc = st.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
vc.index = self.index + 1;
self.navigationController?.pushViewController(vc, animated: true)
}
中使用它
let files = ["file1", "file2", "file3"]