【发布时间】:2019-04-15 10:05:06
【问题描述】:
我正在构建类似于待办事项应用程序的东西,其中 EXPANDABLE “slave” UITableView 在“master” UITableViewCell 内(原因是“可扩展“slave”表的材料设计)。也许这一切都在容器UIView 内部UIScrollView 内嵌入NavigationViewController 和TabViewController 中也是相关的。相当复杂......让我解释一下:
“大师”
UITableViewControler有 2 个部分(今年/长期),带有自定义标题和自定义TableViewCell自定义
TableViewCell内部有一个UIView和“从属”UITableView- 底层UIView被限制为“从属”UITableView并使其成为带有阴影的“材质”设计(cropToBounds防止UITableView上的阴影)“Slave”
UITableView应该只有一个扩展部分(我遵循这个家伙的逻辑:https://www.youtube.com/watch?v=ClrSpJ3txAs)– 点击第一行隐藏/显示子视图和页脚“Slave”
UITableView有 3 个自定义TableViewCell(“header”始终填充在第一行,“subtask”从第二行开始并根据子任务的数量进行填充,“footer”始终在最后一行)
到目前为止丑陋的 UI 的图片可能会更清楚:
我正在尝试尽可能多地使用 Interface Builder(对于初学者来说,它可以节省大量代码并使事情更加清晰)。
代码明智的架构有点复杂,因为我有一个“目标”领域对象,每次都有一个“子任务”对象列表。因此,“主”UITableViewController 数据源获取一个目标并将其传递给“主”TableViewCell(GoalMainCell)cell.goal = goals?[indexPath.row],即其出口“从”UITableView 的数据源和代表。这样我就可以使用来自领域的正确子任务填充“奴隶”UITableView。
当我尝试让两个表的“主”UITableViewController 数据源和委托时,我无法正确填充子任务(即使为每个和一堆 if…else 语句设置 tableView.tag - indexPath.row 也可以' 不被视为目标的索引,因为它从 0 开始每个“奴隶”tableView.row)
类GoalsTableViewController:UITableViewController:(主tableviewcontroller)
let realm = try! Realm()
var goals: Results<Goal>?
var parentVc : OurTasksViewController?
var numberOfSubtasks: Int?
let longTermGoalsCount = 0
@IBOutlet var mainTableView: UITableView!
@IBOutlet weak var noGoalsLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
loadGoals()
}
override func viewDidAppear(_ animated: Bool) { // MUST be viewDidAppear
super.viewDidAppear(true)
parentVc = self.parent as? OurTasksViewController
}
func loadGoals() {
goals = realm.objects(Goal.self)
if goals?.count == 0 || goals?.count == nil { noGoalsLabel.isHidden = false }
tableView.reloadData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
if longTermGoalsCount > 0 {
//TODO: split goals to "this year" and "future" and show second section if future has some goals
return 2
} else {
return 1
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return goals?.count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "GoalMainCell", for: indexPath) as! GoalsMainCell
cell.goal = goals?[indexPath.row] //send goal in to the GoalMainCell controller
cell.layoutIfNeeded()
return cell
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "ThisYearGoalsHeaderTableViewCell") as! ThisYearGoalsHeaderTableViewCell
cell.layoutIfNeeded()
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "LongTermGoalsHeaderCell")!
cell.layoutIfNeeded()
return cell
}
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 44
}
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return UITableView.automaticDimension
}
override func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {
return 44
}
类GoalsMainCell:UITableViewCell(主表的自定义单元格)
@IBOutlet weak var goalsSlaveTableView: GoalsSlaveTableView!
let realm = try! Realm()
var subtasks: Results<GoalSubtask>?
var numberOfRows: Int = 0
var goal: Goal? { //goal passed from master tableView
didSet {
loadSubtasks()
}
}
func loadSubtasks() {
subtasks = goal?.subtasks.sorted(byKeyPath: "targetDate", ascending: true)
guard let expanded = goal?.expanded else {
numberOfRows = 1
return
}
if expanded {
numberOfRows = (subtasks?.count ?? 0) + 2
} else {
numberOfRows = 1
}
}
扩展GoalsMainCell:UITableViewDataSource(主表的自定义单元格)
func numberOfSections(in tableView: UITableView) -> Int { return 1 }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfRows
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "GoalsSlaveTableViewHeaderCell", for: indexPath) as! GoalsSlaveTableViewHeaderCell
cell.goalNameLabel.text = goal?.name ?? "No task added"
cell.layoutIfNeeded()
return cell
} else if indexPath.row > 0 && indexPath.row < numberOfRows - 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: "GoalsSlaveTableViewCell", for: indexPath) as! GoalsSlaveTableViewCell
cell.subTaskNameLabel.text = subtasks?[indexPath.row - 1].name //because first row is main goal name
cell.layoutIfNeeded()
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "GoalsSlaveTableViewFooterCell", for: indexPath) as! GoalsSlaveTableViewFooterCell
cell.layoutIfNeeded()
return cell
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 44
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 0
}
func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {
return 0
}
扩展GoalsMainCell:UITableViewDelegate(主表的自定义单元格)
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
if goal != nil {
do {
try realm.write {
goal!.expanded = !goal!.expanded
}
} catch {
print("Error saving done status, \(error)")
}
}
}
let section = IndexSet.init(integer: indexPath.section)
tableView.reloadSections(section, with: .none)
tableView.deselectRow(at: indexPath, animated: true)
}
类GoalsSlaveTableView:UITableView(从tableViewController)
override func layoutSubviews() {
super.layoutSubviews()
self.layer.cornerRadius = cornerRadius
}
override var intrinsicContentSize: CGSize {
self.layoutIfNeeded()
return self.contentSize
}
override var contentSize: CGSize {
didSet{
self.invalidateIntrinsicContentSize()
}
}
类GoalsSlaveTableViewCell:UITableViewCell(从表的单元格)
@IBOutlet weak var subTaskNameLabel: UILabel!
@IBOutlet weak var subTaskTargetDate: UILabel!
@IBOutlet weak var subTaskDoneImage: UIImageView!
class GoalsSlaveTableViewHeaderCell: UITableViewCell(从表的表头——实际上也是一个自定义单元格)
@IBOutlet weak var goalNameLabel: UILabel!
@IBOutlet weak var goalTargetDate: UILabel!
@IBOutlet weak var goalProgressBar: UIProgressView!
类GoalsSlaveTableViewFooterCell:UITableViewCell(和从属的页脚)
@IBAction func deleteGoalButtonTapped(_ sender: UIButton) {
print("Delete goal")
}
@IBAction func editGoalButtonTapped(_ sender: UIButton) {
print("Edit goal")
}
问题:如何在展开/折叠后调用带有动画(必要时用于两者)表视图的重新加载数据?
从外观上看,它的工作原理与我想要的差不多。唯一也可能是最棘手的事情是在展开/折叠时自动重新加载/调整“主”单元格和“从属”tableView 大小。换句话说:当我点击“从”表中的第一行时,Realm 中的数据会更新(“扩展”布尔属性),但我必须终止应用程序并再次启动以设置布局(我相信 viewDidLoad 必须跑步)。我只是用几个链接来获得灵感,但我发现 没有 可以解释 如何在运行时通过漂亮的动画在大小方面展开/折叠和调用重新加载:
Using Auto Layout in UITableView for dynamic cell layouts & variable row heights Is it possible to implement tableview inside tableview cell in swift 3?
Is it possible to add UITableView within a UITableViewCell
TableView inside tableview cell swift 3
TableView Automatic Dimension Tableview Inside Tableview
Reload a tableView inside viewController
作为一个初学者,我可能会犯一些非常简单的错误,比如在 IB 中缺少一些自动布局约束,因此调用 invalidateIntrinsicContentSize()... “重新加载”的冲突......?希望有人在那里帮助我。感谢您的帮助!
【问题讨论】:
标签: ios swift uitableview