ios - 在 UICollectionView 中找到值为“isUserInteractionEnabled == true”的第一个单元格
问题描述
大家好,首先我很抱歉,如果我的问题可能不是很清楚,我将提供任何澄清。
我有一个UIView
类(TimeSelectorView),其中包含一个UICollectionView
填充有var data: [Section<TimeSelModel>] = []
.
我插入了另一个名为的数组var reservationsData: [(Int, Int)] = []
,其中包含用户选择的单元格。
如果reservationsData
array
包含的值等于var data
array
,则有问题的单元格一定是不可点击的。
到目前为止一切正常,但我有一个问题..我想在第一个可用的单元格上设置一个初始选择,其值为isUserInteractionEnabled == true
如何找到在isUserInteractionEnabled == true
方法之外拥有的单元格的第一个可用索引
func collectionView (_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell?
我知道在方法init(frame: CGRect)
中,collectionView
尚未创建,所以我无法获取单元格索引,但此时我该如何解决?
我需要当用户打开应用程序时,他们可以看到第一个具有值的单元格可用的选择isUserInteractionEnabled == true
你能帮我理解这个吗?
时间模型.swift
import Foundation
struct Section<T> { let dataModel: [T] }
struct TimeSelModel {
let hour: Int
let minute: Int
var time: String { "\(hour):\(minute)" }
}
let dataSec0 = [
TimeSelModel(hour: 09, minute: 30),
TimeSelModel(hour: 10, minute: 00),
TimeSelModel(hour: 10, minute: 30),
TimeSelModel(hour: 11, minute: 00),
TimeSelModel(hour: 11, minute: 30),
TimeSelModel(hour: 15, minute: 00),
TimeSelModel(hour: 15, minute: 30),
TimeSelModel(hour: 16, minute: 00),
TimeSelModel(hour: 16, minute: 30),
TimeSelModel(hour: 17, minute: 00)
]
let dataSec1 = [
TimeSelModel(hour: 12, minute: 00),
TimeSelModel(hour: 12, minute: 30),
TimeSelModel(hour: 13, minute: 00),
TimeSelModel(hour: 14, minute: 00),
TimeSelModel(hour: 14, minute: 30),
TimeSelModel(hour: 17, minute: 30),
TimeSelModel(hour: 18, minute: 00),
TimeSelModel(hour: 18, minute: 30),
TimeSelModel(hour: 19, minute: 00)
]
TimeSelectorView.swift
private var data: [Section<TimeSelModel>] = []
private var reservationsData: [(Int, Int)] = []
private var listern: ListenerRegistration!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() -> Void {
fetchData()
getReservationData(for: .currentDay)
selectAvaiability()
}
private func fetchData() -> Void {
data = [Section(dataModel: dataSec0), Section(dataModel: dataSec1)] }
private func getSection() -> Int {
let interval1 = Date().checkTimeBetweenInterval(from: (9, 0), to: (11, 30))
let interval2 = Date().checkTimeBetweenInterval(from: (14, 30), to: (17, 0))
return interval1 || interval2 ? 0 : 1
}
private func selectAvaiability() -> Void {
let section = getSection()
// Select the first cell with time = or > to the current time and which does not have a value of isUserInteraceEnabled == false
//cv.selectItem(at: IndexPath(item: ????, section: section), animated: false, scrollPosition: [])
}
// MARK: - UICollectionView Datasource
// MARK: -
extension TimeSelView: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int { data.count }
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
data[section].dataModel.count }
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TimeSelCell.cellID, for: indexPath) as! TimeSelCell
var cellData = data[indexPath.section].dataModel
cell.dataModel = cellData[indexPath.item]
if isCurrentDate {
cell.status =
Date().checkTime(hour: cellData[indexPath.item].hour, minute: cellData[indexPath.item].minute) || Date().isClosingDay ||
isAReservations(data: (cellData[indexPath.item].hour, cellData[indexPath.item].minute)) ? .disabled : .enabled
}
return cell
}
private func isAReservations(data: (Int, Int)) -> Bool {
reservationsData.contains(where: { $0.0 == data.0 && $0.1 == data.1}) }
}
extension TimeSelView {
// MARK: Query Periods
// Gestione del periodo da considerare per la query
enum Periods {
case currentDay, future
var query: Query {
let calendar = Calendar.current
let components = calendar.dateComponents ([.year, .month, .day], from: Date ())
let startToday = calendar.date (from: components)!
let endToday = calendar.date (byAdding: .day, value: 1, to: startToday)!
let rootQuery = Service.Database.reservationRoot
switch self {
case .currentDay: return rootQuery.whereField("date", isGreaterThan: startToday).whereField("date", isLessThan: endToday)
case .future : return rootQuery.whereField("date", isGreaterThan: endToday)
}
}
}
// MARK: - Reservation Query
// Recuperiamo tutte le prenotazioni effettuate per un periodo specifico
private func getReservationsDataFromQuery(periods: Periods, completed: @escaping () ->()) -> Void {
// Effetuiamo una query in base al perido in cui ci troviamo ovvero giorno corrente oppure giorni futuri
// Aggiungiamo un ascoltatore per monitorare tutti i cambiamenti che vengono effettuati al database durante le prenotazioni degli uitenti in modo tale da mantenere aggiornata la collectionView
listern = periods.query.addSnapshotListener { (querySnapshot, error) in
guard error == nil else {
print("Errore durante il recupero delle prenotazioni \(error!.localizedDescription)")
return }
for document in querySnapshot!.documents {
// Una volta ottenuti i dati andiamo a recuperare solo i valori di orario che verranno aggiunti all'array che gestisce lo stato delle celle della collectionView
if let timestamp = document.data()["date"] as? Timestamp {
let hour = timestamp.dateValue().component(.hour)
let minute = timestamp.dateValue().component(.minute)
// Aggiunta dei dati all'array
self.prepareReservationsData((hour, minute))
completed()
}
}
}
}
// MARK: -
private func getReservationData(for periods: Periods) -> Void {
// Controlla lo stato dell'utente per rimuovere l'ascoltatore della query quando non abbiamo bisogno di interpellare il database altrimenti effettua la query
NotificationCenter.default.addObserver(forName:.AuthStateDidChange, object: Auth.auth(), queue: nil) { _ in
/// Service.currentUser == nil -> Rimuove l'ascoltatore
/// Service.currentUser != nil -> Effettua la query e ricalcola la collectionView con i nuovi dati
if Service.currentUser == nil { self.listern.remove() }
else {
self.getReservationsDataFromQuery(periods: periods) {
self.cv.reloadData()
}
}
}
}
private func prepareReservationsData( _ value: (Int, Int)) -> Void {
reservationsData += [(value.0, value.1)]
}
}
TimeSelectorCell.swift
class TimeSelCell: UICollectionViewCell {
static let cellID = "time.selector.cell.identifier"
private let minuteLbl = UILabel(font: .systemFont(ofSize: 13), textColor: .white, textAlignment: .center)
private let hourLbl = UILabel(font: .boldSystemFont(ofSize: 17), textColor: .white, textAlignment: .center)
// Tiene traccia dello stato della cella
/// "disabled" -> Le celle non sono utilizzabili e il valore all'interno è barrato
/// "enabled" -> Le celle sono utilizzabili per la prenotazione
enum Status { case disabled, enabled }
var dataModel: TimeSelModel! {
didSet {
hourLbl.text = String(format: "%02d", dataModel.hour)
minuteLbl.text = ":" + String(format: "%02d", dataModel.minute)
}
}
var status: Status {
didSet {
switch status {
case .disabled :
isUserInteractionEnabled = false
case .enabled :
isUserInteractionEnabled = true
}
hourLbl.attributedText = crossedOut(text: hourLbl.text!)
minuteLbl.attributedText = crossedOut(text: minuteLbl.text!)
contentView.alpha = isUserInteractionEnabled ? 1 : 0.5
}
}
override var isSelected: Bool {
didSet {
UIView.animate(withDuration: 0.3) {
self.backgroundColor = self.isSelected ? K.Colors.greenAlpha : .systemFill }
self.layer.borderColor = self.isSelected ? K.Colors.green.cgColor : UIColor.clear.cgColor
self.layer.borderWidth = self.isSelected ? 1 : 0
}
}
var isPreviousDate: Bool = false
var isCurrentDate: Bool = true
override init(frame: CGRect) {
self.status = .disabled
super.init(frame: frame)
backgroundColor = .systemFill
layer.cornerRadius = 3
contentView.addSubview(hourLbl)
contentView.addSubview(minuteLbl)
hourLbl.anchor(top: contentView.topAnchor, leading: contentView.leadingAnchor, bottom: contentView.centerYAnchor, trailing: contentView.trailingAnchor, padding: .init(top: 9, left: 0, bottom: -8, right: 0))
minuteLbl.anchor(top: contentView.centerYAnchor, leading: contentView.leadingAnchor, bottom: contentView.bottomAnchor, trailing: contentView.trailingAnchor, padding: .init(top: -5, left: 0, bottom: 7, right: 0))
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
}
private extension TimeSelCell {
func crossedOut(text: String) -> NSMutableAttributedString {
let attributeString: NSMutableAttributedString = NSMutableAttributedString(string: "\(text)")
attributeString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: isUserInteractionEnabled ? 0 : 1, range: NSMakeRange(0, attributeString.length))
return attributeString
}
}
解决方案
数据源的多个数组是非常糟糕的做法。
更好的模型是自定义结构
struct Slot {
let name : String
var isSaved = false
}
var slots = [Slot(name: "slot1"), Slot(name: "slot2"), Slot(name: "slot3"), Slot(name: "slot4"), Slot(name: "slot5"), Slot(name: "slot6")]
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { slots.count }
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: SlotCell.cellID, for: indexPath) as! SlotCell
let slot = slots[indexPath.item]
cell.lbl.text = slot.name
cell.isUserInteractionEnabled = !slot.isSaved
return cell
}
要找到第一个保存的项目,请询问模型,而不是视图
let firstSavedItem = slots.first{ $0.isSaved }
推荐阅读
- parallel-processing - 如何处理大文件?
- sql-server - 如何在 Azure SQL 中检查 UNIQUEIDENTIFIER 值的默认值?
- python-3.6 - 为什么我不能在这段代码中打印 x 的值?
- python - Python - 无法导入 sklearn,但适用于其他包
- bash - Shell 脚本:等待组中的任何进程完成
- python - QtDesigner 布局不拉伸(预览不同于 GUI)
- django - 在管理员中显示 django 模型属性
- sql - 如果列中存在某些值,则防止 SQL 用空值覆盖
- arrays - 为什么这会导致分段错误?
- javascript - 将 csv 文件转换为 javascript 对象