gpt4 book ai didi

ios - 可重用单元格和阵列的问题

转载 作者:行者123 更新时间:2023-11-28 15:43:23 24 4
gpt4 key购买 nike

我有闹钟应用程序。用户可以添加警报。如果我添加太多警报并向下滚动,我会看到

enter image description here

如果我尝试使用 UISwitch(仅按钮单元格),我将导致应用程序崩溃并出现错误:

Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 8 beyond bounds [0 .. 7]'

我认为是数组内部的问题。如何解决?

import UIKit

class MainAlarmViewController: UITableViewController{

var alarmDelegate: AlarmApplicationDelegate = AppDelegate()
var alarmScheduler: AlarmSchedulerDelegate = Scheduler()
var alarmModel: Alarms = Alarms()

override func viewDidLoad() {
super.viewDidLoad()
tableView.allowsSelectionDuringEditing = true
}

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)

alarmModel = Alarms()
tableView.reloadData()
//dynamically append the edit button
if alarmModel.count != 0 {
self.navigationItem.leftBarButtonItem = editButtonItem
}
else {
self.navigationItem.leftBarButtonItem = nil
}
//unschedule all the notifications, faster than calling the cancelAllNotifications func
//UIApplication.shared.scheduledLocalNotifications = nil

let cells = tableView.visibleCells
if !cells.isEmpty {
for i in 0..<cells.count {
if alarmModel.alarms[i].enabled {
(cells[i].accessoryView as! UISwitch).setOn(true, animated: false)
cells[i].backgroundColor = UIColor.white
cells[i].textLabel?.alpha = 1.0
cells[i].detailTextLabel?.alpha = 1.0
}
else {
(cells[i].accessoryView as! UISwitch).setOn(false, animated: false)
cells[i].backgroundColor = UIColor.groupTableViewBackground
cells[i].textLabel?.alpha = 0.5
cells[i].detailTextLabel?.alpha = 0.5
}
}
}
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}

// MARK: - Table view data source

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 90
}

override func numberOfSections(in tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
if alarmModel.count == 0 {
tableView.separatorStyle = UITableViewCellSeparatorStyle.none
}
else {
tableView.separatorStyle = UITableViewCellSeparatorStyle.singleLine
}
return alarmModel.count
}


override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if isEditing {
performSegue(withIdentifier: Id.editSegueIdentifier, sender: SegueInfo(curCellIndex: indexPath.row, isEditMode: true, label: alarmModel.alarms[indexPath.row].label, mediaLabel: alarmModel.alarms[indexPath.row].mediaLabel, mediaID: alarmModel.alarms[indexPath.row].mediaID, repeatWeekdays: alarmModel.alarms[indexPath.row].repeatWeekdays, enabled: alarmModel.alarms[indexPath.row].enabled, dateTime: alarmModel.alarms[indexPath.row].date))
}
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: Id.alarmCellIdentifier)
if (cell == nil) {
cell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: Id.alarmCellIdentifier)
}

//cell text
cell!.selectionStyle = .none
cell!.tag = indexPath.row
let alarm: Alarm = alarmModel.alarms[indexPath.row]
let amAttr: [String : Any] = [NSFontAttributeName : UIFont.systemFont(ofSize: 20.0)]
let str = NSMutableAttributedString(string: alarm.formattedTime, attributes: amAttr)
let timeAttr: [String : Any] = [NSFontAttributeName : UIFont.systemFont(ofSize: 45.0)]
str.addAttributes(timeAttr, range: NSMakeRange(0, str.length-2))
cell!.textLabel?.attributedText = str
cell!.detailTextLabel?.text = alarm.label
//append switch button
let sw = UISwitch(frame: CGRect())
sw.transform = CGAffineTransform(scaleX: 0.9, y: 0.9);

//tag is used to indicate which row had been touched
sw.tag = indexPath.row
sw.addTarget(self, action: #selector(MainAlarmViewController.switchTapped(_:)), for: UIControlEvents.touchUpInside)
if alarm.enabled {
sw.setOn(true, animated: false)
}
cell!.accessoryView = sw

//delete empty seperator line
tableView.tableFooterView = UIView(frame: CGRect.zero)

return cell!
}

@IBAction func switchTapped(_ sender: UISwitch) {
let index = sender.tag
alarmModel.alarms[index].enabled = sender.isOn
if sender.isOn {
print("switch on")
sender.superview?.backgroundColor = UIColor.white
alarmScheduler.setNotificationWithDate(alarmModel.alarms[index].date, onWeekdaysForNotify: alarmModel.alarms[index].repeatWeekdays, snoozeEnabled: alarmModel.alarms[index].snoozeEnabled, onSnooze: false, soundName: alarmModel.alarms[index].mediaLabel, index: index)
let cells = tableView.visibleCells
if !cells.isEmpty {
cells[index].textLabel?.alpha = 1.0
cells[index].detailTextLabel?.alpha = 1.0
}
}
else {
print("switch off")
sender.superview?.backgroundColor = UIColor.groupTableViewBackground
let cells = tableView.visibleCells
if !cells.isEmpty {
cells[index].textLabel?.alpha = 0.5
cells[index].detailTextLabel?.alpha = 0.5
}
alarmScheduler.reSchedule()
}
}


// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let index = indexPath.row
alarmModel.alarms.remove(at: index)
let cells = tableView.visibleCells
for cell in cells {
let sw = cell.accessoryView as! UISwitch
//adjust saved index when row deleted
if sw.tag > index {
sw.tag -= 1
}
}
if alarmModel.count == 0 {
self.navigationItem.leftBarButtonItem = nil
}

// Delete the row from the data source
tableView.deleteRows(at: [indexPath], with: .fade)
alarmScheduler.reSchedule()
}
}

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
let dist = segue.destination as! UINavigationController
let addEditController = dist.topViewController as! AlarmAddEditViewController
if segue.identifier == Id.addSegueIdentifier {
addEditController.navigationItem.title = "Add Alarm"
addEditController.segueInfo = SegueInfo(curCellIndex: alarmModel.count, isEditMode: false, label: "Alarm", mediaLabel: "bell", mediaID: "", repeatWeekdays: [], enabled: false, dateTime: Date())
}
else if segue.identifier == Id.editSegueIdentifier {
addEditController.navigationItem.title = "Edit Alarm"
addEditController.segueInfo = sender as! SegueInfo
}
}

@IBAction func unwindFromAddEditAlarmView(_ segue: UIStoryboardSegue) {
isEditing = false
}

}

最佳答案

当您遇到运行时错误时,您应该将引发错误的行作为问题的一部分。

标签是一种非常脆弱的方式来确定哪个单元格被窃听了。 (标签是做任何事情的一种非常脆弱的方式。几乎总有比使用标签更好的方法来查找 View 或找出有关 View 的信息。)

我为 UITableView 创建了一个简单的扩展,它允许您向 TableView 询问单元格中任何 View 的 IndexPath。编写一个按钮 IBAction 很容易,它使用扩展来确定哪个单元格被点击了。我建议重新编写代码以使用这种方法

扩展名:

public extension UITableView {

/**
This method returns the indexPath of the cell that contains the specified view
- Parameter view: The view to find.
- Returns: The indexPath of the cell containing the view, or nil if it can't be found
*/
func indexPathForView(_ view: UIView) -> IndexPath? {
let bounds = view.bounds
let center = CGPoint(x: bounds.midX, y: bounds.midY)
let viewCenter = self.convert(center, from: view)
let indexPath = self.indexPathForRow(at: viewCenter)
return indexPath
}
}

以及使用它的 IBAction:

@IBAction func buttonTapped(_ button: UIButton) {
if let indexPath = self.tableView.indexPathForView(button) {
print("Button tapped at indexPath \(indexPath)")
}
else {
print("Button indexPath not found")
}
}

整个项目都可以在 Github 上找到:

TableViewExtension project on GitHub

我不知道为什么 Apple 不在 UITableView 中构建像 indexPathForView 函数这样的东西。这似乎是一个普遍有用的功能。

关于ios - 可重用单元格和阵列的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43425506/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com