我正在使用这个Ray Wenderlich tutorial作为我正在进行的项目的指南。但是,我正在尝试扩展此模板以合并子目录、子目录、子子子目录等。
例如,如果您在主/主目录屏幕中单击“Candy Cane”单元格,它将带您进入一个带有表视图的“新”视图控制器(也称为子目录屏幕),该控制器将显示各种Candy Cane供应商及其价格是贵是便宜(这将是每行的副标题)。请参见subCategory
。。。下面是txt文件。
在这篇文章中,我使用了category和directory这两个词。
这是第一个陷阱。在过去,我做了一个这样的项目,但我通过许多情节串连板和视图控制器文件为每个我导航的子类别。同样的原则也适用于从子类别移动到子类别屏幕等等。
我不想做广告:但具体来说,我正在开发的应用程序名为iEngineering,它在AppStore上是免费的。请参阅应用程序的库部分,因为我相信它将为理解我的最终目标提供额外帮助。请记住,应用程序的当前版本是使用无数的脚本和视图控制器文件构建的,每次我在Xcode中运行模拟器时都必须构建这些文件。查看显示库功能的应用程序屏幕截图就足够了,无需下载应用程序。
这是第二个陷阱。我在这个应用程序中也有子类别等等。例如,如果单击子类别屏幕中显示甘蔗糖供应商和价格列表的单元格,它将导航到新屏幕(子类别)以显示特定供应商销售的所有不同甘蔗糖。因此,在下面的“my made up”文本文件中,subCandyCaneTextFile.txt
,如果您要单击标签为“Candy Cane King”的单元格,它将带您进入显示“Candy Cane King”作为导航标题的屏幕,并显示/加载一个列表(通过读取文本文件导入),其中显示Candy Cane King提供的所有Candy Cane。我没有提供这个和其他txt文件,我认为是子类别。我希望大家记住这一点,因为当我在下面问我问题的根源/核心时。
对于这个项目,我想从使用多个脚本和多个视图控制器。这是因为我以前的项目每次在模拟器中编译/构建都需要大约5到10分钟。因此,我试图在项目运行时读取应用程序中每个新/类别屏幕的文本文件。我希望这能让我跑得更快(大约10秒)。
在下面的示例中,category/main view控制器通过读取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
在subcollipoptextfile.txt中,
Cheap, Zollipops
Cheap, YumEarth
Expensive, Dum Dums
感谢你们的时间和你们能提供的任何指导。我非常感激。
最佳答案
您可以从同一个脚本中重用DetailViewController
。将Storyboard ID
分配给它,然后可以从当前实例创建DetailViewController
类的新实例。您可以保留新VC的索引,例如递增索引,并使用它从预定义数组中获取下一个类别文件名,并将其加载到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"]
使用它
if let filepath = Bundle.main.path(forResource: files[index], ofType: "txt")