From cb8465431ef30b7d9eeaf286e4d1c12de992ec58 Mon Sep 17 00:00:00 2001 From: Jeff Zhang Date: Wed, 2 May 2018 17:18:29 +1000 Subject: [PATCH] v0.2.1: Improvements for longPress position & Fix collectionView Reuse caused bugs --- .../project.pbxproj | 8 + .../LongPressViews/LongPressEventCell.swift | 42 +++++ .../LongPressViews/LongPressEventCell.xib | 64 +++++++ .../LongPressViewController.swift | 4 +- .../LongPressViews/LongPressWeekView.swift | 4 +- .../Source/RootViewModel.swift | 2 +- .../Supporting Files/Info.plist | 4 +- JZCalendarWeekView.podspec | 2 +- JZCalendarWeekView.xcodeproj/project.pbxproj | 2 +- JZCalendarWeekView/Info.plist | 2 +- .../{ReusableViews => }/JZBaseEventCell.swift | 5 +- JZCalendarWeekView/JZLongPressWeekView.swift | 162 ++++++++++++------ JZCalendarWeekView/JZWeekViewFlowLayout.swift | 21 +-- JZCalendarWeekView/Utils/Extensions.swift | 8 + README.md | 9 +- 15 files changed, 262 insertions(+), 77 deletions(-) create mode 100644 Example/JZCalendarWeekViewExample/Source/LongPressViews/LongPressEventCell.swift create mode 100644 Example/JZCalendarWeekViewExample/Source/LongPressViews/LongPressEventCell.xib rename JZCalendarWeekView/{ReusableViews => }/JZBaseEventCell.swift (63%) diff --git a/Example/JZCalendarWeekViewExample.xcodeproj/project.pbxproj b/Example/JZCalendarWeekViewExample.xcodeproj/project.pbxproj index a3a2b1d..4b01d33 100644 --- a/Example/JZCalendarWeekViewExample.xcodeproj/project.pbxproj +++ b/Example/JZCalendarWeekViewExample.xcodeproj/project.pbxproj @@ -31,6 +31,8 @@ 8E9A7DF520969919009AEB6E /* LongPressWeekView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E9A7DF420969919009AEB6E /* LongPressWeekView.swift */; }; 8E9AE75B20773F3500B7004E /* ExampleOptionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E9AE75A20773F3500B7004E /* ExampleOptionsViewController.swift */; }; 8EBD62EA2096F8AD00520E58 /* LongPressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EBD62E92096F8AD00520E58 /* LongPressViewController.swift */; }; + 8EC8A46B209999A2002C7634 /* LongPressEventCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EC8A469209999A2002C7634 /* LongPressEventCell.swift */; }; + 8EC8A46C209999A2002C7634 /* LongPressEventCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8EC8A46A209999A2002C7634 /* LongPressEventCell.xib */; }; 8EE28FBF20882DE900C69A77 /* JZCalendarWeekView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8EE28FBB20882D7A00C69A77 /* JZCalendarWeekView.framework */; }; 8EE28FC020882DE900C69A77 /* JZCalendarWeekView.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8EE28FBB20882D7A00C69A77 /* JZCalendarWeekView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ @@ -100,6 +102,8 @@ 8E9A7DF420969919009AEB6E /* LongPressWeekView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongPressWeekView.swift; sourceTree = ""; }; 8E9AE75A20773F3500B7004E /* ExampleOptionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleOptionsViewController.swift; sourceTree = ""; }; 8EBD62E92096F8AD00520E58 /* LongPressViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongPressViewController.swift; sourceTree = ""; }; + 8EC8A469209999A2002C7634 /* LongPressEventCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongPressEventCell.swift; sourceTree = ""; }; + 8EC8A46A209999A2002C7634 /* LongPressEventCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = LongPressEventCell.xib; sourceTree = ""; }; 8EE28FB520882D7900C69A77 /* JZCalendarWeekView.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = JZCalendarWeekView.xcodeproj; path = ../JZCalendarWeekView.xcodeproj; sourceTree = ""; }; /* End PBXFileReference section */ @@ -216,6 +220,8 @@ children = ( 8EBD62E92096F8AD00520E58 /* LongPressViewController.swift */, 8E9A7DF420969919009AEB6E /* LongPressWeekView.swift */, + 8EC8A469209999A2002C7634 /* LongPressEventCell.swift */, + 8EC8A46A209999A2002C7634 /* LongPressEventCell.xib */, ); path = LongPressViews; sourceTree = ""; @@ -313,6 +319,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8EC8A46C209999A2002C7634 /* LongPressEventCell.xib in Resources */, 8E5BB3DC2072F4A000FA853F /* LaunchScreen.storyboard in Resources */, 8E5EE63D20857A6500150FD3 /* ExpandableHeaderView.xib in Resources */, 8E7263E02074B55300ECF7CD /* EventCell.xib in Resources */, @@ -335,6 +342,7 @@ 8E2E6567208D5F3600FB2CE4 /* BlackGridLine.swift in Sources */, 8E5BB404207322BA00FA853F /* Event.swift in Sources */, 8EBD62EA2096F8AD00520E58 /* LongPressViewController.swift in Sources */, + 8EC8A46B209999A2002C7634 /* LongPressEventCell.swift in Sources */, 8E048ED9207F26F500A532BA /* OptionsTableViewCell.swift in Sources */, 8E9A7DF520969919009AEB6E /* LongPressWeekView.swift in Sources */, 8E7263DF2074B55300ECF7CD /* EventCell.swift in Sources */, diff --git a/Example/JZCalendarWeekViewExample/Source/LongPressViews/LongPressEventCell.swift b/Example/JZCalendarWeekViewExample/Source/LongPressViews/LongPressEventCell.swift new file mode 100644 index 0000000..c83f294 --- /dev/null +++ b/Example/JZCalendarWeekViewExample/Source/LongPressViews/LongPressEventCell.swift @@ -0,0 +1,42 @@ +// +// LongPressEventCell.swift +// JZCalendarWeekViewExample +// +// Created by Jeff Zhang on 2/5/18. +// Copyright © 2018 Jeff Zhang. All rights reserved. +// + +import UIKit +import JZCalendarWeekView + +class LongPressEventCell: JZBaseEventCell { + + @IBOutlet weak var titleLabel: UILabel! + @IBOutlet weak var locationLabel: UILabel! + @IBOutlet weak var borderView: UIView! + + override func awakeFromNib() { + super.awakeFromNib() + + setupBasic() + } + + func setupBasic() { + self.clipsToBounds = true + layer.shadowColor = UIColor.black.cgColor + layer.shadowOffset = CGSize(width: 0, height: 4) + layer.shadowRadius = 5 + layer.shadowOpacity = 0 + locationLabel.font = UIFont.systemFont(ofSize: 12) + titleLabel.font = UIFont.systemFont(ofSize: 14, weight: .medium) + self.backgroundColor = UIColor(hex: 0xEEF7FF) + borderView.backgroundColor = UIColor(hex: 0x0899FF) + } + + func updateView(event: Event) { + self.event = event + locationLabel.text = event.location + titleLabel.text = event.title + } + +} diff --git a/Example/JZCalendarWeekViewExample/Source/LongPressViews/LongPressEventCell.xib b/Example/JZCalendarWeekViewExample/Source/LongPressViews/LongPressEventCell.xib new file mode 100644 index 0000000..1cbbe3b --- /dev/null +++ b/Example/JZCalendarWeekViewExample/Source/LongPressViews/LongPressEventCell.xib @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/JZCalendarWeekViewExample/Source/LongPressViews/LongPressViewController.swift b/Example/JZCalendarWeekViewExample/Source/LongPressViews/LongPressViewController.swift index e967f08..d1c5659 100644 --- a/Example/JZCalendarWeekViewExample/Source/LongPressViews/LongPressViewController.swift +++ b/Example/JZCalendarWeekViewExample/Source/LongPressViews/LongPressViewController.swift @@ -92,8 +92,8 @@ extension LongPressViewController: JZLongPressViewDelegate, JZLongPressViewDataS weekView.forceReload(reloadEvents: viewModel.eventsByDate) } - func weekView(_ weekView: JZLongPressWeekView, movingCell: UICollectionViewCell, didEndMoveLongPressAt startDate: Date) { - let event = (movingCell as! EventCell).event! + func weekView(_ weekView: JZLongPressWeekView, editingEvent: JZBaseEvent, didEndMoveLongPressAt startDate: Date) { + let event = editingEvent as! Event let duration = Calendar.current.dateComponents([.minute], from: event.startDate, to: event.endDate).minute! let selectedIndex = viewModel.events.index(where: { $0.id == event.id })! viewModel.events[selectedIndex].startDate = startDate diff --git a/Example/JZCalendarWeekViewExample/Source/LongPressViews/LongPressWeekView.swift b/Example/JZCalendarWeekViewExample/Source/LongPressViews/LongPressWeekView.swift index 7d7d024..6dc04a8 100644 --- a/Example/JZCalendarWeekViewExample/Source/LongPressViews/LongPressWeekView.swift +++ b/Example/JZCalendarWeekViewExample/Source/LongPressViews/LongPressWeekView.swift @@ -14,11 +14,11 @@ class LongPressWeekView: JZLongPressWeekView { override func registerViewClasses() { super.registerViewClasses() - self.collectionView.register(UINib(nibName: EventCell.className, bundle: nil), forCellWithReuseIdentifier: EventCell.className) + self.collectionView.register(UINib(nibName: LongPressEventCell.className, bundle: nil), forCellWithReuseIdentifier: LongPressEventCell.className) } override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: EventCell.className, for: indexPath) as! EventCell + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LongPressEventCell.className, for: indexPath) as! LongPressEventCell cell.updateView(event: getCurrentEvent(with: indexPath) as! Event) return cell } diff --git a/Example/JZCalendarWeekViewExample/Source/RootViewModel.swift b/Example/JZCalendarWeekViewExample/Source/RootViewModel.swift index 8276b37..c9d5be8 100644 --- a/Example/JZCalendarWeekViewExample/Source/RootViewModel.swift +++ b/Example/JZCalendarWeekViewExample/Source/RootViewModel.swift @@ -18,7 +18,7 @@ class RootViewModel: NSObject { lazy var events = [Event(id: "0", title: "One", startDate: firstDate, endDate: firstDate.add(component: .hour, value: 1), location: "Melbourne", eventType: 0), Event(id: "1", title: "Two", startDate: secondDate, endDate: secondDate.add(component: .hour, value: 4), location: "Sydney", eventType: 0), Event(id: "2", title: "Three", startDate: thirdDate, endDate: thirdDate.add(component: .hour, value: 2), location: "Tasmania", eventType: 1), - Event(id: "3", title: "Four", startDate: thirdDate, endDate: thirdDate.add(component: .day, value: 2), location: "Canberra", eventType: 1)] + Event(id: "3", title: "Four", startDate: thirdDate, endDate: thirdDate.add(component: .day, value: 1), location: "Canberra", eventType: 1)] lazy var eventsByDate: EventsByDate = JZWeekViewHelper.getIntraEventsByDate(originalEvents: events) diff --git a/Example/JZCalendarWeekViewExample/Supporting Files/Info.plist b/Example/JZCalendarWeekViewExample/Supporting Files/Info.plist index a833c36..0499535 100644 --- a/Example/JZCalendarWeekViewExample/Supporting Files/Info.plist +++ b/Example/JZCalendarWeekViewExample/Supporting Files/Info.plist @@ -15,9 +15,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.2.0 + 0.2.1 CFBundleVersion - 1805011500 + 1805021706 LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/JZCalendarWeekView.podspec b/JZCalendarWeekView.podspec index 441b09e..e175477 100644 --- a/JZCalendarWeekView.podspec +++ b/JZCalendarWeekView.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "JZCalendarWeekView" - s.version = "0.2.0" + s.version = "0.2.1" s.summary = "Calendar Week & Day View in iOS Swift" s.homepage = "https://github.com/zjfjack/JZCalendarWeekView" s.license = { :type => "MIT", :file => "LICENSE" } diff --git a/JZCalendarWeekView.xcodeproj/project.pbxproj b/JZCalendarWeekView.xcodeproj/project.pbxproj index dc5e8d0..1d6a6e1 100644 --- a/JZCalendarWeekView.xcodeproj/project.pbxproj +++ b/JZCalendarWeekView.xcodeproj/project.pbxproj @@ -119,6 +119,7 @@ 8E21E8AF20916A96002D72D0 /* JZLongPressWeekView.swift */, 8E1BA95A206B4826007BE13C /* JZWeekViewFlowLayout.swift */, 8E023C83206C732300C523BE /* JZBaseEvent.swift */, + 8E1BA971206B61B7007BE13C /* JZBaseEventCell.swift */, 8E1BA969206B5350007BE13C /* JZWeekViewHelper.swift */, 8E1BA95C206B4AD9007BE13C /* ReusableViews */, 8E1BA955206B4687007BE13C /* Utils */, @@ -140,7 +141,6 @@ 8E1BA95C206B4AD9007BE13C /* ReusableViews */ = { isa = PBXGroup; children = ( - 8E1BA971206B61B7007BE13C /* JZBaseEventCell.swift */, 8E1BA95F206B4B84007BE13C /* JZColumnHeader.swift */, 8E1BA961206B4B99007BE13C /* JZRowHeader.swift */, 8E1BA963206B4E82007BE13C /* JZCornerHeader.swift */, diff --git a/JZCalendarWeekView/Info.plist b/JZCalendarWeekView/Info.plist index 23d96cb..f3f69d3 100644 --- a/JZCalendarWeekView/Info.plist +++ b/JZCalendarWeekView/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.2.0 + 0.2.1 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/JZCalendarWeekView/ReusableViews/JZBaseEventCell.swift b/JZCalendarWeekView/JZBaseEventCell.swift similarity index 63% rename from JZCalendarWeekView/ReusableViews/JZBaseEventCell.swift rename to JZCalendarWeekView/JZBaseEventCell.swift index fd5b26e..81ad6fa 100644 --- a/JZCalendarWeekView/ReusableViews/JZBaseEventCell.swift +++ b/JZCalendarWeekView/JZBaseEventCell.swift @@ -8,7 +8,10 @@ import UIKit -// No Need to subclass, used only for identifier now, will refactor in the future +// If you want to use Move Type LongPressWeekView, you have to subclass this class open class JZBaseEventCell: UICollectionViewCell { + public var event: JZBaseEvent! + + } diff --git a/JZCalendarWeekView/JZLongPressWeekView.swift b/JZCalendarWeekView/JZLongPressWeekView.swift index d099337..db3f29d 100644 --- a/JZCalendarWeekView/JZLongPressWeekView.swift +++ b/JZCalendarWeekView/JZLongPressWeekView.swift @@ -21,9 +21,9 @@ public protocol JZLongPressViewDelegate: class { /// You should handle what should be done after editing (moving) a existed event. /// - Parameters: /// - weekView: current long pressed JZLongPressWeekView - /// - movingCell: the moving (existed, editing) cell + /// - editingEvent: the moving (existed, editing) event /// - startDate: the startDate of the event when gesture ends - func weekView(_ weekView: JZLongPressWeekView, movingCell: UICollectionViewCell, didEndMoveLongPressAt startDate: Date) + func weekView(_ weekView: JZLongPressWeekView, editingEvent: JZBaseEvent, didEndMoveLongPressAt startDate: Date) /// Sometimes the longPress will be cancelled because some curtain reason. /// Normally this function no need to be implemented. @@ -56,7 +56,7 @@ extension JZLongPressViewDelegate { // Keep them optional public func weekView(_ weekView: JZLongPressWeekView, longPressType: JZLongPressWeekView.LongPressType, didCancelLongPressAt startDate: Date) {} public func weekView(_ weekView: JZLongPressWeekView, didEndAddNewLongPressAt startDate: Date) {} - public func weekView(_ weekView: JZLongPressWeekView, movingCell: UICollectionViewCell, didEndMoveLongPressAt startDate: Date) {} + public func weekView(_ weekView: JZLongPressWeekView, editingEvent: JZBaseEvent, didEndMoveLongPressAt startDate: Date) {} } extension JZLongPressViewDataSource { @@ -80,10 +80,22 @@ open class JZLongPressWeekView: JZBaseWeekView { case move } + /// This structure is used to save editing information before reusing collectionViewCell (Type Move used only) + private struct CurrentEditingInfo { + /// The editing event when move type long press(used to be currentMovingCell, it is a reference of cell but item will be reused in CollectionView!!) + var event: JZBaseEvent! + /// The editing cell original size, get it from the long press status began + var cellSize: CGSize! + /// Save current indexPath to check whether a cell is the previous one + var indexPath: IndexPath! + } + private var isLongPressing: Bool = false private var currentLongPressType: LongPressType! private var longPressView: UIView! - private var currentMovingCell: UICollectionViewCell! + private var currentEditingInfo = CurrentEditingInfo() + /// Get this value when long press began and save the current relative X and Y value until it ended or cancelled + private var pressPosition: (xToViewLeft: CGFloat, yToViewTop: CGFloat)? public weak var longPressDelegate: JZLongPressViewDelegate? public weak var longPressDataSource: JZLongPressViewDataSource? @@ -101,18 +113,20 @@ open class JZLongPressWeekView: JZBaseWeekView { label.textColor = UIColor.gray return label }() + public var movingCellAlpha: CGFloat = 0.6 /// The most top Y in the collectionView that you want longPress gesture enable. /// If you customise some decoration and supplementry views on top, **must** override this variable - open var longPressTopMarginY: CGFloat { - return flowLayout.columnHeaderHeight - } - + open var longPressTopMarginY: CGFloat { return flowLayout.columnHeaderHeight } /// The most bottom Y in the collectionView that you want longPress gesture enable. /// If you customise some decoration and supplementry views on bottom, **must** override this variable - open var longPressBottomMarginY: CGFloat{ - return frame.height - } + open var longPressBottomMarginY: CGFloat{ return frame.height } + /// The most left X in the collectionView that you want longPress gesture enable. + /// If you customise some decoration and supplementry views on left, **must** override this variable + open var longPressLeftMarginX: CGFloat { return flowLayout.rowHeaderWidth } + /// The most right X in the collectionView that you want longPress gesture enable. + /// If you customise some decoration and supplementry views on right, **must** override this variable + open var longPressRightmMarginX: CGFloat{ return frame.width } public override init(frame: CGRect) { @@ -133,36 +147,37 @@ open class JZLongPressWeekView: JZBaseWeekView { } /// Updating time label in longPressView during dragging - private func updateTimeLabel(time: Date, point: CGPoint) { + private func updateTimeLabel(time: Date, pointInSelfView: CGPoint) { longPressTimeLabel.text = time.getTimeIgnoreSecondsFormat() - let isOutsideRowHeader = point.x - longPressView.frame.width/2 < flowLayout.rowHeaderWidth - longPressTimeLabel.textAlignment = isOutsideRowHeader ? .right : .left + let isOutsideLeftMargin = pointInSelfView.x - pressPosition!.xToViewLeft < longPressLeftMarginX + longPressTimeLabel.textAlignment = isOutsideLeftMargin ? .right : .left let labelHeight = longPressTimeLabel.frame.height - let isBeyondTopMargin = point.y - labelHeight < longPressTopMarginY + let isBeyondTopMargin = pointInSelfView.y - pressPosition!.yToViewTop - labelHeight < longPressTopMarginY let yPosition = isBeyondTopMargin ? longPressView.frame.height : -labelHeight if longPressTimeLabel.frame.origin.y != yPosition { longPressTimeLabel.frame.origin.y = yPosition } } - /// When dragging the longPressView, the collectionView should scroll with the drag point - private func updateScroll(point: CGPoint) { + /// When dragging the longPressView, the collectionView should scroll with the drag point. + /// - The logic of vertical scroll is top scroll depending on **longPressView top** to longPressTopMarginY, bottom scroll denpending on **finger point** to LongPressBottomMarginY. + /// - The logic of horizontal scroll is left scroll depending on **finger point** to longPressLeftMarginY, bottom scroll denpending on **finger point** to LongPressRightMarginY. + private func updateScroll(pointInSelfView: CGPoint) { // vertical - if point.y < longPressTopMarginY + 10 && !isScrolling { + if pointInSelfView.y - pressPosition!.yToViewTop < longPressTopMarginY + 10 && !isScrolling { isScrolling = true scrollingTo(direction: .up) - } else if point.y > longPressBottomMarginY - 30 && !isScrolling { + } else if pointInSelfView.y > longPressBottomMarginY - 40 && !isScrolling { isScrolling = true scrollingTo(direction: .down) } // horizontal - if point.x < flowLayout.rowHeaderWidth && !isScrolling { + if pointInSelfView.x < longPressLeftMarginX + 10 && !isScrolling { isScrolling = true scrollingTo(direction: .right) - - } else if frame.width - point.x < 20 && !isScrolling { + } else if pointInSelfView.x > longPressRightmMarginX - 20 && !isScrolling { isScrolling = true scrollingTo(direction: .left) } @@ -175,10 +190,7 @@ open class JZLongPressWeekView: JZBaseWeekView { if direction == .up || direction == .down { var yOffset = CGFloat() - // TODO: NOT SURE WHY NEED THIS LINE - if scrollType == .sectionScroll { - scrollSections = 0 - } + if scrollType == .sectionScroll { scrollSections = 0 } if direction == .up { yOffset = max(0,currentOffset.y - 50) @@ -229,7 +241,7 @@ open class JZLongPressWeekView: JZBaseWeekView { return startDate } - /// Initialise the long press view with longPressTimeLabel + /// Initialise the long press view with longPressTimeLabel. open func initLongPressView(selectedCell: UICollectionViewCell?, type: LongPressType, startDate: Date) -> UIView { let longPressView = type == .move ? longPressDataSource!.weekView(self, movingCell: selectedCell!, viewForMoveLongPressAt: startDate) : @@ -243,8 +255,33 @@ open class JZLongPressWeekView: JZBaseWeekView { let timeLabelWidth = max(selectedCell?.bounds.width ?? flowLayout.sectionWidth, textWidth) longPressTimeLabel.frame = CGRect(x: 0, y: -labelHeight, width: timeLabelWidth, height: labelHeight) longPressView.addSubview(longPressTimeLabel) + longPressView.setDefaultShadow() return longPressView } + + // Following three functions are used to Handle collectionView items reusued + + /// when the previous cell is reused, have to find current one + open func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + guard isLongPressing == true && currentLongPressType == .move else { return } + cell.contentView.alpha = checkIfTheOriginalMovingCell(indexPath: indexPath, cell: cell) ? movingCellAlpha : 1 + } + + /// Use start and end time and indexPath to make sure 100% is the original cell + private func checkIfTheOriginalMovingCell(indexPath: IndexPath, cell: UICollectionViewCell) -> Bool { + let jzCell = cell as! JZBaseEventCell + return indexPath == currentEditingInfo.indexPath && jzCell.event.startDate == currentEditingInfo.event.startDate && jzCell.event.endDate == currentEditingInfo.event.endDate + } + + /// Get the current moving cell and change the contentView alpha back + private func getCurrentMovingCell() -> UICollectionViewCell? { + for cell in collectionView.visibleCells { + if cell.contentView.alpha < 1 { + return cell + } + } + return nil + } } // Long press Gesture methods @@ -256,8 +293,9 @@ extension JZLongPressWeekView: UIGestureRecognizerDelegate { let pointInCollectionView = gestureRecognizer.location(in: collectionView) if gestureRecognizer.state == .possible { - // Long press on rowheader or beyond top margin should not begin - let isOutsideBeginArea = pointInSelfView.x <= flowLayout.rowHeaderWidth || pointInSelfView.y < longPressTopMarginY + // Long press on ouside margin area should not begin + let isOutsideBeginArea = pointInSelfView.x < longPressLeftMarginX || pointInSelfView.x > longPressRightmMarginX || + pointInSelfView.y < longPressTopMarginY || pointInSelfView.y > longPressBottomMarginY if isOutsideBeginArea { return false } } // Long press should not begin if no events at long press position and addNew not required @@ -268,20 +306,23 @@ extension JZLongPressWeekView: UIGestureRecognizerDelegate { return true } + /// The basic longPressView position logic is moving with your finger's original position. + /// - The Move type longPressView will keep the relative position during this longPress, that's how Apple Calendar did. + /// - The AddNew type longPressView will be created centrally at your finger press position @objc private func handleLongPressGesture(_ gestureRecognizer: UILongPressGestureRecognizer) { let pointInSelfView = gestureRecognizer.location(in: self) + /// Used for get startDate of longPressView let pointInCollectionView = gestureRecognizer.location(in: collectionView) let state = gestureRecognizer.state - - let date = getDateForPoint(pointCollectionView: pointInCollectionView, pointSelfView: pointInSelfView) - let startDate = getLongPressStartDate(date: date, dateInSection: getDateForX(xCollectionView: pointInCollectionView.x, xSelfView: pointInSelfView.x), timeMinInterval: moveTimeMinInterval) + var currentMovingCell: UICollectionViewCell! if isLongPressing == false { if let indexPath = collectionView.indexPathForItem(at: pointInCollectionView) { // Can add some conditions for allowing only few types of cells can be moved currentLongPressType = .move + currentEditingInfo.indexPath = indexPath currentMovingCell = collectionView.cellForItem(at: indexPath) } else { currentLongPressType = .addNew @@ -289,26 +330,39 @@ extension JZLongPressWeekView: UIGestureRecognizerDelegate { isLongPressing = true } - let viewSize = currentLongPressType == .move ? currentMovingCell.frame.size : CGSize(width: flowLayout.sectionWidth, height: flowLayout.defaultHourHeight * CGFloat(addNewDurationMins/60)) + // The startDate of the longPressView (the date of top Y in longPressView) + var longPressViewStartDate: Date! + + // pressPosition is nil only when state equals began + if pressPosition != nil { + longPressViewStartDate = getLongPressViewStartDate(pointInCollectionView: pointInCollectionView, pointInSelfView: pointInSelfView) + } if state == .began { - longPressView = initLongPressView(selectedCell: currentMovingCell, type: currentLongPressType, startDate: startDate) - longPressView.frame.size = viewSize - longPressView.center = CGPoint(x: pointInSelfView.x, y: pointInSelfView.y + viewSize.height/2) + currentEditingInfo.cellSize = currentLongPressType == .move ? currentMovingCell.frame.size : CGSize(width: flowLayout.sectionWidth, height: flowLayout.defaultHourHeight * CGFloat(addNewDurationMins/60)) + pressPosition = currentLongPressType == .move ? (pointInCollectionView.x - currentMovingCell.frame.origin.x, pointInCollectionView.y - currentMovingCell.frame.origin.y) : + (currentEditingInfo.cellSize.width/2, currentEditingInfo.cellSize.height/2) + longPressViewStartDate = getLongPressViewStartDate(pointInCollectionView: pointInCollectionView, pointInSelfView: pointInSelfView) + longPressView = initLongPressView(selectedCell: currentMovingCell, type: currentLongPressType, startDate: longPressViewStartDate) + longPressView.frame.size = currentEditingInfo.cellSize longPressView.transform = CGAffineTransform(scaleX: 0.1, y: 0.1) self.addSubview(longPressView) + longPressView.center = CGPoint(x: pointInSelfView.x - pressPosition!.xToViewLeft + currentEditingInfo.cellSize.width/2, + y: pointInSelfView.y - pressPosition!.yToViewTop + currentEditingInfo.cellSize.height/2) if currentLongPressType == .move { - currentMovingCell.isHidden = true + currentMovingCell.contentView.alpha = movingCellAlpha + currentEditingInfo.event = (currentMovingCell as! JZBaseEventCell).event } + UIView.animate(withDuration: 0.2, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 5, options: .curveEaseOut, animations: { self.longPressView.transform = CGAffineTransform.identity }, completion: nil) } else if state == .changed { - - let yPoint = max(pointInSelfView.y, longPressTopMarginY) + viewSize.height/2 - longPressView.center = CGPoint(x: pointInSelfView.x, y: yPoint) + let topYPoint = max(pointInSelfView.y - pressPosition!.yToViewTop, longPressTopMarginY) + longPressView.center = CGPoint(x: pointInSelfView.x - pressPosition!.xToViewLeft + currentEditingInfo.cellSize.width/2, + y: topYPoint + currentEditingInfo.cellSize.height/2) } else if state == .cancelled { @@ -318,34 +372,38 @@ extension JZLongPressWeekView: UIGestureRecognizerDelegate { (finished: Bool) -> Void in self.longPressView.removeFromSuperview() }) - - longPressDelegate?.weekView(self, longPressType: currentLongPressType, didCancelLongPressAt: startDate) + longPressDelegate?.weekView(self, longPressType: currentLongPressType, didCancelLongPressAt: longPressViewStartDate) } else if state == .ended { - UIView.animate(withDuration: 0.5, delay: 0.0, options: .curveEaseOut, - animations: { self.longPressView.alpha = 0 }, - completion: { (finished: Bool) -> Void in - self.longPressView.removeFromSuperview()}) - + self.longPressView.removeFromSuperview() if currentLongPressType == .addNew { - longPressDelegate?.weekView(self, didEndAddNewLongPressAt: startDate) + longPressDelegate?.weekView(self, didEndAddNewLongPressAt: longPressViewStartDate) } else if currentLongPressType == .move { - longPressDelegate?.weekView(self, movingCell: currentMovingCell, didEndMoveLongPressAt: startDate) - currentMovingCell.isHidden = false + getCurrentMovingCell()?.contentView.alpha = 1 + longPressDelegate?.weekView(self, editingEvent: currentEditingInfo.event, didEndMoveLongPressAt: longPressViewStartDate) } } if state == .began || state == .changed { - updateTimeLabel(time: startDate, point: pointInSelfView) - updateScroll(point: pointInSelfView) + updateTimeLabel(time: longPressViewStartDate, pointInSelfView: pointInSelfView) + updateScroll(pointInSelfView: pointInSelfView) } if state == .ended || state == .cancelled { longPressTimeLabel.removeFromSuperview() isLongPressing = false + pressPosition = nil return } } + /// work for handleLongPressGesture only + private func getLongPressViewStartDate(pointInCollectionView: CGPoint, pointInSelfView: CGPoint) -> Date { + let longPressViewTopDate = getDateForPoint(pointCollectionView: CGPoint(x: pointInCollectionView.x, y: pointInCollectionView.y - pressPosition!.yToViewTop) , pointSelfView: pointInSelfView) + let longPressViewStartDate = getLongPressStartDate(date: longPressViewTopDate, dateInSection: getDateForX(xCollectionView: pointInCollectionView.x, xSelfView: pointInSelfView.x), + timeMinInterval: moveTimeMinInterval) + return longPressViewStartDate + } + } diff --git a/JZCalendarWeekView/JZWeekViewFlowLayout.swift b/JZCalendarWeekView/JZWeekViewFlowLayout.swift index 61cb2df..c80d783 100644 --- a/JZCalendarWeekView/JZWeekViewFlowLayout.swift +++ b/JZCalendarWeekView/JZWeekViewFlowLayout.swift @@ -27,16 +27,17 @@ open class JZWeekViewFlowLayout: UICollectionViewFlowLayout { public var hourGridDivision: JZHourGridDivision! var minuteHeight: CGFloat { return hourHeight / 60 } - open var defaultHourHeight: CGFloat = 50 - open var defaultRowHeaderWidth: CGFloat = 42 - open var defaultColumnHeaderHeight: CGFloat = 44 - open var defaultHourGridDivision = JZHourGridDivision.noneDiv - // If you want to change following constants, subclass the WeekViewFlowLayout, and override them - open var defaultGridThickness: CGFloat = 0.5 - open var defaultCurrentTimeLineHeight: CGFloat = 10 - open var contentsMargin = UIEdgeInsets(top: 10, left: 0, bottom: 10, right: 0) //Margin for the flowLayout in collectionView - open var sectionMargin = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) - open var itemMargin = UIEdgeInsets(top: 1, left: 1, bottom: 1, right: 1) + public var defaultHourHeight: CGFloat = 50 + public var defaultRowHeaderWidth: CGFloat = 42 + public var defaultColumnHeaderHeight: CGFloat = 44 + public var defaultHourGridDivision = JZHourGridDivision.noneDiv + // You can change following constants + public var defaultGridThickness: CGFloat = 0.5 + public var defaultCurrentTimeLineHeight: CGFloat = 10 + /// Margin for the flowLayout in collectionView + public var contentsMargin = UIEdgeInsets(top: 10, left: 0, bottom: 10, right: 0) + public var sectionMargin = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) + public var itemMargin = UIEdgeInsets(top: 1, left: 1, bottom: 1, right: 1) /// weekview contentSize height private var maxSectionHeight: CGFloat { return columnHeaderHeight + hourHeight * 24 + contentsMargin.top + contentsMargin.bottom } diff --git a/JZCalendarWeekView/Utils/Extensions.swift b/JZCalendarWeekView/Utils/Extensions.swift index 00b9ff8..bc4e853 100644 --- a/JZCalendarWeekView/Utils/Extensions.swift +++ b/JZCalendarWeekView/Utils/Extensions.swift @@ -132,6 +132,14 @@ extension UIView { UIGraphicsEndImageContext() return result } + + func setDefaultShadow() { + self.layer.shadowColor = UIColor.black.cgColor + self.layer.shadowOpacity = 0.05 + self.layer.shadowOffset = CGSize.zero + self.layer.shadowRadius = 4 + self.layer.masksToBounds = false + } } extension UILabel { diff --git a/README.md b/README.md index 2db9202..be3e655 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ Inspired from WRCalendarView (https://github.com/wayfinders/WRCalendarView) - [x] X-Day per Page (Day view: 1-day, 3-day view, weekview: 7-day) - [x] Two Scroll types: One-Day scroll (scroll a section) or Page scroll +- [x] Two Types of Long Press Gestures: Add a new event & Move an existing event - [x] Events display on calendar view (supports events with conflict time and events crossing few days) - [x] Current time line -- [x] Two Types of Long Press Gestures: Add a new event & Move an existing event @@ -50,7 +50,7 @@ override func viewWillTransition(to size: CGSize, with coordinator: UIViewContro Create your own WeekView class inheriting from `JZBaseWeekView`, and you should override the following functions. -1. Register function: Register your own CollectionViewCell, SupplementaryView or DecorationView here +1. Register function: Register your own `UICollectionReusableView` here. (CollectionViewCell, SupplementaryView or DecorationView) ```swift override func registerViewClasses() { @@ -85,7 +85,7 @@ override func collectionView(_ collectionView: UICollectionView, cellForItemAt i ### JZLongPressView -This view is inheriated from JZBaseWeekView and implements the long press gestures. You can simply follow the setup rules of JZBaseWeekView.
+This view is inheriated from `JZBaseWeekView` and implements the long press gestures. You can simply follow the setup rules of `JZBaseWeekView`.
In order to achieve the long press gestures, you should implement the `JZLongPressViewDelegate` and `JZLongPressViewDataSource` in your ViewController. ```swift @@ -93,7 +93,7 @@ public protocol JZLongPressViewDelegate: class { /// When addNew long press gesture ends, this function will be called. func weekView(_ weekView: JZLongPressWeekView, didEndAddNewLongPressAt startDate: Date) /// When Move long press gesture ends, this function will be called. - func weekView(_ weekView: JZLongPressWeekView, movingCell: UICollectionViewCell, didEndMoveLongPressAt startDate: Date) + func weekView(_ weekView: JZLongPressWeekView, editingEvent: JZBaseEvent, didEndMoveLongPressAt startDate: Date) /// Sometimes the longPress will be cancelled because some curtain reason. func weekView(_ weekView: JZLongPressWeekView, longPressType: JZLongPressWeekView.LongPressType, didCancelLongPressAt startDate: Date) } @@ -116,6 +116,7 @@ calendarWeekView.longPressTypes = [.addNew, .move] calendarWeekView.addNewDurationMins = 120 calendarWeekView.moveTimeMinInterval = 15 ``` +If you want to use the `move` type long press, you have to inherit your `UICollectionViewCell` from `JZBaseEventCell` to allow retrieving editing `JZBaseEvent` because of `UICollectionView` reuse problem. ### JZBaseEvent