Skip to content

Commit

Permalink
Added ability to flip calendar horizontally
Browse files Browse the repository at this point in the history
  • Loading branch information
patchthecode committed Oct 21, 2016
1 parent f5b75a6 commit a991b89
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 177 deletions.
6 changes: 6 additions & 0 deletions Sources/CalendarEnums.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ public enum InDateCellGeneration {
case forFirstMonthOnly, forAllMonths, off
}

/// Describes the calendar reading direction
/// Useful for regions that read text from right to left
public enum ReadingOrientation {
case rightToLeft, leftToRight
}

/// Describes which month owns the date
public enum DateOwner: Int {
/// Describes which month owns the date
Expand Down
4 changes: 4 additions & 0 deletions Sources/CalendarStructs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ public struct ConfigurationParameters {
var endDate: Date
/// Number of rows you want to calendar to display per date section
var numberOfRows: Int
/// Your calendar() Instance
var calendar: Calendar.Identifier
/// Describes the types of in-date cells to be generated.
var generateInDates: InDateCellGeneration
/// Describes the types of out-date cells to be generated.
Expand All @@ -64,12 +66,14 @@ public struct ConfigurationParameters {
public init(startDate: Date,
endDate: Date,
numberOfRows: Int,
calendar: Calendar.Identifier,
generateInDates: InDateCellGeneration,
generateOutDates: OutDateCellGeneration,
firstDayOfWeek: DaysOfWeek) {
self.startDate = startDate
self.endDate = endDate
self.numberOfRows = numberOfRows
self.calendar = calendar
self.generateInDates = generateInDates
self.generateOutDates = generateOutDates
self.firstDayOfWeek = firstDayOfWeek
Expand Down
4 changes: 2 additions & 2 deletions Sources/JTAppleCalendarDelegateProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ protocol JTAppleCalendarDelegateProtocol: class {
extension JTAppleCalendarView: JTAppleCalendarDelegateProtocol {

func cachedDate() -> (start: Date, end: Date, calendar: Calendar) {
return (start: cachedConfiguration.startDate,
end: cachedConfiguration.endDate,
return (start: startDateCache,
end: endDateCache,
calendar: calendar)
}

Expand Down
80 changes: 48 additions & 32 deletions Sources/JTAppleCalendarView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,7 @@ open class JTAppleCalendarView: UIView {
}
}

let calendar: Calendar = {
var cal = Calendar(identifier: .gregorian)
cal.timeZone = TimeZone(secondsFromGMT: 0)!
return cal
}()

var calendar: Calendar!
// Configuration parameters from the dataSource
var cachedConfiguration: ConfigurationParameters!
// Set the start of the month
Expand Down Expand Up @@ -266,8 +261,8 @@ open class JTAppleCalendarView: UIView {
lazy var calendarView: UICollectionView = {
let layout = JTAppleCalendarLayout(withDelegate: self)
layout.scrollDirection = self.direction
let cv = UICollectionView(frame: CGRect.zero,
collectionViewLayout: layout)

let cv = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
cv.dataSource = self
cv.delegate = self
cv.decelerationRate = UIScrollViewDecelerationRateFast
Expand Down Expand Up @@ -314,6 +309,11 @@ open class JTAppleCalendarView: UIView {
layout.itemSize = size
}
}

/// Changes the calendar's reading orientation
/// from left-to-right or right-to-left
/// Useful for ethnic calendars
var orientation: ReadingOrientation = .leftToRight

/// Initializes and returns a newly allocated
/// view object with the specified frame rectangle.
Expand Down Expand Up @@ -406,6 +406,23 @@ open class JTAppleCalendarView: UIView {
}
return retval
}
/// Changes the calendar reading direction
public func changeVisibleDirection(to orientation: ReadingOrientation) {
if !calendarIsAlreadyLoaded {
delayedExecutionClosure.append {
self.changeVisibleDirection(to: orientation)
}
return
}

if orientation == self.orientation {
return
}

self.orientation = orientation
calendarView.transform.a = orientation == .leftToRight ? 1 : -1
calendarView.reloadData()
}

func calendarOffsetIsAlreadyAtScrollPosition(
forOffset offset: CGPoint) -> Bool? {
Expand Down Expand Up @@ -593,13 +610,14 @@ open class JTAppleCalendarView: UIView {
Date.endOfMonth(for: newDateBoundary.endDate,
using: calendar)
let oldStartOfMonth =
Date.startOfMonth(for: cachedConfiguration.startDate,
Date.startOfMonth(for: startDateCache,
using: calendar)
let oldEndOfMonth =
Date.endOfMonth(for: cachedConfiguration.endDate,
Date.endOfMonth(for: endDateCache,
using: calendar)
if newStartOfMonth != oldStartOfMonth ||
newEndOfMonth != oldEndOfMonth ||
newDateBoundary.calendar != cachedConfiguration.calendar ||
newDateBoundary.numberOfRows != cachedConfiguration.numberOfRows ||
newDateBoundary.generateInDates != cachedConfiguration.generateInDates ||
newDateBoundary.generateOutDates != cachedConfiguration.generateOutDates ||
Expand Down Expand Up @@ -760,23 +778,18 @@ extension JTAppleCalendarView {
return retval
}

func scrollToSection(_ section: Int,
triggerScrollToDateDelegate: Bool = false,
animateScroll: Bool = true,
completionHandler: (() -> Void)?) {
func scrollToSection(_ section: Int, triggerScrollToDateDelegate: Bool = false, animateScroll: Bool = true, completionHandler: (() -> Void)?) {
if scrollInProgress {
return
}
if let date = dateInfoFromPath(IndexPath(
item: maxNumberOfDaysInWeek - 1, section: section))?.date {
let recalcDate = Date.startOfMonth(for: date,
using: calendar)!
self.scrollToDate(recalcDate,
triggerScrollToDateDelegate:
triggerScrollToDateDelegate,
animateScroll: animateScroll,
preferredScrollPosition: nil,
completionHandler: completionHandler)
if let date = dateInfoFromPath(IndexPath( item: maxNumberOfDaysInWeek - 1, section: section))?.date {
let recalcDate = Date.startOfMonth(for: date, using: calendar)!
self.scrollToDate(recalcDate,
triggerScrollToDateDelegate:
triggerScrollToDateDelegate,
animateScroll: animateScroll,
preferredScrollPosition: nil,
completionHandler: completionHandler)
}
}

Expand All @@ -786,10 +799,14 @@ extension JTAppleCalendarView {
var totalSections = 0
var totalDays = 0
if let validConfig = dataSource?.configureCalendar(self) {
// check if the dates are in correct order
let comparison = calendar.compare(validConfig.startDate,
to: validConfig.endDate,
toGranularity: .nanosecond)
// Create a new calendar and check if the dates are in correct order

var aNewCalender = Calendar(identifier: validConfig.calendar)
aNewCalender.timeZone = TimeZone(secondsFromGMT: 0)!

let comparison = aNewCalender.compare( validConfig.startDate,
to: validConfig.endDate,
toGranularity: .nanosecond)

if comparison == ComparisonResult.orderedDescending {
assert(false, "Error, your start date cannot be " + "greater than your end date\n")
Expand All @@ -798,6 +815,7 @@ extension JTAppleCalendarView {

// Set the new cache
cachedConfiguration = validConfig
calendar = aNewCalender

if let
startMonth = Date.startOfMonth(for: validConfig.startDate, using: calendar),
Expand All @@ -813,16 +831,14 @@ extension JTAppleCalendarView {
endOfMonthCache: endOfMonthCache,
configuredCalendar: calendar,
firstDayOfWeek: validConfig.firstDayOfWeek)
let generatedData = dateGenerator
.setupMonthInfoDataForStartAndEndDate(parameters)
let generatedData = dateGenerator.setupMonthInfoDataForStartAndEndDate(parameters)
months = generatedData.months
monthMap = generatedData.monthMap
totalSections = generatedData.totalSections
totalDays = generatedData.totalDays
}
}
let data = CalendarData(months: months, totalSections: totalSections,
monthMap: monthMap, totalDays: totalDays)
let data = CalendarData(months: months, totalSections: totalSections, monthMap: monthMap, totalDays: totalDays)
return data
}

Expand Down
8 changes: 3 additions & 5 deletions Sources/JTAppleCollectionReusableView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,12 @@
//

/// The header view class of the calendar
open class JTAppleCollectionReusableView: UICollectionReusableView,
JTAppleReusableViewProtocol {
open class JTAppleCollectionReusableView: UICollectionReusableView, JTAppleReusableViewProtocol {
var view: JTAppleHeaderView?

func update() {
view!.frame = self.frame
view!.center = CGPoint(x: self.bounds.size.width * 0.5,
y: self.bounds.size.height * 0.5)
view!.center = CGPoint(x: self.bounds.size.width * 0.5, y: self.bounds.size.height * 0.5)
}

override init(frame: CGRect) {
Expand Down
45 changes: 21 additions & 24 deletions Sources/JTAppleReusableViewProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,63 +8,60 @@

internal protocol JTAppleReusableViewProtocol: class {
associatedtype ViewType: UIView
func setupView(_ cellSource: JTAppleCalendarViewSource)
func setupView(_ cellSource: JTAppleCalendarViewSource, leftToRightOrientation: ReadingOrientation)
var view: ViewType? {get set}
}

extension JTAppleReusableViewProtocol {

func setupView(_ cellSource: JTAppleCalendarViewSource) {
func setupView(_ cellSource: JTAppleCalendarViewSource, leftToRightOrientation: ReadingOrientation) {
if (self as? UIView)?.transform.a != 1 && leftToRightOrientation == .leftToRight {
(self as? UIView)!.transform.a = 1
} else if (self as? UIView)?.transform.a != -1 && leftToRightOrientation == .rightToLeft {
(self as? UIView)!.transform.a = -1
}

if let nonNilView = view {
nonNilView.setNeedsLayout()
return
}

switch cellSource {
case let .fromXib(xibName, bundle):
let bundleToUse = bundle ?? Bundle.main
let viewObject = bundleToUse
.loadNibNamed(xibName, owner: self, options: [:])
guard let view = viewObject?[0] as? ViewType else {
print("xib: \(xibName), " +
"file class does not conform to the JTAppleViewProtocol")
print("xib: \(xibName) file class does not conform to the JTAppleViewProtocol")
assert(false)
return
}
self.view = view
break
case let .fromClassName(className, bundle):
let bundleToUse = bundle ?? Bundle.main
guard let theCellClass =
bundleToUse.classNamed(className) as? ViewType.Type else {
print("Error loading registered class: '\(className)'")
print("Make sure that: \n\n(1) It is a subclass of: " +
"'UIView' and conforms to 'JTAppleViewProtocol'")
print("(2) You registered your class using the fully " +
"qualified name like so --> " +
"'theNameOfYourProject.theNameOfYourClass'\n")
assert(false)
return
guard let theCellClass = bundleToUse.classNamed(className) as? ViewType.Type else {
print("Error loading registered class: '\(className)'")
print("Make sure that: \n\n(1) It is a subclass of: 'UIView' and conforms to 'JTAppleViewProtocol'")
print("(2) You registered your class using the fully qualified name like so --> 'theNameOfYourProject.theNameOfYourClass'\n")
assert(false)
return
}
self.view = theCellClass.init()
break
case let .fromType(cellType):
guard let theCellClass = cellType as? ViewType.Type else {
print("Error loading registered class: '\(cellType)'")
print("Make sure that: \n\n(1) It is a subclass of: " +
"'UIiew' and conforms to 'JTAppleViewProtocol'\n")
print("Make sure that: \n\n(1) It is a subclass of: 'UIiew' and conforms to 'JTAppleViewProtocol'\n")
assert(false)
return
}
self.view = theCellClass.init()
break
}
guard
let validSelf = self as? UIView,
let validView = view else {
print("Error setting up views. \(developerErrorMessage)")
return
guard let validView = view else {
print("Error setting up views. \(developerErrorMessage)")
return
}
validSelf.addSubview(validView)
(self as? UIView)?.addSubview(validView)
}

}
Loading

0 comments on commit a991b89

Please sign in to comment.