diff --git a/JZCalendarWeekView.xcodeproj/project.pbxproj b/JZCalendarWeekView.xcodeproj/project.pbxproj index 5268237..2365461 100644 --- a/JZCalendarWeekView.xcodeproj/project.pbxproj +++ b/JZCalendarWeekView.xcodeproj/project.pbxproj @@ -33,6 +33,8 @@ 8EF7EBCD20A55C8E00D5CEEA /* JZAllDayCorner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EF7EBCC20A55C8E00D5CEEA /* JZAllDayCorner.swift */; }; 8EF7EBCF20A55CF900D5CEEA /* JZAllDayHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EF7EBCE20A55CF900D5CEEA /* JZAllDayHeader.swift */; }; 8EF7EBD120A55D1000D5CEEA /* JZAllDayHeaderBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EF7EBD020A55D1000D5CEEA /* JZAllDayHeaderBackground.swift */; }; + F84EDF81218287F4009FCA08 /* JZWorkDay.swift in Sources */ = {isa = PBXBuildFile; fileRef = F84EDF80218287F4009FCA08 /* JZWorkDay.swift */; }; + F84EDF832182A505009FCA08 /* JZNonWorkingHoursBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = F84EDF822182A505009FCA08 /* JZNonWorkingHoursBackground.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -75,6 +77,8 @@ 8EF7EBCC20A55C8E00D5CEEA /* JZAllDayCorner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JZAllDayCorner.swift; sourceTree = ""; }; 8EF7EBCE20A55CF900D5CEEA /* JZAllDayHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JZAllDayHeader.swift; sourceTree = ""; }; 8EF7EBD020A55D1000D5CEEA /* JZAllDayHeaderBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JZAllDayHeaderBackground.swift; sourceTree = ""; }; + F84EDF80218287F4009FCA08 /* JZWorkDay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JZWorkDay.swift; sourceTree = ""; }; + F84EDF822182A505009FCA08 /* JZNonWorkingHoursBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JZNonWorkingHoursBackground.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -135,6 +139,7 @@ 8E023C83206C732300C523BE /* JZBaseEvent.swift */, 8E40B2DF20B65C4600FFEB68 /* JZAllDayEvent.swift */, 8E1BA969206B5350007BE13C /* JZWeekViewHelper.swift */, + F84EDF80218287F4009FCA08 /* JZWorkDay.swift */, 8E1BA95C206B4AD9007BE13C /* ReusableViews */, 8E1BA955206B4687007BE13C /* Utils */, 8E1BA94D206B4329007BE13C /* JZCalendarWeekView.h */, @@ -167,6 +172,7 @@ 8EF7EBCC20A55C8E00D5CEEA /* JZAllDayCorner.swift */, 8EF7EBCE20A55CF900D5CEEA /* JZAllDayHeader.swift */, 8EF7EBD020A55D1000D5CEEA /* JZAllDayHeaderBackground.swift */, + F84EDF822182A505009FCA08 /* JZNonWorkingHoursBackground.swift */, ); path = ReusableViews; sourceTree = ""; @@ -298,9 +304,11 @@ 1FB6140D2130FE7400F08ACD /* JZCurrentTimelinePage.swift in Sources */, 8EF7EBCF20A55CF900D5CEEA /* JZAllDayHeader.swift in Sources */, 8E1BA95B206B4826007BE13C /* JZWeekViewFlowLayout.swift in Sources */, + F84EDF832182A505009FCA08 /* JZNonWorkingHoursBackground.swift in Sources */, 8E40B2E020B65C4600FFEB68 /* JZAllDayEvent.swift in Sources */, 8E1BA968206B4F40007BE13C /* JZCurrentTimelineSection.swift in Sources */, 8E1BA960206B4B84007BE13C /* JZColumnHeader.swift in Sources */, + F84EDF81218287F4009FCA08 /* JZWorkDay.swift in Sources */, 8E023C84206C732300C523BE /* JZBaseEvent.swift in Sources */, 8E1BA964206B4E82007BE13C /* JZCornerHeader.swift in Sources */, 8E1BA96E206B5B55007BE13C /* JZRowHeaderBackground.swift in Sources */, diff --git a/JZCalendarWeekView/JZBaseWeekView.swift b/JZCalendarWeekView/JZBaseWeekView.swift index ef2bff8..eabe9b6 100644 --- a/JZCalendarWeekView/JZBaseWeekView.swift +++ b/JZCalendarWeekView/JZBaseWeekView.swift @@ -43,6 +43,7 @@ open class JZBaseWeekView: UIView { } } public var firstDayOfWeek: DayOfWeek? + public var workDayForDate: ((_ date: Date) -> JZWorkDay?)? public var allEventsBySection: [Date: [JZBaseEvent]]! { didSet { self.isAllDaySupported = allEventsBySection is [Date: [JZAllDayEvent]] @@ -100,7 +101,7 @@ open class JZBaseWeekView: UIView { // decoration flowLayout.registerDecorationViews([JZColumnHeaderBackground.self, JZRowHeaderBackground.self, - JZAllDayHeaderBackground.self, JZAllDayCorner.self]) + JZAllDayHeaderBackground.self, JZAllDayCorner.self, JZNonWorkingHoursBackground.self]) flowLayout.register(JZGridLine.self, forDecorationViewOfKind: JZDecorationViewKinds.verticalGridline) flowLayout.register(JZGridLine.self, forDecorationViewOfKind: JZDecorationViewKinds.horizontalGridline) } @@ -541,6 +542,11 @@ extension JZBaseWeekView: WeekViewFlowLayoutDelegate { return date! } + public func collectionView(_ collectionView: UICollectionView, layout: JZWeekViewFlowLayout, workDayForSection section: Int) -> JZWorkDay? { + let date = Calendar.current.date(byAdding: .day, value: section, to: initDate) + return self.workDayForDate?(date!) + } + public func collectionView(_ collectionView: UICollectionView, layout: JZWeekViewFlowLayout, startTimeForItemAtIndexPath indexPath: IndexPath) -> Date { let date = flowLayout.dateForColumnHeader(at: indexPath) diff --git a/JZCalendarWeekView/JZWeekViewFlowLayout.swift b/JZCalendarWeekView/JZWeekViewFlowLayout.swift index 397347e..3488cf3 100644 --- a/JZCalendarWeekView/JZWeekViewFlowLayout.swift +++ b/JZCalendarWeekView/JZWeekViewFlowLayout.swift @@ -9,6 +9,8 @@ public protocol WeekViewFlowLayoutDelegate: class { /// Get the date for given section func collectionView(_ collectionView: UICollectionView, layout: JZWeekViewFlowLayout, dayForSection section: Int) -> Date + /// Get the work day for given section + func collectionView(_ collectionView: UICollectionView, layout: JZWeekViewFlowLayout, workDayForSection section: Int) -> JZWorkDay? /// Get the start time for given item indexPath func collectionView(_ collectionView: UICollectionView, layout: JZWeekViewFlowLayout, startTimeForItemAtIndexPath indexPath: IndexPath) -> Date /// Get the end time for given item indexPath @@ -67,6 +69,7 @@ open class JZWeekViewFlowLayout: UICollectionViewFlowLayout { var itemAttributes = AttDic() var columnHeaderAttributes = AttDic() var columnHeaderBackgroundAttributes = AttDic() + var nonWorkingHoursBackgroundAttributes = AttDic() var rowHeaderAttributes = AttDic() var rowHeaderBackgroundAttributes = AttDic() var verticalGridlineAttributes = AttDic() @@ -174,6 +177,7 @@ open class JZWeekViewFlowLayout: UICollectionViewFlowLayout { allAttributes.append(contentsOf: columnHeaderBackgroundAttributes.values) allAttributes.append(contentsOf: rowHeaderAttributes.values) allAttributes.append(contentsOf: rowHeaderBackgroundAttributes.values) + allAttributes.append(contentsOf: nonWorkingHoursBackgroundAttributes.values) allAttributes.append(contentsOf: verticalGridlineAttributes.values) allAttributes.append(contentsOf: horizontalGridlineAttributes.values) allAttributes.append(contentsOf: cornerHeaderAttributes.values) @@ -288,6 +292,7 @@ open class JZWeekViewFlowLayout: UICollectionViewFlowLayout { attributes.frame = CGRect(x: sectionMinX, y: columnHeaderMinY, width: sectionWidth, height: columnHeaderHeight) attributes.zIndex = zIndexForElementKind(JZSupplementaryViewKinds.columnHeader) + layoutNonWorkingHoursBackgroundAttributes(section: section, sectionX: sectionMinX, calendarGridMinY: calendarGridMinY, sectionHeight: sectionHeight) layoutVerticalGridLinesAttributes(section: section, sectionX: sectionMinX, calendarGridMinY: calendarGridMinY, sectionHeight: sectionHeight) layoutItemsAttributes(section: section, sectionX: sectionMinX, calendarStartY: calendarGridMinY) } @@ -331,6 +336,48 @@ open class JZWeekViewFlowLayout: UICollectionViewFlowLayout { currentSectionZ: zIndexForElementKind(JZSupplementaryViewKinds.eventCell)) } + func layoutNonWorkingHoursBackgroundAttributes(section: Int, sectionX: CGFloat, calendarGridMinY: CGFloat, sectionHeight: CGFloat) { + var attributes = UICollectionViewLayoutAttributes() + + let workDay = delegate?.collectionView(collectionView!, layout: self, workDayForSection: section) + for hour in 0...23 { + (attributes, nonWorkingHoursBackgroundAttributes) = layoutAttributesForDecorationView(at: IndexPath(item: hour, section: section), + ofKind: JZDecorationViewKinds.nonWorkingHoursBackground, + withItemCache: nonWorkingHoursBackgroundAttributes) + if workDay != nil { + let nonWorkingHoursBackgroundMinX = nearbyint(sectionX - defaultGridThickness / 2.0) + var nonWorkingHoursBackgroundMinY = nearbyint(calendarGridMinY + (hourHeight * CGFloat(hour))) - (defaultGridThickness / 2.0) + let nonWorkingHoursBackgroundWidth = fmin(sectionWidth, collectionView!.frame.width) + var nonWorkingHoursBackgroundHeight: CGFloat = 0 + if let day = delegate?.collectionView(collectionView!, layout: self, dayForSection: section) { + let startTime = day.startOfDay.addingTimeInterval(TimeInterval(hour) * 60 * 60) + let endTime = startTime.addingTimeInterval(60 * 60) + if startTime < workDay!.startTime || endTime > workDay!.endTime { + if (startTime < workDay!.startTime && endTime <= workDay!.startTime) || (startTime >= workDay!.endTime) { + nonWorkingHoursBackgroundHeight = hourHeight + } + else if (startTime < workDay!.startTime && endTime > workDay!.startTime) { + nonWorkingHoursBackgroundHeight = hourHeight - (hourHeight * CGFloat((workDay!.startTime.timeIntervalSince(startTime) / endTime.timeIntervalSince(startTime)))) + } + else if (startTime > workDay!.startTime && endTime > workDay!.endTime) { + nonWorkingHoursBackgroundMinY += hourHeight * CGFloat((workDay!.endTime.timeIntervalSince(startTime) / endTime.timeIntervalSince(startTime))) + nonWorkingHoursBackgroundHeight = hourHeight - nonWorkingHoursBackgroundMinY + } + } + } + else { + fatalError() + } + attributes.frame = CGRect(x: nonWorkingHoursBackgroundMinX, y: nonWorkingHoursBackgroundMinY, + width: nonWorkingHoursBackgroundWidth, height: nonWorkingHoursBackgroundHeight) + } + else { + attributes.frame = .zero + } + attributes.zIndex = zIndexForElementKind(JZDecorationViewKinds.nonWorkingHoursBackground) + } + } + func layoutVerticalGridLinesAttributes(section: Int, sectionX: CGFloat, calendarGridMinY: CGFloat, sectionHeight: CGFloat) { var attributes = UICollectionViewLayoutAttributes() @@ -435,6 +482,8 @@ open class JZWeekViewFlowLayout: UICollectionViewFlowLayout { return allDayHeaderBackgroundAttributes[indexPath] case JZDecorationViewKinds.allDayCorner: return allDayCornerAttributes[indexPath] + case JZDecorationViewKinds.nonWorkingHoursBackground: + return nonWorkingHoursBackgroundAttributes[indexPath] default: return nil } @@ -597,6 +646,7 @@ open class JZWeekViewFlowLayout: UICollectionViewFlowLayout { rowHeaderAttributes.removeAll() rowHeaderBackgroundAttributes.removeAll() cornerHeaderAttributes.removeAll() + nonWorkingHoursBackgroundAttributes.removeAll() itemAttributes.removeAll() allAttributes.removeAll() @@ -712,6 +762,8 @@ open class JZWeekViewFlowLayout: UICollectionViewFlowLayout { return minBackgroundZ + 2 case JZDecorationViewKinds.verticalGridline: return minBackgroundZ + 1 + case JZDecorationViewKinds.nonWorkingHoursBackground: + return minBackgroundZ default: return minCellZ } diff --git a/JZCalendarWeekView/JZWeekViewHelper.swift b/JZCalendarWeekView/JZWeekViewHelper.swift index c0de006..7e86483 100644 --- a/JZCalendarWeekView/JZWeekViewHelper.swift +++ b/JZCalendarWeekView/JZWeekViewHelper.swift @@ -18,6 +18,7 @@ public enum JZSupplementaryViewKinds { public enum JZDecorationViewKinds { public static let columnHeaderBackground = JZColumnHeaderBackground.className public static let rowHeaderBackground = JZRowHeaderBackground.className + public static let nonWorkingHoursBackground = JZNonWorkingHoursBackground.className public static let allDayHeaderBackground = JZAllDayHeaderBackground.className public static let allDayCorner = JZAllDayCorner.className public static let verticalGridline = "VerticalGridline" diff --git a/JZCalendarWeekView/JZWorkDay.swift b/JZCalendarWeekView/JZWorkDay.swift new file mode 100644 index 0000000..2930f16 --- /dev/null +++ b/JZCalendarWeekView/JZWorkDay.swift @@ -0,0 +1,21 @@ +// +// JZWorkDay.swift +// JZCalendarWeekView +// +// Created by Stefan on 26/10/18. +// Copyright © 2018 Jeff Zhang. All rights reserved. +// + +import Foundation + +open class JZWorkDay: NSObject { + + public var startTime: Date + public var endTime: Date + + public init(startTime: Date, endTime: Date) { + self.startTime = startTime + self.endTime = endTime + } + +} diff --git a/JZCalendarWeekView/ReusableViews/JZNonWorkingHoursBackground.swift b/JZCalendarWeekView/ReusableViews/JZNonWorkingHoursBackground.swift new file mode 100644 index 0000000..6ddd07d --- /dev/null +++ b/JZCalendarWeekView/ReusableViews/JZNonWorkingHoursBackground.swift @@ -0,0 +1,23 @@ +// +// JZNonWorkingHoursBackground.swift +// JZCalendarWeekView +// +// Created by Stefan on 26/10/18. +// Copyright © 2018 Jeff Zhang. All rights reserved. +// + +import Foundation + +/// The background drawed on layout to outline non working hours +class JZNonWorkingHoursBackground: UICollectionReusableView { + + public override init(frame: CGRect) { + super.init(frame: frame) + backgroundColor = JZWeekViewColors.nonWorkingHours + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/JZCalendarWeekView/Utils/JZWeekViewColors.swift b/JZCalendarWeekView/Utils/JZWeekViewColors.swift index 315c972..c741183 100644 --- a/JZCalendarWeekView/Utils/JZWeekViewColors.swift +++ b/JZCalendarWeekView/Utils/JZWeekViewColors.swift @@ -11,6 +11,7 @@ import Foundation /// cannot change for now, will implement theme in the future class JZWeekViewColors { + class var nonWorkingHours: UIColor { return UIColor(hex: 0xF0F0F0) } class var columnHeaderWeekday: UIColor { return UIColor(hex: 0x757575) } class var columnHeaderDay: UIColor { return UIColor(hex: 0x757575) } class var allDayHeader: UIColor { return UIColor(hex: 0x757575) }