UItableview Cell 随机或在滚动时更改数据和属性
Posted
技术标签:
【中文标题】UItableview Cell 随机或在滚动时更改数据和属性【英文标题】:UItableview Cell changes data and attributes randomly or while scrolling 【发布时间】:2020-07-01 15:23:06 【问题描述】:我已经尝试解决这个问题好几个小时了,我知道我的代码不是最好的,但我似乎无法确定问题所在。我有一个 NSmanagedobject 数组,其中包含属性。如果“isComplete”的属性为真,我希望单元格的背景颜色为绿色。我有一个自定义视图,它创建一个新的 nsmanaged 对象并将其添加到 tableview。默认情况下,它应该添加一个白色背景单元格。我知道有很多代码需要解释,但已经过去了几个小时,我只是不明白为什么我的 tableview 加载单元格数据和配置不正确。这是我的视图控制器,里面有 tableview。
import CoreData
import UIKit
var coreTasks: [NSManagedObject] = []
var taskAdditionView:TaskAdditionView!
let appDel : AppDelegate = UIApplication.shared.delegate as! AppDelegate
let context: NSManagedObjectContext = appDel.persistentContainer.viewContext
class HomeViewController: UIViewController
@IBOutlet var addTaskFunction: UIBarButtonItem!
@IBOutlet var homeTableView: UITableView!
var todaysdeadlineLabel: UILabel!
@IBAction func addTaskFunctions(_ sender: UIBarButtonItem)
animateIn()
addTaskFunction.isEnabled = false
func animateIn()
taskAdditionView = TaskAdditionView() // the view where i create a new nsmanaged object
view.addSubview(taskAdditionView)
view.bringSubviewToFront(taskAdditionView)
func tableviewsConstraints()
homeTableView.translatesAutoresizingMaskIntoConstraints = false
homeTableView.layer.cornerRadius = 4
homeTableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20).isActive = true
homeTableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20).isActive = true
homeTableView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 120).isActive = true
homeTableView.heightAnchor.constraint(equalToConstant: homeTableView.rowHeight * 3).isActive = true
homeTableView.layer.borderWidth = 0.5
homeTableView.layer.borderColor = UIColor
.black.cgColor
override func viewDidLoad()
super.viewDidLoad()
loadTasks()
tableviewsConstraints()
NotificationCenter.default.addObserver(self, selector: #selector(reloadTable), name: NSNotification.Name(rawValue: "reloadTableNotification"), object: nil)
homeTableView.delegate = self
homeTableView.dataSource = self
UNUserNotificationCenter.current().requestAuthorization(options: [.alert,.sound,.badge]) (didallow, error) in
@objc func reloadTable(notification:Notification)
animateOut()
DispatchQueue.main.async
self.homeTableView.reloadData()
self.addTaskFunction.isEnabled = true
print("reloadTable() fired!")
extension HomeViewController: UITableViewDelegate,UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return coreTasks.count
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let task = coreTasks[indexPath.row] // creating a new task from the already stored task depending on the indexpath.row if indexPath.row is 3 then the task is tasks[3]
let cell = tableView.dequeueReusableCell(withIdentifier: "taskCell") as! CustomCell // setting the identifier ( we have already set in the storyboard, the class of our cells to be our custom cell)
cell.setTask(task: task) // this changes the label and date text since an instance of the task contains both the task and the date
print("CellData Task :", task.value(forKey: "isComplete") as! Bool, task.value(forKey: "name") as! String)
if (task.value(forKey: "isComplete") as! Bool == true)
cell.labelsToYellow()
cell.backgroundColor = Colors.greencomplete
cell.selectionStyle = .none
return cell
// Leading Swipe Action
func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
let task = coreTasks[indexPath.row]
let complete = markComplete(at: indexPath)
if !(task.value(forKey: "isComplete") as! Bool)
return UISwipeActionsConfiguration(actions: [complete])
else
return UISwipeActionsConfiguration(actions: [])
// Mark as Completed Task
func markComplete(at: IndexPath) -> UIContextualAction
let df = DateFormatter()
df.dateFormat = "dd-MM-yyyy" // assigning the date format
let now = df.string(from: Date()) // extracting the date with the given format
let cell = homeTableView.cellForRow(at: at) as! CustomCell
let task = coreTasks[at.row]
let completeActionImage = UIImage(named: "AddTask")?.withTintColor(.white)
let action = UIContextualAction(style: .normal, title: "Complete") (action, view, completion) in
task.setValue(!(task.value(forKey: "isComplete") as! Bool), forKey: "isComplete")
self.homeTableView.cellForRow(at: at)?.backgroundColor = task.value(forKey: "isComplete") as! Bool ? Colors.greencomplete : .white
cell.backgroundColor = Colors.greencomplete
cell.labelsToYellow()
task.setValue("Finished " + now, forKey: "date")
do
try
context.save()
self.homeTableView.reloadData()
catch
print("Markcomplete save error")
// cell.displayIcons(task: task)
completion(true)
//action.image = #imageLiteral(resourceName: "AddTask")
action.image = completeActionImage
action.backgroundColor = Colors.greencomplete
return action
// Trailing Swipe Actions
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
let task = coreTasks[indexPath.row]
let important = importantAction(at: indexPath)
let delete = deleteAction(at: indexPath)
if task.value(forKey: "isComplete") as? Bool == true
return UISwipeActionsConfiguration(actions: [delete])
else
return UISwipeActionsConfiguration(actions: [delete,important])
// Delete Action
func deleteAction(at: IndexPath) -> UIContextualAction
// remove !!!! from coredata memory as well not just array
let deleteActionImage = UIImage(named: "Delete")?.withTintColor(.white)
let action = UIContextualAction(style: .destructive , title: "Delete") (action, view, completion) in
let objectToDelete = coreTasks.remove(at: at.row)
context.delete(objectToDelete)
self.homeTableView.deleteRows(at: [at], with: .automatic)
do
try
context.save()
catch
print("Problem while saving")
completion(true)
action.image = deleteActionImage
action.backgroundColor = Colors.reddelete
return action
func loadTasks()
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Tasks")
do
coreTasks = try context.fetch(request) as! [NSManagedObject]
print("loadTasks() fired!")
catch let error as NSError
print("Could not fetch. \(error), \(error.userInfo)")
这是我的任务添加视图
import UserNotifications
import UIKit
import CoreData
class TaskAdditionView: UIView
var importanceSegmentControl: CustomSegmentControl!
var headerLabel:UILabel!
var taskTextField: CustomTextField!
var submitButton:CustomButton!
var reminderSwitch: UISwitch!
var datePicker: UIDatePicker!
var dateSelected: Date?
var importanceValue: Int16 = 0
override init(frame: CGRect)
super.init(frame: frame)
// initiliaze the view like this TaskAdditionView()
required init?(coder aDecoder: NSCoder)
super.init(coder: aDecoder)
func setupSwitchPicker()
reminderSwitch = UISwitch()
reminderSwitch.transform = CGAffineTransform(rotationAngle: CGFloat.pi/2)
datePicker = UIDatePicker()
datePicker.minimumDate = Date()
datePicker.addTarget(self, action: #selector(pickingDate(sender:)), for: .valueChanged)
@objc func pickingDate(sender: UIDatePicker)
self.dateSelected = sender.date
print("Date selected: \(dateSelected)")
func setupLabel()
headerLabel = UILabel()
headerLabel?.text = "Add Task"
headerLabel.textAlignment = .center
headerLabel.textColor = .black
headerLabel?.font = UIFont(name: "AvenirNext-Bold", size: 30.0)
headerLabel?.backgroundColor = UIColor.clear
@objc func indexChanged(control : CustomSegmentControl)
// This all works fine and it prints out the value of 3 on any click
switch control.selectedIndex
case 0:
importanceValue = 0
print(importanceValue)
case 1:
importanceValue = 1
print(importanceValue)
case 2:
importanceValue = 2
print(importanceValue)
default:
break;
//Switch
// indexChanged for the Segmented Control
func setupSegmentControl()
importanceSegmentControl = CustomSegmentControl()
importanceSegmentControl.addTarget(self, action: #selector(indexChanged(control:)),for: UIControl.Event.valueChanged)
func setupButton()
let myAttributes = [ NSAttributedString.Key.font: UIFont(name: "AvenirNext-DemiBold", size: 18.0)! , NSAttributedString.Key.foregroundColor: UIColor.white ]
let myTitle = "Add"
let myAttributedTitle = NSAttributedString(string: myTitle, attributes: myAttributes)
submitButton = CustomButton()
submitButton.setAttributedTitle(myAttributedTitle, for: .normal)
submitButton.addTarget(self, action: #selector(submitFunction(sender:)), for: .touchUpInside)
// Submit Function
@objc func submitFunction(sender: CustomButton)
print("Worked")
submitButton.shake()
NotificationCenter.default.post(name: NSNotification.Name("reloadTableNotification") , object: nil)
addTask()
if (reminderSwitch.isOn)
setupNotification()
print(dateSelected)
NSLayoutConstraint.deactivate(self.constraints)
removeFromSuperview()
func setupTextField()
taskTextField = CustomTextField()
func setupConstraints()
setupLabel()
setupTextField()
setupSegmentControl()
setupButton()
setupSwitchPicker()
addSubview(headerLabel!)
addSubview(importanceSegmentControl!)
addSubview(taskTextField)
addSubview(submitButton)
reminderSwitch.transform = reminderSwitch.transform.rotated(by: -(.pi/2))
addSubview(reminderSwitch)
addSubview(datePicker)
override func didMoveToSuperview()
setupConstraints()
override func removeFromSuperview()
for view in self.subviews
view.removeFromSuperview()
NSLayoutConstraint.deactivate(self.constraints)
removeAllConstraintsFromView(view: self)
func addTask()
let df = DateFormatter()
df.dateFormat = "dd-MM-yyyy" // assigning the date format
let now = reminderSwitch.isOn ? "Deadline " + df.string(from: dateSelected!) : df.string(from: Date()) // extracting the date with the given format
print("Reminder Switch is ON: ", reminderSwitch.isOn)
// Adding a task to the array
let entity =
NSEntityDescription.entity(forEntityName: "Tasks",
in: context)!
let newTask = NSManagedObject(entity: entity, insertInto: context)
newTask.setValue(taskTextField.text!, forKey: "name")
newTask.setValue(false, forKey: "isComplete")
newTask.setValue(now, forKey: "date")
newTask.setValue(importanceValue, forKey: "importValue")
do
try
context.save()
coreTasks.append(newTask)
print("addTask() fired!")
catch
print("Problem while saving")
func setupNotification()
let currentDate = Date()
let interval = dateSelected?.timeIntervalSince(currentDate)
print(interval)
let notifcation = UNMutableNotificationContent()
notifcation.title = "Task Reminder"
notifcation.subtitle = taskTextField.text ?? "Empty"
notifcation.badge = 1
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: interval!, repeats: false)
let request = UNNotificationRequest(identifier: "taskReminder", content: notifcation, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
taskTextField.endEditing(true)
func removeAllConstraintsFromView(view: UIView) for c in view.constraints view.removeConstraint(c)
extension UIView
func removeAllConstraints()
let superViewConstraints = superview?.constraints.filter $0.firstItem === self || $0.secondItem === self ?? []
superview?.removeConstraints(superViewConstraints + constraints)
这是我的自定义表格视图单元格
import UIKit
import CoreData
class CustomCell: UITableViewCell
@IBOutlet var taskLabel: UILabel!
@IBOutlet var dateLabel: UILabel!
func setTask(task: NSManagedObject )
taskLabel.text = task.value(forKey: "name") as? String
dateLabel.text = task.value(forKey: "date") as? String
func labelsToYellow()
taskLabel.textColor = .white
dateLabel.textColor = .white
func labelsToBlack()
taskLabel.textColor = .black
dateLabel.textColor = .black
理想情况下,当我通过任务添加创建一个 nsmanaged 对象类型的新任务时。我的由 nsmanagedobject 数组填充的 tableview 应该将任务添加到背景颜色为白色的单元格中,并相应地添加任务标签。我有一个上下文操作,标记任务完成并使单元格背景变为绿色。奇怪的是,它在某个时候起作用了。现在随机有时任务单元格是用绿色背景创建的,有时标签是空白的,或者当我向下或向上滚动时,所有标签都会变成绿色。非常感谢您的帮助。
【问题讨论】:
【参考方案1】:我以前遇到过这个问题,因为 TableViewCells 被重复使用,所以无论是否为默认,您都需要确保设置背景。
因此,当您添加代码以将背景设置为绿色时,请添加 else 语句或在查询之前将单元格背景设置为白色/您的默认颜色Issue with UITableViewCells Repeating Content
【讨论】:
以上是关于UItableview Cell 随机或在滚动时更改数据和属性的主要内容,如果未能解决你的问题,请参考以下文章
UITableView Cell 不应该水平滚动 swift iOS