我为 tableview 创建了一个删除按钮,通过平移手势滑动即可显示。我可以使用 UIContextualAction 来完成此操作,但我无法自定义按钮的外观。所以我决定...
我为 tableview 创建了一个删除按钮,通过平移手势滑动即可显示。我可以使用 UIContextualAction 来完成此操作,但我无法自定义按钮的外观。所以我决定自己制作按钮。我创建的平移手势功能也有效,但存在一个问题。当我按住单元格(长按)并将单元格向左滑动时,单元格会立即返回到之前的状态。我该如何防止这种情况发生?有两个与此主题相关的类,分别称为 TableViewCell 和 ViewController
这是我的 TableViewCell 类
import UIKit
import CoreData
protocol TableViewCellDelegate : AnyObject {
func didRequestDelete(_ cell: TableViewCell)
}
class TableViewCell: UITableViewCell {
let titleLabel = UILabel()
let noteLabel = UILabel()
let dateLabel = UILabel()
let timeLabel = UILabel()
let dateFormatter = DateFormatter()
let timeFormatter = DateFormatter()
let deleteButton = UIButton()
weak var delegate : TableViewCellDelegate?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
dateFormatter.dateFormat = "dd/MM/yyyy"
timeFormatter.dateFormat = "HH:mm"
selectionStyle = .none
setupDeleteButton()
contentView.layer.cornerRadius = 15
contentView.layer.borderWidth = 2
contentView.clipsToBounds = false
backgroundColor = UIColor.clear
backgroundView?.backgroundColor = UIColor.clear
selectedBackgroundView?.backgroundColor = UIColor.clear
setLabel(label: titleLabel, textColor: .systemGray5, fontSize: 22, numberOfLine: 0).isEnabled = false
setLabel(label: noteLabel, textColor: .systemGray5, fontSize: 18, numberOfLine: 3).isHidden = false
setLabel(label: dateLabel, textColor: .systemGray, fontSize: 14, numberOfLine: 1).isHidden = false
setLabel(label: timeLabel, textColor: .systemGray, fontSize: 14, numberOfLine: 1).isHidden = false
addContentView(views: [titleLabel,noteLabel,dateLabel,timeLabel])
let colorIndex = UserDefaults.standard.integer(forKey: "index")
setupBasedOnColors(index: colorIndex)
setupConstraints()
let leftSwipeGesture = UIPanGestureRecognizer(target: self, action: #selector(handleLeftSwipe(_:)))
contentView.addGestureRecognizer(leftSwipeGesture)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleCellTap(_:)))
tapGesture.delegate = self
contentView.addGestureRecognizer(tapGesture)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let pointForTargetView = deleteButton.convert(point, from: self)
if deleteButton.bounds.contains(pointForTargetView) {
return deleteButton
}
return super.hitTest(point, with: event)
}
func setupBasedOnColors(index: Int){
switch index {
case 0:
contentView.layer.borderColor = UIColor(red: 0.7, green: 0.7, blue: 0.7, alpha: 1).cgColor
case 1:
contentView.layer.borderColor = UIColor.systemPink.cgColor
case 2:
contentView.layer.borderColor = UIColor.systemGray3.cgColor
case 3:
contentView.layer.borderColor = UIColor.systemBlue.cgColor
case 4:
contentView.layer.borderColor = UIColor.systemYellow.cgColor
case 5:
contentView.layer.borderColor = UIColor(red: 0.3, green: 0.3, blue: 0.3, alpha: 1).cgColor
default:
print("Renk Bulunamadı")
}
contentView.setNeedsDisplay()
}
func setupDeleteButton(){
contentView.addSubview(deleteButton)
deleteButton.setImage(UIImage(systemName: "trash"), for: .normal)
deleteButton.layer.cornerRadius = 15
deleteButton.tintColor = .white
deleteButton.addTarget(self, action: #selector(deleteButtonTapped), for: .touchUpInside)
deleteButton.isUserInteractionEnabled = true
}
func setLabel(label: UILabel, textColor : UIColor, fontSize: CGFloat, numberOfLine: Int) -> UILabel {
label.textColor = textColor
label.font = UIFont.systemFont(ofSize: fontSize)
label.numberOfLines = numberOfLine
return label
}
func addContentView(views: [UILabel]){
views.forEach { view in
contentView.addSubview(view)
}
}
private func setupConstraints() {
titleLabel.translatesAutoresizingMaskIntoConstraints = false
noteLabel.translatesAutoresizingMaskIntoConstraints = false
dateLabel.translatesAutoresizingMaskIntoConstraints = false
timeLabel.translatesAutoresizingMaskIntoConstraints = false
deleteButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10),
deleteButton.leadingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 6),
deleteButton.topAnchor.constraint(equalTo: contentView.topAnchor),
deleteButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
deleteButton.widthAnchor.constraint(equalToConstant: 100),
noteLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 5),
noteLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
noteLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10),
noteLabel.bottomAnchor.constraint(equalTo: dateLabel.topAnchor, constant: -10),
dateLabel.topAnchor.constraint(equalTo: noteLabel.bottomAnchor, constant: 10),
dateLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
dateLabel.trailingAnchor.constraint(equalTo: timeLabel.leadingAnchor, constant: -15),
dateLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10),
timeLabel.topAnchor.constraint(equalTo: dateLabel.topAnchor),
timeLabel.leadingAnchor.constraint(equalTo: dateLabel.trailingAnchor, constant: 20),
timeLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -9)
])
}
func configure(with note: NoteText) {
titleLabel.text = note.title
noteLabel.text = note.text
if let date = note.date {
timeLabel.text = timeFormatter.string(from: date)
} else {
timeLabel.text = "No time"
}
if let date = note.date {
configureCellDateLabel(with: date)
} else {
dateLabel.text = "No date"
}
}
func configureCellDateLabel(with date: Date) {
let calendar = Calendar.current
if calendar.isDateInToday(date) {
dateLabel.text = "Today"
} else {
dateFormatter.dateFormat = "dd/MM/yyyy"
dateLabel.text = dateFormatter.string(from: date)
}
}
@objc func handleLeftSwipe(_ gesture: UIPanGestureRecognizer) {
let translation = gesture.translation(in: contentView)
if gesture.state == .changed {
let newPosition = max(translation.x, -(deleteButton.frame.width + 7))
if newPosition <= 0 {
contentView.frame.origin.x = newPosition
}
} else if gesture.state == .ended {
let shouldRevealButton = contentView.frame.origin.x < -deleteButton.frame.width / 2
UIView.animate(withDuration: 0.3) {
self.contentView.frame.origin.x = shouldRevealButton ? -(self.deleteButton.frame.width + 7) : 0
}
}
}
@objc func handleCellTap(_ gesture: UITapGestureRecognizer) {
if contentView.frame.origin.x != 0 {
UIView.animate(withDuration: 0.3) {
self.contentView.frame.origin.x = 0
}
}
}
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return contentView.frame.origin.x != 0
}
@objc func deleteButtonTapped(){
delegate?.didRequestDelete(self)
}
}
我尝试在 ViewController 类的 didSelectRowAt 函数中添加一些条件,但没有效果。
import UIKit
import CoreData
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, TableViewCellDelegate {
var context: NSManagedObjectContext!
let tableView = UITableView()
let createNoteButton = UIButton()
let cellSpacing : CGFloat = 6
var fetchedNotes: [NoteText] = []
var titleLabel = UILabel()
var selectedIndexPath: IndexPath?
var randomLabel = UILabel()
var randomColor = UIColor()
var randomBool = Bool()
let dateFormatter = DateFormatter()
let timeFormatter = DateFormatter()
lazy var optionsMarkItem : UIBarButtonItem = {
let item = UIBarButtonItem(image: UIImage(systemName: "gearshape.fill"), style: .plain, target: self, action: #selector(optionsButtonTapped))
item.tintColor = .systemYellow
return item
}()
//MARK: viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .black
setupTableView()
for family in UIFont.familyNames.sorted() {
print("\(family)")
for name in UIFont.fontNames(forFamilyName: family).sorted() {
print("== \(name)")
}
}
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
fatalError("Cannot retrieve app delegate")
}
context = appDelegate.persistentContainer.viewContext
setupCreateNoteButton()
randomLabel.isHidden = true
setupTitleLabel()
tableView.backgroundColor = .black
tableView.delegate = self
tableView.dataSource = self
fetchNotes()
navigationItem.rightBarButtonItem = optionsMarkItem
let longPressGestureButton = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPressCell))
let longPressGestureCell = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPressButton))
longPressGestureCell.minimumPressDuration = 0.2
longPressGestureButton.minimumPressDuration = 0.2
tableView.addGestureRecognizer(longPressGestureButton)
createNoteButton.addGestureRecognizer(longPressGestureCell)
let switchValue = UserDefaults.standard.bool(forKey: "mySwitchValue")
setupUIBasedOnSwitch(switchValue: switchValue)
}
// MARK: viewWillAppear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
fetchNotes()
tableView.delegate = self
tableView.dataSource = self
let switchValue = UserDefaults.standard.bool(forKey: "mySwitchValue")
setupUIBasedOnSwitch(switchValue: switchValue)
dateFormatter.dateFormat = "dd/MM/yyyy"
timeFormatter.dateFormat = "HH:mm"
let colorIndex = UserDefaults.standard.integer(forKey: "index")
setupBasedOnColors(index: colorIndex)
}
// MARK: UserDefaults get functions
func getIndexValue() -> Int{
let index = UserDefaults.standard.integer(forKey: "index")
return index
}
//MARK: Core Data Fonksiyonları
func fetchNotes() {
let request: NSFetchRequest<NoteText> = NoteText.fetchRequest()
do {
fetchedNotes = try context.fetch(request)
} catch {
print("Not çekme hatası: \(error)")
}
tableView.reloadData()
}
func addNoteWithTitle(_ title: String, text: String, date: Date, time: Date) {
let newNote = NoteText(context: context)
newNote.title = title
newNote.text = text
newNote.date = date
newNote.time = date
do {
try context.save()
fetchNotes() // Listeyi güncelle
} catch {
print("Not kaydetme hatası: \(error)")
}
}
func deleteNoteAtIndexPath(_ indexPath: IndexPath) {
let noteToDelete = fetchedNotes[indexPath.section]
context.delete(noteToDelete)
fetchedNotes.remove(at: indexPath.section)
do {
try context.save()
tableView.beginUpdates()
tableView.deleteSections([indexPath.section], with: .automatic)
tableView.endUpdates()
} catch {
print("Not silme hatası: \(error)")
}
}
//MARK: setup Fonksiyonları
func setupTableView() {
view.addSubview(tableView)
tableView.dataSource = self
tableView.delegate = self
tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
tableView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 5),
tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -50),
tableView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -5)
])
tableView.register(TableViewCell.self, forCellReuseIdentifier: "TableViewCell")
}
func setupCreateNoteButton() {
view.addSubview(createNoteButton)
let symbolConfiguration = UIImage.SymbolConfiguration(pointSize: 45, weight: .regular, scale: .default)
// "+" işaretini oluşturuyoruz ve konfigürasyonunu ekliyoruz
let plusSymbol = UIImage(systemName: "plus", withConfiguration: symbolConfiguration)
createNoteButton.setImage(plusSymbol, for: .normal)
createNoteButton.tintColor = .systemYellow // "+" işaretinin rengini sarı yapıyoruz
// Buton için içi boş çember oluşturuyoruz
createNoteButton.backgroundColor = .clear // Butonun arka planını transparan yapıyoruz
createNoteButton.layer.cornerRadius = 35 // Butonun yarıçapını ayarlıyoruz
createNoteButton.layer.borderWidth = 2 // Çerçeve genişliği
createNoteButton.layer.borderColor = UIColor.systemYellow.cgColor
createNoteButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
createNoteButton.heightAnchor.constraint(equalToConstant: 70),
createNoteButton.widthAnchor.constraint(equalToConstant: 70),
createNoteButton.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -20),
createNoteButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -55)
])
createNoteButton.addTarget(self, action: #selector(createNoteButtonTapped), for: .touchUpInside)
}
func setupTitleLabel() {
titleLabel.text = "TextCrypt"
titleLabel.font = UIFont(name: "TimesNewRomanPS-BoldMT", size: 27)
titleLabel.textColor = .white
navigationItem.titleView = titleLabel
}
func setupUIBasedOnSwitch(switchValue: Bool) {
if switchValue {
view.backgroundColor = .white
tableView.backgroundColor = .white
randomLabel.textColor = .black
titleLabel.textColor = .black
// randomColor = UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 1)
randomColor = .white
} else {
view.backgroundColor = .black
tableView.backgroundColor = .black
randomLabel.textColor = .white
titleLabel.textColor = .white
// randomColor = UIColor(red: 0.1, green: 0.1, blue: 0.1, alpha: 1)
randomColor = .black
}
}
private func resetCellSize(at indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
UIView.animate(withDuration: 0.2) {
cell.transform = CGAffineTransform.identity
}
}
}
private func resetButtonSize(){
UIView.animate(withDuration: 0.1) {
self.createNoteButton.transform = CGAffineTransform.identity
}
}
// MARK: @objc fonksiyonları
@objc func optionsButtonTapped() {
let settingsVC = SettingsController()
settingsVC.modalPresentationStyle = .fullScreen
settingsVC.mainVC = self
present(settingsVC, animated: true)
// settingsVC.dismissAction = { [weak self] in }
}
@objc func handleLongPressButton(gesture: UILongPressGestureRecognizer) {
if gesture.state == .began {
UIView.animate(withDuration: 0.1) {
self.createNoteButton.transform = CGAffineTransform(scaleX: 0.90, y: 0.90)
}
} else if gesture.state == .ended || gesture.state == .cancelled{
resetButtonSize()
}
}
@objc func handleLongPressCell(gesture: UILongPressGestureRecognizer) {
let point = gesture.location(in: tableView)
if let indexPath = tableView.indexPathForRow(at: point) {
switch gesture.state {
case .began:
if let cell = tableView.cellForRow(at: indexPath) {
UIView.animate(withDuration: 0.1) {
cell.transform = CGAffineTransform(scaleX: 0.97, y: 0.97)
}
}
case .ended, .changed:
resetCellSize(at: indexPath)
default:
break
}
}
}
// MARK: tavleView ayarları
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func numberOfSections(in tableView: UITableView) -> Int {
return fetchedNotes.count
}
func didRequestDelete(_ cell: TableViewCell) {
guard let indexPath = tableView.indexPath(for: cell) else {return}
self.deleteNoteAtIndexPath(indexPath)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as? TableViewCell else {
return UITableViewCell()
}
cell.delegate = self
cell.contentView.backgroundColor = self.randomColor
cell.contentView.layer.borderColor = self.createNoteButton.tintColor.cgColor
cell.titleLabel.textColor = self.randomLabel.textColor
cell.noteLabel.textColor = self.randomLabel.textColor
cell.deleteButton.backgroundColor = self.createNoteButton.tintColor
let note = fetchedNotes[indexPath.section]
cell.configure(with: note)
return cell
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let view = UIView()
view.backgroundColor = .clear
return view
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return cellSpacing
}
// MARK: didSelectRowAt
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.reloadRows(at: [indexPath], with: .none)
if let cell = tableView.cellForRow(at: indexPath) {
let animation = CABasicAnimation(keyPath: "transform.scale")
animation.fromValue = 1.0
animation.toValue = 0.92
animation.duration = 0.10
animation.autoreverses = true
animation.repeatCount = 1
cell.layer.add(animation, forKey: "bounce")
}
let noteDetailVC = NoteDetailViewController()
// Seçilen notu NoteDetailViewController'a geçir
let selectedNote = fetchedNotes[indexPath.section]
noteDetailVC.note = selectedNote
noteDetailVC.noteContent = selectedNote.text
noteDetailVC.titleContent = selectedNote.title
noteDetailVC.dateContent = selectedNote.date
noteDetailVC.timeContent = selectedNote.time
noteDetailVC.view.backgroundColor = self.view.backgroundColor
noteDetailVC.textView.backgroundColor = self.view.backgroundColor
noteDetailVC.titleLabel.textColor = self.randomLabel.textColor
noteDetailVC.textView.textColor = self.randomLabel.textColor
noteDetailVC.backMarkItem.tintColor = createNoteButton.tintColor
noteDetailVC.checkMarkItem.tintColor = createNoteButton.tintColor
noteDetailVC.redoButton.tintColor = createNoteButton.tintColor
noteDetailVC.undoButton.tintColor = createNoteButton.tintColor
noteDetailVC.encryptMarkItem.tintColor = createNoteButton.tintColor
noteDetailVC.specialButton.backgroundColor = createNoteButton.tintColor
noteDetailVC.dismissAction = { [weak self] in
// Kullanıcı notu tamamen silip geri döndüğünde ilgili notu sil
if let newNote = noteDetailVC.noteContent, newNote.isEmpty,
let newTitle = noteDetailVC.titleContent, newTitle.isEmpty {
// Not silme fonksiyonunu çağır
self?.deleteNoteAtIndexPath(indexPath)
} else {
// Not güncellendiyse veya değişiklik olmadıysa güncellemeleri kaydet
if let newNote = noteDetailVC.noteContent, !newNote.isEmpty,
let newTitle = noteDetailVC.titleContent, !newTitle.isEmpty {
// Yeni bir Core Data nesnesi oluştur ve kaydet
}
// Notları tekrar çekmek için fetchNotes çağrılabilir
self?.fetchNotes()
}
}
let navigationController = UINavigationController(rootViewController: noteDetailVC)
navigationController.modalPresentationStyle = .fullScreen
present(navigationController, animated: true)
}
}
在此输入