我正在构建类似待办事项应用程序的东西,其中我在UITableView
中包含了 EXPANDABLE “ slave” UITableViewCell
(原因是“ expandable” slave的“材料设计” ”表)。也可能与此有关,它们都位于UIView
和UIScrollView
中嵌入的NavigationViewController
内的容器TabViewController
中。非常复杂...让我解释一下:
“硕士” UITableViewControler
,有2个部分(今年/长期),具有自定义标题和自定义TableViewCell
自定义TableViewCell
在内部具有UIView
和“从属” UITableView
-基础UIView
被限制为“从属” UITableView
并使其带有阴影的“材料”设计(cropToBounds
可以防止UITableView
上的阴影)
“从属” UITableView
应该只有一个扩展部分(我遵循这个人的逻辑:https://www.youtube.com/watch?v=ClrSpJ3txAs)–点击第一行隐藏/显示子视图和页脚
“从属” UITableView
具有3个自定义TableViewCell
(“标头”始终填充在第一行,“子任务”始终填充在第二行,并根据子任务的数量填充,“页脚”始终最后)
到目前为止,丑陋的UI图片可能会使其更加清晰:
我正在尝试尽可能多地使用Interface Builder(对我来说,作为一个初学者,它节省了大量代码,并使事情变得更清楚)。
从代码的角度来看,它有点复杂,因为我有一个“目标”领域对象,每次都有“子任务”对象列表。因此,“主” UITableViewController
数据源抓住了一个目标,并将其传递给了“主” TableViewCell
(GoalMainCell)cell.goal = goals?[indexPath.row]
,它是dataSource并为其出口“奴隶” UITableView
委托。这样,我可以使用领域中正确的子任务填充“从属” UITableView
。
当我试图让“主” UITableViewController
成为两个表的数据源和委托时,我无法正确填充子任务(即使为tableView.tag
的每一行都设置if…else
语句– indexPath.row
不能作为目标的索引,因为对于每个“从属” tableView.row
)它从0开始。
类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!
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的大小。换句话说:当我点击“ slave”表中的第一行时,Realm中的数据将被更新(“ expanded” bool属性),但是我必须终止应用程序并再次启动以设置布局(我认为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()
……或者也许使用这种体系结构不可能进行动画平滑表重装,因为这样会总是“重装”冲突...?希望那里有人可以帮助我。谢谢您的帮助!
答案 0 :(得分:0)
我解决了动画/更新。
1)我忘记更改.expanded
属性后重新加载数据:
扩展GoalsMainCell:UITableViewDelegate
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let goalNotificationInfo = ["index" : goalIndex ]
if indexPath.row == 0 {
if goal != nil {
do {
try realm.write {
goal!.expanded = !goal!.expanded
}
} catch {
print("Error saving done status, \(error)")
}
}
loadSubtasks()
tableView.deselectRow(at: indexPath, animated: true)
let section = IndexSet.init(integer: indexPath.section)
tableView.reloadSections(section, with: .none)
NotificationCenter.default.post(name: .goalNotKey, object: nil, userInfo: goalNotificationInfo as [AnyHashable : Any])
}
}
2)我将通知观察者添加到从表代表中,并调用.beginUpdate()
+ .endUpdate()
@objc func updateGoalSection(_ notification: Notification) {
tableView.beginUpdates()
tableView.endUpdates()
}
现在,更新可以平滑过渡了。在粗略解决方案中,这揭示了索引编制和主调用自动尺寸的另一个问题,但这是另一个主题...
希望它可以帮助其他人减少挣扎。
答案 1 :(得分:0)
在这种情况下,您可以考虑使用collectionView。装饰视图可以用作“材料设计”背景。这也可能会提高加载和滚动性能,因为要加载的表较少(只有一个集合),并且只能在一个类中调用reload。
在粗略收集中,视图需要花费更多的时间来建立。