【问题标题】:Constructing a data model for Time Periods为时间段构建数据模型
【发布时间】:2019-09-23 03:18:10
【问题描述】:

我正在构建一个应用程序,该应用程序提供类似于遛狗的服务。遛狗的人可以上传他们可用的日期和时间。我没有让他们选择像 Mon Jan 1st 这样的实际日期,而是让他们选择一周中的任何一天以及他们可用的任何时间。

我遇到的问题是我不知道如何为其构建数据模型。

照片中的内容是带有一个单元格的 collectionView,在每个单元格中我显示了他们可以选择的可用日期和时间段。一周中的每一天都有相同的 7 个时间段供想要成为遛狗者的用户选择。

问题是,如果有人选择 Sun 6am-9am、12pm-3pm 和 6pm-9pm,但他们也选择 Mon 6am-9m,我如何构建可以区分日期和时间的数据模型。例如周日早上 6 点到 9 点和周一早上 6 点到 9 点,如何区分?这些时隙应该是双精度还是字符串?

这是我目前用于 collectionView 数据源和单元格的:

// the collectionView's data source
var tableData = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]

//cellForItem
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: availabilityCell, for: indexPath) as! AvailabilityCell

cell.clearCellForReuse()
cell.dayOfWeek = tableData[indexPath.item]

// inside the AvailabilityCell itself
var dayOfWeek: String? {
    didSet {

        dayOfWeekLabel.text = dayOfWeek
    }
}

func clearCellForReuse() {

    dayOfWeekLabel.text = nil
    // deselect whatever radio buttons were selected to prevent scrolling issues
}

为了进一步解释,最终会发生的情况是,当希望他们的狗走路的用户滚动查看谁可用时,如果他们滚动的日期和时间不在发帖人的任何日期和时间(Sun & Mon with the selected hours) 不可用,则他们的帖子不应出现在提要中,但如果是其中一天和这些时间之一,那么他们的帖子将出现在提要中(在示例中,如果有人在周日晚上 10 点滚动此帖子不应该出现)。数据模型中的任何内容都将与帖子当前滚动的日期和时间进行比较。我正在使用 Firebase 作为后端。

我想出的东西相当复杂,这就是为什么我需要更合理的东西。

class Availability {

    var monday: String?
    var tuesday: String?
    var wednesday: String?
    var thursday: String?
    var friday: String?
    var saturday: String?
    var sunday: String?

    var slotOne: Double? // sunday 6am-9am I was thinking about putting military hours  here that's why I used a double
    var slotTwo: Double? // sunday 9am-12pm
    var slotTwo: Double? // sunday 12pm-3pm
    // these slots would continue all through saturday and this doesn't seem like the correct way to do this. There would be 49 slots in total (7 days of the week * 7 different  slots per day)
}

我还考虑过将它们分成不同的数据模型,例如星期一类、星期二类等,但这似乎也不起作用,因为它们对于 collectionView 数据源都必须是相同的数据类型。

更新 在@rob 的回答中,他给了我一些见解,以便对我的代码进行一些更改。我还在消化它,但我仍然有几个问题。他发了cool project that shows his idea.

1- 由于我将数据保存到 Firebase 数据库,数据应该如何结构化才能保存?可能有多天时间相似。

2- 我仍然在思考 rob 的代码,因为我以前从未处理过时间范围,所以这对我来说很陌生。我仍然不知道要根据什么进行排序,尤其是回调内部的时间范围

// someone is looking for a dog walker on Sunday at 10pm so the initial user who posted their post shouldn't appear in the feed

let postsRef = Database().database.reference().child("posts")

postsRef.observe( .value, with: { (snapshot) in

    guard let availabilityDict = snapshot.value as? [String: Any] else { return }

    let availability = Availability(dictionary: availabilityDict)

    let currentDayOfWeek = dayOfTheWeek()

    // using rob;s code this compares the days and it 100% works
    if currentDayOfWeek != availability.dayOfWeek.text {

        // don't add this post to the array
        return
    }

    let currentTime = Calendar.current.dateComponents([.hour,.minute,.second], from: Date())

    // how to compare the time slots to the current time?
    if currentTime != availability.??? {
        // don't add this post to the array
        return
    }

    // if it makes this far then the day and the time slots match up to append it to the array to get scrolled
})

func dayOfTheWeek() -> String? {        
    let dateFormatter = NSDateFormatter()
    dateFormatter.dateFormat = "EEEE"
    return dateFormatter.stringFromDate(self)
}

【问题讨论】:

    标签: ios swift class datamodel


    【解决方案1】:

    有很多方法可以给猫剥皮,但我可能会将可用性定义为枚举日期和时间范围:

    struct Availability {
        let dayOfWeek: DayOfWeek
        let timeRange: TimeRange
    }
    

    您的星期几可能是:

    enum DayOfWeek: String, CaseIterable {
        case sunday, monday, tuesday, wednesday, thursday, friday, saturday
    }
    

    或者你也可以这样做:

    enum DayOfWeek: Int, CaseIterable {
        case sunday = 0, monday, tuesday, wednesday, thursday, friday, saturday
    }
    

    IntString 各有优劣。在 Firestore 基于 Web 的 UI 中,字符串表示形式更易于阅读。整数表示提供了更容易排序的潜力。

    您的时间范围:

    typealias Time = Double
    typealias TimeRange = Range<Time>
    
    extension TimeRange {
        static let allCases: [TimeRange] = [
            6 ..< 9,
            9 ..< 12,
            12 ..< 15,
            15 ..< 18,
            18 ..< 21,
            21 ..< 24,
            24 ..< 30
        ]
    }
    

    在与 Firebase 交互方面,它不了解枚举和范围,因此我定义了一个 init 方法和 dictionary 属性来映射到您可以与 Firebase 交换的 [String: Any] 字典:

    struct Availability {
        let dayOfWeek: DayOfWeek
        let timeRange: TimeRange
    
        init(dayOfWeek: DayOfWeek, timeRange: TimeRange) {
            self.dayOfWeek = dayOfWeek
            self.timeRange = timeRange
        }
    
        init?(dictionary: [String: Any]) {
            guard
                let dayOfWeekRaw = dictionary["dayOfWeek"] as? DayOfWeek.RawValue,
                let dayOfWeek = DayOfWeek(rawValue: dayOfWeekRaw),
                let startTime = dictionary["startTime"] as? Double,
                let endTime = dictionary["endTime"] as? Double
            else {
                return nil
            }
    
            self.dayOfWeek = dayOfWeek
            self.timeRange = startTime ..< endTime
        }
    
        var dictionary: [String: Any] {
            return [
                "dayOfWeek": dayOfWeek.rawValue,
                "startTime": timeRange.lowerBound,
                "endTime": timeRange.upperBound
            ]
        }
    }
    

    您还可以定义一些扩展以使其更易于使用,例如,

    extension Availability {
        func overlaps(_ availability: Availability) -> Bool {
            return dayOfWeek == availability.dayOfWeek && timeRange.overlaps(availability.timeRange)
        }
    }
    
    extension TimeRange {
        private func string(forHour hour: Int) -> String {
            switch hour % 24 {
            case 0:      return NSLocalizedString("Midnight", comment: "Hour text")
            case 1...11: return "\(hour % 12)" + NSLocalizedString("am", comment: "Hour text")
            case 12:     return NSLocalizedString("Noon", comment: "Hour text")
            default:     return "\(hour % 12)" + NSLocalizedString("pm", comment: "Hour text")
            }
        }
    
        var text: String {
            return string(forHour: Int(lowerBound)) + "-" + string(forHour: Int(upperBound))
        }
    }
    
    extension DayOfWeek {
        var text: String {
            switch self {
            case .sunday:    return NSLocalizedString("Sunday", comment: "DayOfWeek text")
            case .monday:    return NSLocalizedString("Monday", comment: "DayOfWeek text")
            case .tuesday:   return NSLocalizedString("Tuesday", comment: "DayOfWeek text")
            case .wednesday: return NSLocalizedString("Wednesday", comment: "DayOfWeek text")
            case .thursday:  return NSLocalizedString("Thursday", comment: "DayOfWeek text")
            case .friday:    return NSLocalizedString("Friday", comment: "DayOfWeek text")
            case .saturday:  return NSLocalizedString("Saturday", comment: "DayOfWeek text")
            }
        }
    }
    

    如果你不想使用Range,你可以将TimeRange定义为struct

    enum DayOfWeek: String, CaseIterable {
        case sunday, monday, tuesday, wednesday, thursday, friday, saturday
    }
    
    extension DayOfWeek {
        var text: String {
            switch self {
            case .sunday:    return NSLocalizedString("Sunday", comment: "DayOfWeek text")
            case .monday:    return NSLocalizedString("Monday", comment: "DayOfWeek text")
            case .tuesday:   return NSLocalizedString("Tuesday", comment: "DayOfWeek text")
            case .wednesday: return NSLocalizedString("Wednesday", comment: "DayOfWeek text")
            case .thursday:  return NSLocalizedString("Thursday", comment: "DayOfWeek text")
            case .friday:    return NSLocalizedString("Friday", comment: "DayOfWeek text")
            case .saturday:  return NSLocalizedString("Saturday", comment: "DayOfWeek text")
            }
        }
    }
    
    struct TimeRange {
        typealias Time = Double
    
        let startTime: Time
        let endTime: Time
    }
    
    extension TimeRange {
        static let allCases: [TimeRange] = [
            TimeRange(startTime: 6, endTime: 9),
            TimeRange(startTime: 9, endTime: 12),
            TimeRange(startTime: 12, endTime: 15),
            TimeRange(startTime: 15, endTime: 18),
            TimeRange(startTime: 18, endTime: 21),
            TimeRange(startTime: 21, endTime: 24),
            TimeRange(startTime: 24, endTime: 30)
        ]
    
        func overlaps(_ availability: TimeRange) -> Bool {
            return (startTime ..< endTime).overlaps(availability.startTime ..< availability.endTime)
        }
    }
    
    extension TimeRange {
        private func string(forHour hour: Int) -> String {
            switch hour % 24 {
            case 0:      return NSLocalizedString("Midnight", comment: "Hour text")
            case 1...11: return "\(hour % 12)" + NSLocalizedString("am", comment: "Hour text")
            case 12:     return NSLocalizedString("Noon", comment: "Hour text")
            default:     return "\(hour % 12)" + NSLocalizedString("pm", comment: "Hour text")
            }
        }
    
        var text: String {
            return string(forHour: Int(startTime)) + "-" + string(forHour: Int(endTime))
        }
    }
    
    struct Availability {
        let dayOfWeek: DayOfWeek
        let timeRange: TimeRange
    
        init(dayOfWeek: DayOfWeek, timeRange: TimeRange) {
            self.dayOfWeek = dayOfWeek
            self.timeRange = timeRange
        }
    
        init?(dictionary: [String: Any]) {
            guard
                let dayOfWeekRaw = dictionary["dayOfWeek"] as? DayOfWeek.RawValue,
                let dayOfWeek = DayOfWeek(rawValue: dayOfWeekRaw),
                let startTime = dictionary["startTime"] as? Double,
                let endTime = dictionary["endTime"] as? Double
            else {
                return nil
            }
    
            self.dayOfWeek = dayOfWeek
            self.timeRange = TimeRange(startTime: startTime, endTime: endTime)
        }
    
        var dictionary: [String: Any] {
            return [
                "dayOfWeek": dayOfWeek.rawValue,
                "startTime": timeRange.startTime,
                "endTime": timeRange.endTime
            ]
        }
    }
    
    extension Availability {
        func overlaps(_ availability: Availability) -> Bool {
            return dayOfWeek == availability.dayOfWeek && timeRange.overlaps(availability.timeRange)
        }
    }
    

    【讨论】:

    • 嗨,Rob,感谢您的详细回答。我刚睡醒。让我玩一下代码并尝试一下。如果我有任何问题,我会在几个小时内回复您。我已经知道你的方法有效??,我只需要吸收它。非常感谢!
    • 实际上我以前从未使用过多次,因为这是我第一次与他们互动,我必须做一些谷歌搜索才能理解它。
    • 我有 1 个问题和几个问题。首先是问题,当我尝试 c+p TimeRange 必须在具有“where”子句指定的约束的非专用泛型类型“Range”上声明约束扩展时出现错误。第一个问题是如何跟踪单元格中选择的时间?通过细胞重复使用,我将细胞擦干净,然后重新填充。我通常会在数据模型上使用 didSet,然后更新任何 ui 元素以反映它。我如何跟踪日期?第二个问题我必须添加一些代码,因为我仍然不知道如何排序
    • 对不起,我的意思是复制和粘贴。我在您发布的 TimeRange 代码块部分复制并粘贴了 typealias Time = Double 和其他所有内容。我首先在 Playgrounds 中尝试了它并得到了错误,然后我在一个项目中尝试了它并得到了同样的错误。
    • 仅供参考,请参阅github.com/robertmryan/DogWalking。如果您有更多问题,请在此处发布 issue,而不是在此处发布更多 cmets。
    猜你喜欢
    • 1970-01-01
    • 2019-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-08
    • 2019-07-06
    • 1970-01-01
    • 2020-05-31
    相关资源
    最近更新 更多