// // MenstrualCalendarVC.swift // Twear // // Created by yangbin on 2021/12/31. // import UIKit import JTAppleCalendar import SwiftDate //import HandyJSON import RealmSwift enum MenstrualDateModel: String { case fertilePeriod = "fertilePeriod" case safetyPeriod = "safetyPeriod" case ovulationDay = "ovulationDay" case menstrualPeriod = "menstrualPeriod" case predict = "predict" case none = "none" } class MenstrualCalendarModel: Object { @Persisted var date: Date? = nil @Persisted var typeRaw: String = "safetyPeriod" @Persisted var newTypeRaw: String? // @Persisted var pos @Persisted var positionRaw: Int = 4 @Persisted var dateStr: String = "" @Persisted var love: Bool = false @Persisted var temperature: String = "--℃" @Persisted var whichDay: Int = 0 @Persisted var days: Int = 0 override class func primaryKey() -> String? { "dateStr" } //case left = 1, middle, right, full, none var newType: MenstrualDateModel? { get { if newTypeRaw == nil { return nil } else { return MenstrualDateModel(rawValue: newTypeRaw!) } } set { newTypeRaw = newValue?.rawValue } } var type: MenstrualDateModel? { get { return MenstrualDateModel(rawValue: typeRaw) } set { typeRaw = newValue?.rawValue ?? "safetyPeriod" } } var position: MenstrualRangePosition? { get { return MenstrualRangePosition(rawValue: positionRaw) } set { self.positionRaw = newValue?.rawValue ?? 4 } } convenience init(date: Date, type: MenstrualDateModel) { self.init() self.date = date self.typeRaw = type.rawValue } func add() { if let array = RealmTools.queryObjects(MenstrualCalendarModel.self, filter: "date = %@", [self.date ?? Date()]) as? [MenstrualCalendarModel] { RealmTools.deleteList(array) } RealmTools.add(self) } func update() { let model = MenstrualCalendarModel() model.date = self.date model.dateStr = self.dateStr model.type = self.type model.newType = self.newType model.position = self.position model.add() } class func getAllDate() -> [MenstrualCalendarModel] { guard let array = RealmTools.objectsWithPredicateAndSorted(object: self, sortedKey: "date") as? Array, array.count > 0 else { return [] } return array } class func getLastMenstrual() -> MenstrualCalendarModel? { guard let array = RealmTools.objectsWithPredicateAndSorted(object: self, sortedKey: "date", isAssending: false) as? Array, array.count > 0 else { return nil } for model in array { if model.newType == .menstrualPeriod { return model } if model.type == .menstrualPeriod { return model // break } } return nil } class func getDate(_ date: Date) -> MenstrualCalendarModel? { guard let array = RealmTools.objectsWithPredicateAndSorted(object: self, predicate: NSPredicate(format: "date = %@", argumentArray:[date]), sortedKey: "date") as? Array, array.count == 1 else { return nil } return array.first } } class MenstrualCalendarVC: UIViewController { @IBOutlet weak var calendarView: JTAppleCalendarView! @IBOutlet weak var menstrualSwitch: UIButton! @IBOutlet weak var loveSwitch: UIButton! @IBOutlet weak var dateLabel: UILabel! var menstrual: MenstrualModel = UserInfo.menstrual var selectedDate: Date = Date().dateAtStartOf(.day) var historyData = MenstrualCalendarModel.getAllDate() var setClosure: (() -> ())? @IBOutlet weak var temperatureLabel: UILabel! override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationController?.setNavigationBarHidden(false, animated: true) menstrual = UserInfo.menstrual calendarView.reloadData() calendarView.scrollToDate(Date(), animateScroll: false) selectedDate = Date().dateAtStartOf(.day) calendarView.selectDates([selectedDate]) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) setClosure?() } override func viewDidLoad() { super.viewDidLoad() title = LocString("经期显示") // menstrual.lastDate = Date()-1.days // menstrual.cycle = 34 // menstrual.days = 7 addHistoryData() calendarView.scrollingMode = .stopAtEachCalendarFrame calendarView.scrollDirection = .horizontal calendarView.showsHorizontalScrollIndicator = false calendarView.cellSize = (SCREEN_WIDTH-35)/7.0 // calendarView.allowsMultipleSelection = true // calendarView.isRangeSelectionUsed = true let setButton = UIButton(frame: CGRect(x: 0, y: 0, width: 40, height: 28)) setButton.setTitle(LocString("设置"), for: .normal) setButton.titleLabel?.font = BoldFont(18) setButton.titleLabel?.adjustsFontSizeToFitWidth = true setButton.setTitleColor(UIColor.rgbColorFromHex(0xEF3257), for: .normal) setButton.addTarget(self, action: #selector(clickSetButton), for: .touchUpInside) navigationItem.rightBarButtonItem = UIBarButtonItem(customView: setButton) } @objc func clickSetButton() { let vc = UIStoryboard.loadViewControllerIdentifier(storyboardName: "Home", identifier: "WomenHealthVC") as! WomenHealthVC vc.isFirst = false navigationController?.pushViewController(vc, animated: true) } func addHistoryData() { if historyData.count == 0 { let n: Int = (selectedDate - menstrual.lastDate!.dateAtStartOf(.day)).in(.day) ?? 0 for i in 0..<3*menstrual.cycle+abs(n+1) { let menstrualData = MenstrualCalendarModel() let date = selectedDate - i.days let result = getDateTypeAndPosition(date: date) menstrualData.date = date menstrualData.position = result.position menstrualData.type = result.type menstrualData.dateStr = date.toString(.custom("yyyy.MM.dd")) menstrualData.whichDay = result.whichDay menstrualData.days = result.days menstrualData.add() } } } func getDateTypeAndPosition(date: Date) -> (type: MenstrualDateModel, position: MenstrualRangePosition, whichDay: Int, days: Int) { var type: MenstrualDateModel = .safetyPeriod var position: MenstrualRangePosition = .none var days: Int = 0 var whichDay: Int = 0 var differDays: Int = (date - menstrual.lastDate!.dateAtStartOf(.day)).in(.day) ?? 0 differDays = differDays%menstrual.cycle var intervalDays = 0 if differDays < 0 { intervalDays = menstrual.cycle + differDays } else { intervalDays = differDays } if intervalDays < menstrual.days { //月经 whichDay = intervalDays+1 days = menstrual.days if date.isInFuture { type = .predict } else { type = .menstrualPeriod if menstrual.days == 1 { position = .full } else { if intervalDays == 0 { if date.isToday { position = .full } else if date.day == date.monthDays || date.weekday == 7 { position = .full } else { position = .left } } else if intervalDays == menstrual.days-1 { if date.day == 1 || date.weekday == 1 { position = .full } else { position = .right } } else { if date.day == date.monthDays || date.weekday == 7 { position = .right } else if date.day == 1 || date.weekday == 1 { position = .left } else { position = .middle } } } } } else if intervalDays > menstrual.cycle - 10 { //安全期 whichDay = intervalDays-menstrual.cycle+10 type = .safetyPeriod days = 9 } else if intervalDays > menstrual.cycle - 20 { //易孕期 whichDay = intervalDays-menstrual.cycle+20 days = min(menstrual.cycle-9-menstrual.days, 10) type = .fertilePeriod // if menstrual.cycle-19 == menstrual.cycle-10 { // cell.type = .ovulationDay // } if date.day == date.monthDays && date.weekday == 1 { position = .full } else if date.day == 1 && date.weekday == 7 { position = .full } else if intervalDays == menstrual.cycle-10 { //最后一天 if date.day == 1 || date.weekday == 1 { position = .full } else { position = .right } } else if intervalDays == menstrual.cycle-19 { //第一天 if date.day == date.monthDays || date.weekday == 7 { position = .full } else { position = .left } } else { if intervalDays == menstrual.cycle-14 { type = .ovulationDay } if date.day == date.monthDays || date.weekday == 7 { position = .right } else if date.day == 1 || date.weekday == 1 { position = .left } else { position = .middle } } } else { //安全期 whichDay = intervalDays-menstrual.days+1 days = menstrual.cycle-19-menstrual.days type = .safetyPeriod } return (type, position, whichDay, days) } @IBAction func menstrualChanged(_ sender: UIButton) { // print("??click?") sender.isSelected = !sender.isSelected sender.setImage(UIImage(named: sender.isSelected ? "menstrual_switch_true" : "menstrual_switch_false"), for: .normal) // let menstrualModel = MenstrualCalendarModel() if let model = MenstrualCalendarModel.getDate(selectedDate) { let previousModel = MenstrualCalendarModel.getDate(selectedDate-1.days) let nextModel = MenstrualCalendarModel.getDate(selectedDate+1.days) if model.newType != nil { RealmTools.updateWithTranstion { _ in model.newType = nil } if nextModel?.type == .menstrualPeriod { for i in 0.. MenstrualRangePosition? { guard let model = MenstrualCalendarModel.getDate(date), let previousModel = MenstrualCalendarModel.getDate(date-1.days) else { return nil } let nextModel = MenstrualCalendarModel.getDate(date+1.days) let previousType = getNewestType(previousModel) let dateType = getNewestType(model) if nextModel == nil { if dateType == .menstrualPeriod { if previousType == .menstrualPeriod { return .right } else { return .full } } else if dateType == .fertilePeriod || dateType == .ovulationDay { if previousType == .fertilePeriod || previousType == .ovulationDay { let t = getDateTypeAndPosition(date: selectedDate+1.days).type if t == .fertilePeriod || t == .ovulationDay { return .middle } else { return.right } } else { return .left } } } else { let nextType = getNewestType(nextModel!) if dateType == .menstrualPeriod { if previousType == .menstrualPeriod && nextType == .menstrualPeriod { return .middle } else if previousType == .menstrualPeriod { return .right } else if nextType == .menstrualPeriod { return .left } else { return .full } } else if dateType == .fertilePeriod || dateType == .ovulationDay { if (previousType == .fertilePeriod || previousType == .ovulationDay) && (nextType == .fertilePeriod || nextType == .ovulationDay) { if model.position == .left { return .middle } else { if date.day == 1 || date.weekday == 1 { return .left } else if date.day == date.monthDays || date.weekday == 7 { return .right } else { return .middle } } // if date.day == 1 || date.weekday == 1 { } else if (previousType == .fertilePeriod || previousType == .ovulationDay) && (nextType == .menstrualPeriod) { if model.position == .left { return .full } else if model.position == .middle { return .right } // return .right } else if (nextType == .fertilePeriod || nextType == .ovulationDay) && (previousType == .menstrualPeriod) { if model.position == .right { return .full } else if model.position == .middle { return .left } // return .left } else if nextType == .fertilePeriod || nextType == .ovulationDay { if model.position == .full { // if date.day == 1 || date.weekday == 1 { // return .full // } else { return .left // } } else if model.position == .right { return .middle } // return .left } else if previousType == .fertilePeriod || previousType == .ovulationDay { if model.position == .full { // if date.day == 1 || date.weekday == 1 { // return .full // } else { return .right // } } else if model.position == .left { return .middle } // return .left } else { return .full } } } return nil } func getNewestType(_ model: MenstrualCalendarModel) -> MenstrualDateModel { if model.newType == nil { return model.type! } else { return model.newType! } } @IBAction func selectMonth(_ sender: UIButton) { calendarView.scrollToSegment(sender.tag == 0 ? .previous : .next) } @IBAction func loveValueChanged(_ sender: UIButton) { sender.isSelected = !sender.isSelected if let model = MenstrualCalendarModel.getDate(selectedDate) { sender.setImage(UIImage(named: menstrualSwitch.isSelected ? "menstrual_switch_true" : "menstrual_switch_false"), for: .normal) RealmTools.updateWithTranstion { _ in model.love = sender.isSelected } } calendarView.reloadDates([selectedDate]) } @IBAction func setTemperature(_ sender: Any) { let view = TemperatureView() view.show() view.clickClosure = { [weak self] value in self?.updateTemp(value: value) } } func updateTemp(value: String) { if let model = MenstrualCalendarModel.getDate(selectedDate) { RealmTools.updateWithTranstion { _ in model.temperature = value } } calendarView.reloadDates([selectedDate]) } } extension MenstrualCalendarVC: JTAppleCalendarViewDelegate, JTAppleCalendarViewDataSource { func configureCalendar(_ calendar: JTAppleCalendarView) -> ConfigurationParameters { let formatter = DateFormatter() formatter.dateFormat = "yyyy MM dd" let startDate = formatter.date(from: "2020 11 01")! let endDate = formatter.date(from: "2025 01 01")! let params = ConfigurationParameters(startDate: startDate, endDate: endDate, numberOfRows: 6, calendar: Calendar.current, generateInDates: InDateCellGeneration.forAllMonths, generateOutDates: OutDateCellGeneration.tillEndOfGrid, firstDayOfWeek: .sunday, hasStrictBoundaries: false) return params } func configureCell(myCustomCell: JTAppleCell, cellState: CellState, date: Date, indexPath: IndexPath) { let cell = myCustomCell as! DateCell if cellState.dateBelongsTo == .thisMonth { cell.isHidden = false } else { cell.isHidden = true } cell.dayLabel.text = cellState.text if !date.isInFuture { if let menstrualData = MenstrualCalendarModel.getDate(date) { if menstrualData.newType != nil { cell.type = menstrualData.newType! } else { cell.type = menstrualData.type! } cell.position = menstrualData.position! cell.temp = menstrualData.temperature cell.love = menstrualData.love } else { if (DateInRegion().date - cellState.date).in(.day)! <= 3*menstrual.cycle { let menstrualData = MenstrualCalendarModel() let result = getDateTypeAndPosition(date: cellState.date) menstrualData.date = date menstrualData.position = result.position menstrualData.type = result.type menstrualData.dateStr = date.toString(.custom("yyyy.MM.dd")) menstrualData.whichDay = result.whichDay menstrualData.days = result.days menstrualData.add() cell.type = result.type cell.position = result.position if let prePosition = getHistoryDatePosition(date: selectedDate-1.days), let previousModel = MenstrualCalendarModel.getDate(selectedDate-1.days) { RealmTools.updateWithTranstion { _ in previousModel.position = prePosition } } } else { cell.type = .none cell.position = .none } cell.love = false cell.temp = "--℃" } } else { let result = getDateTypeAndPosition(date: cellState.date) cell.type = result.type cell.position = result.position cell.love = false cell.temp = "--℃" } cell.selectedView.isHidden = !cellState.isSelected cell.updateUI() handleCellSelected(cell: cell, cellState: cellState) } func calendar(_ calendar: JTAppleCalendarView, willDisplay cell: JTAppleCell, forItemAt date: Date, cellState: CellState, indexPath: IndexPath) { configureCell(myCustomCell: cell, cellState: cellState, date: date, indexPath: indexPath) handleCellSelected(cell: cell, cellState: cellState) } func calendar(_ calendar: JTAppleCalendarView, cellForItemAt date: Date, cellState: CellState, indexPath: IndexPath) -> JTAppleCell { let myCustomCell = calendar.dequeueReusableCell(withReuseIdentifier: "DateCell", for: indexPath) as! DateCell // cell.dayLabel.text = cellState.text configureCell(myCustomCell: myCustomCell, cellState: cellState, date: date, indexPath: indexPath) return myCustomCell } func calendar(_ calendar: JTAppleCalendarView, didScrollToDateSegmentWith visibleDates: DateSegmentInfo) { if let date = visibleDates.monthDates.first?.date { dateLabel.text = date.toString(.custom("yyyy-MM")) } } func calendar(_ calendar: JTAppleCalendarView, didSelectDate date: Date, cell: JTAppleCell?, cellState: CellState) { // guard let dateCell = cell as? DateCell else { return } selectedDate = date handleCellSelected(cell: cell, cellState: cellState) // calendarView.selectDates([date]) // print(date) } func calendar(_ calendar: JTAppleCalendarView, didDeselectDate date: Date, cell: JTAppleCell?, cellState: CellState) { handleCellSelected(cell: cell, cellState: cellState) } func handleCellSelected(cell: JTAppleCell?, cellState: CellState) { // cellState.isSelected = !cellState.isSelected if cellState.date.isInFuture { return } guard let dateCell = cell as? DateCell else { return } dateCell.selectedView.isHidden = !cellState.isSelected if cellState.isSelected { menstrualSwitch.isSelected = dateCell.type == .menstrualPeriod menstrualSwitch.setImage(UIImage(named: menstrualSwitch.isSelected ? "menstrual_switch_true" : "menstrual_switch_false"), for: .normal) // menstrual.type = menstrualSwitch.isSelected ? .menstrualPeriod loveSwitch.isSelected = dateCell.love loveSwitch.setImage(UIImage(named: loveSwitch.isSelected ? "menstrual_switch_true" : "menstrual_switch_false"), for: .normal) temperatureLabel.text = dateCell.temp } } }