Compare commits

...

3 Commits

Author SHA1 Message Date
Wenchao Ding c896d67181
Merge pull request #1030 from VirtusaPolarisGTO/multi-week-support
Multi week support
2018-12-01 09:34:50 +08:00
Badrinarayan Vijayakumar eefdb9a673 Added Multi Week Support. 2018-11-26 16:47:17 -05:00
Badrinarayan Vijayakumar 8cf046e30d Added multi week support. 2018-11-26 16:44:10 -05:00
13 changed files with 360 additions and 58 deletions

View File

@ -27,6 +27,7 @@
30F4C9471E07C3AD00D2EC4D /* FSCalendarStickyHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = 30F4C9351E07C3AD00D2EC4D /* FSCalendarStickyHeader.m */; };
30F4C9481E07C3AD00D2EC4D /* FSCalendarWeekdayView.m in Sources */ = {isa = PBXBuildFile; fileRef = 30F4C9371E07C3AD00D2EC4D /* FSCalendarWeekdayView.m */; };
50F2ADFA216DA21000CC481F /* FSCalendarSeparatorDecorationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 50F2ADF8216DA20F00CC481F /* FSCalendarSeparatorDecorationView.m */; };
CC7D16F72135D56100445693 /* FSWeeksSelectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC7D16F62135D56100445693 /* FSWeeksSelectionCell.swift */; };
EE0954AD1B97DDE0007F6964 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0954AC1B97DDE0007F6964 /* AppDelegate.swift */; };
EE0954AF1B97DDE0007F6964 /* InterfaceBuilderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0954AE1B97DDE0007F6964 /* InterfaceBuilderViewController.swift */; };
EE0954B21B97DDE0007F6964 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EE0954B01B97DDE0007F6964 /* Main.storyboard */; };
@ -84,6 +85,7 @@
30F4C9361E07C3AD00D2EC4D /* FSCalendarWeekdayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSCalendarWeekdayView.h; sourceTree = "<group>"; };
30F4C9371E07C3AD00D2EC4D /* FSCalendarWeekdayView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSCalendarWeekdayView.m; sourceTree = "<group>"; };
30F4C9381E07C3AD00D2EC4D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
CC7D16F62135D56100445693 /* FSWeeksSelectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FSWeeksSelectionCell.swift; sourceTree = "<group>"; };
50F2ADF8216DA20F00CC481F /* FSCalendarSeparatorDecorationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FSCalendarSeparatorDecorationView.m; sourceTree = "<group>"; };
50F2ADF9216DA21000CC481F /* FSCalendarSeparatorDecorationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FSCalendarSeparatorDecorationView.h; sourceTree = "<group>"; };
EE0954A71B97DDE0007F6964 /* FSCalendarSwiftExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FSCalendarSwiftExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
@ -191,6 +193,7 @@
30031AA91DCEA83200FC34FE /* DIYCalendarCell.swift */,
3024AF161E164F8C008B6001 /* DelegateAppearanceViewController.swift */,
305282C81E1608A100CC2877 /* FSCalendarScopeViewController.swift */,
CC7D16F62135D56100445693 /* FSWeeksSelectionCell.swift */,
EE0954AE1B97DDE0007F6964 /* InterfaceBuilderViewController.swift */,
F9DE053A1E3CCAE800902F7D /* CalendarConfigViewController.swift */,
30D458E81DB4E2DB00B163C7 /* LoadViewExampleViewController.swift */,
@ -271,7 +274,7 @@
TargetAttributes = {
EE0954A61B97DDE0007F6964 = {
CreatedOnToolsVersion = 6.4;
DevelopmentTeam = HZF422TY46;
DevelopmentTeam = SW5SL8G723;
LastSwiftMigration = 0900;
};
F9CB483C1E406A9200C92065 = {
@ -348,6 +351,7 @@
30F4C9481E07C3AD00D2EC4D /* FSCalendarWeekdayView.m in Sources */,
30F4C93E1E07C3AD00D2EC4D /* FSCalendarCell.m in Sources */,
30D458E91DB4E2DB00B163C7 /* LoadViewExampleViewController.swift in Sources */,
CC7D16F72135D56100445693 /* FSWeeksSelectionCell.swift in Sources */,
30031AAA1DCEA83200FC34FE /* DIYCalendarCell.swift in Sources */,
EE0954AD1B97DDE0007F6964 /* AppDelegate.swift in Sources */,
EE0954AF1B97DDE0007F6964 /* InterfaceBuilderViewController.swift in Sources */,
@ -497,11 +501,11 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = HZF422TY46;
DEVELOPMENT_TEAM = SW5SL8G723;
INFOPLIST_FILE = "$(SRCROOT)/FSCalendarSwiftExample/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.wenchaod.FSCalendarSwiftExample;
PRODUCT_BUNDLE_IDENTIFIER = com.dipendra.FSCalendarSwiftExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "FSCalendarSwiftExample/Objc-Bridge-Header.h";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
@ -514,11 +518,11 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = HZF422TY46;
DEVELOPMENT_TEAM = SW5SL8G723;
INFOPLIST_FILE = "$(SRCROOT)/FSCalendarSwiftExample/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.wenchaod.FSCalendarSwiftExample;
PRODUCT_BUNDLE_IDENTIFIER = com.dipendra.FSCalendarSwiftExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "FSCalendarSwiftExample/Objc-Bridge-Header.h";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;

View File

@ -278,28 +278,81 @@
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="blue" indentationWidth="10" reuseIdentifier="cell" textLabel="fGJ-HR-nLa" imageView="aHn-NP-iqx" style="IBUITableViewCellStyleDefault" id="mEc-Yq-s56">
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="cell_weeks_number" id="BOn-2g-TEq" customClass="FSWeeksSelectionCell" customModule="FSCalendarSwiftExample" customModuleProvider="target">
<rect key="frame" x="0.0" y="143.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="mEc-Yq-s56" id="Sz2-Op-ikR">
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" tableViewCell="BOn-2g-TEq" id="g8C-Vq-AUS">
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Lorem ipsum dolor sit er elit lamet" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="fGJ-HR-nLa">
<rect key="frame" x="48" y="0.0" width="312" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Week(s)" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="SzV-hA-k6t">
<rect key="frame" x="15" y="10.5" width="70" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="70" id="JQ1-Ua-2wz"/>
<constraint firstAttribute="height" constant="21" id="gWe-yI-EzF"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" image="icon_cat" id="aHn-NP-iqx">
<rect key="frame" x="15" y="14" width="18" height="15"/>
<autoresizingMask key="autoresizingMask"/>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="JKb-qi-PzU">
<rect key="frame" x="330" y="7.5" width="30" height="30"/>
<constraints>
<constraint firstAttribute="width" constant="30" id="Xi8-H6-zGT"/>
<constraint firstAttribute="height" constant="30" id="mtu-hy-nX4"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="21"/>
<state key="normal" title="+">
<color key="titleColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<connections>
<action selector="increaseButtonPressedWithSender:" destination="BOn-2g-TEq" eventType="touchUpInside" id="gZH-NH-KQJ"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="99" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iZF-jG-qtn">
<rect key="frame" x="301" y="11.5" width="21" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="21" id="U0i-mw-uLj"/>
<constraint firstAttribute="height" constant="21" id="aqK-F7-IL5"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="WJp-M4-Z65">
<rect key="frame" x="263" y="7.5" width="30" height="30"/>
<constraints>
<constraint firstAttribute="width" constant="30" id="62m-8w-vlE"/>
<constraint firstAttribute="height" constant="30" id="Usj-yo-VEX"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="21"/>
<state key="normal" title="-">
<color key="titleColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<connections>
<action selector="decreaseButtonPressedWithSender:" destination="BOn-2g-TEq" eventType="touchUpInside" id="fq9-Kk-b75"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="WJp-M4-Z65" firstAttribute="centerY" secondItem="g8C-Vq-AUS" secondAttribute="centerY" id="2TS-Pq-G91"/>
<constraint firstItem="SzV-hA-k6t" firstAttribute="leading" secondItem="g8C-Vq-AUS" secondAttribute="leading" constant="15" id="6BF-BJ-n8z"/>
<constraint firstItem="JKb-qi-PzU" firstAttribute="leading" secondItem="iZF-jG-qtn" secondAttribute="trailing" constant="8" id="IJA-3v-bix"/>
<constraint firstItem="iZF-jG-qtn" firstAttribute="centerY" secondItem="g8C-Vq-AUS" secondAttribute="centerY" id="QJz-20-xa3"/>
<constraint firstItem="JKb-qi-PzU" firstAttribute="centerY" secondItem="g8C-Vq-AUS" secondAttribute="centerY" id="nDZ-hd-57N"/>
<constraint firstItem="SzV-hA-k6t" firstAttribute="centerY" secondItem="g8C-Vq-AUS" secondAttribute="centerY" id="rnY-sl-Xfw"/>
<constraint firstAttribute="trailing" secondItem="JKb-qi-PzU" secondAttribute="trailing" constant="15" id="t4p-jD-j3p"/>
<constraint firstItem="iZF-jG-qtn" firstAttribute="leading" secondItem="WJp-M4-Z65" secondAttribute="trailing" constant="8" id="wE5-OG-EoD"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="decreaseButton" destination="WJp-M4-Z65" id="jF0-S8-kPw"/>
<outlet property="increaseButton" destination="JKb-qi-PzU" id="12U-sn-g7t"/>
<outlet property="weeksLabel" destination="iZF-jG-qtn" id="pjg-Yd-b6w"/>
</connections>
</tableViewCell>
</prototypes>
<sections/>
<connections>
<outlet property="dataSource" destination="E9E-eQ-Fo2" id="U76-Sy-x7x"/>
<outlet property="delegate" destination="E9E-eQ-Fo2" id="NsD-lC-L5v"/>
@ -333,7 +386,7 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ref-0a-lFG" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="844" y="-225"/>
<point key="canvasLocation" x="844" y="-225.33733133433284"/>
</scene>
<!--Configuration-->
<scene sceneID="EaG-2r-dag">
@ -496,7 +549,7 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Click to select" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Soy-1d-VzB">
<rect key="frame" x="15" y="0.0" width="345" height="43.5"/>
<rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -649,7 +702,6 @@
</scene>
</scenes>
<resources>
<image name="icon_cat" width="18" height="15"/>
<image name="icon_setting" width="24" height="24"/>
</resources>
</document>

View File

@ -16,6 +16,10 @@ class FSCalendarScopeExampleViewController: UIViewController, UITableViewDataSou
@IBOutlet weak var calendarHeightConstraint: NSLayoutConstraint!
fileprivate var gregorian: NSCalendar! = NSCalendar(calendarIdentifier: .gregorian)
fileprivate var scope: FSCalendarScope = .month
fileprivate var numberOfWeeks = 1
fileprivate lazy var dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy/MM/dd"
@ -41,7 +45,8 @@ class FSCalendarScopeExampleViewController: UIViewController, UITableViewDataSou
self.view.addGestureRecognizer(self.scopeGesture)
self.tableView.panGestureRecognizer.require(toFail: self.scopeGesture)
self.calendar.scope = .week
self.calendar.placeholderType = .fillHeadTail
self.calendar.appearance.headerMinimumDissolvedAlpha = 0
// For UITest
self.calendar.accessibilityIdentifier = "calendar"
@ -74,37 +79,50 @@ class FSCalendarScopeExampleViewController: UIViewController, UITableViewDataSou
}
func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
print("did select date \(self.dateFormatter.string(from: date))")
let selectedDates = calendar.selectedDates.map({self.dateFormatter.string(from: $0)})
print("selected dates is \(selectedDates)")
if monthPosition == .next || monthPosition == .previous {
calendar.setCurrentPage(date, animated: true)
}
//Reload calendar header view
calendar.calendarHeaderView.reloadData()
}
func calendarCurrentPageDidChange(_ calendar: FSCalendar) {
print("\(self.dateFormatter.string(from: calendar.currentPage))")
func maximumDate(for calendar: FSCalendar) -> Date {
return self.gregorian.date(byAdding: .weekOfYear, value: 104, to: Date(), options: NSCalendar.Options(rawValue: 0))!
}
func minimumDate(for calendar: FSCalendar) -> Date {
return self.gregorian.date(byAdding: .weekOfYear, value: -104, to: Date(), options: NSCalendar.Options(rawValue: 0))!
}
// MARK:- UITableViewDataSource
func numberOfSections(in tableView: UITableView) -> Int {
return 2
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return [2,20][section]
if self.scope == .month {
return 2
} else {
return 3
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let identifier = ["cell_month", "cell_week"][indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: identifier)!
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
let identifier = ["cell_month", "cell_week", "cell_weeks_number"][indexPath.row]
if indexPath.row == 2 {
guard let cell = tableView.dequeueReusableCell(withIdentifier: identifier) as? FSWeeksSelectionCell else {
return UITableViewCell()
}
cell.weeksLabel.text = "\(self.numberOfWeeks)"
cell.delegate = self
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: identifier)!
return cell
}
@ -113,8 +131,25 @@ class FSCalendarScopeExampleViewController: UIViewController, UITableViewDataSou
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if indexPath.section == 0 {
let scope: FSCalendarScope = (indexPath.row == 0) ? .month : .week
self.calendar.setScope(scope, animated: self.animationSwitch.isOn)
if indexPath.row == 0 && self.scope != .month {
self.scope = .month
self.calendar.setScope(self.scope, animated: self.animationSwitch.isOn)
//
tableView.beginUpdates()
tableView.deleteRows(at: [IndexPath(row: 2, section: 0) ], with: .automatic)
tableView.endUpdates()
} else if indexPath.row == 1 && self.scope != .week {
self.calendar.numberOfWeeks = self.numberOfWeeks
self.scope = .week
self.calendar.setScope(self.scope, animated: self.animationSwitch.isOn)
//
tableView.beginUpdates()
tableView.insertRows(at: [IndexPath(row: 2, section: 0) ], with: .automatic)
tableView.endUpdates()
}
}
}
@ -123,13 +158,56 @@ class FSCalendarScopeExampleViewController: UIViewController, UITableViewDataSou
}
// MARK:- Target actions
@IBAction func toggleClicked(sender: AnyObject) {
if self.calendar.scope == .month {
self.calendar.setScope(.week, animated: self.animationSwitch.isOn)
if self.scope == .month {
self.calendar.numberOfWeeks = self.numberOfWeeks
self.scope = .week
self.calendar.setScope(self.scope, animated: self.animationSwitch.isOn)
//
tableView.beginUpdates()
tableView.insertRows(at: [IndexPath(row: 2, section: 0) ], with: .automatic)
tableView.endUpdates()
} else {
self.calendar.setScope(.month, animated: self.animationSwitch.isOn)
self.scope = .month
self.calendar.setScope(self.scope, animated: self.animationSwitch.isOn)
//
tableView.beginUpdates()
tableView.deleteRows(at: [IndexPath(row: 2, section: 0) ], with: .automatic)
tableView.endUpdates()
}
}
}
extension FSCalendarScopeExampleViewController: FSWeeksSelectionCellDelegate {
func increaseButtonPressed(cell: FSWeeksSelectionCell) {
if self.numberOfWeeks < 4 {
self.numberOfWeeks += 1
cell.weeksLabel.text = "\(self.numberOfWeeks)"
if self.numberOfWeeks == 1 {
cell.decreaseButton.isEnabled = false
} else {
cell.decreaseButton.isEnabled = true
}
//
self.calendar.setNumberOfWeeks(self.numberOfWeeks, animated: self.animationSwitch.isOn)
}
}
func decreaseButtonPressed(cell: FSWeeksSelectionCell) {
if self.numberOfWeeks > 1 {
self.numberOfWeeks -= 1
cell.weeksLabel.text = "\(self.numberOfWeeks)"
if self.numberOfWeeks == 1 {
cell.decreaseButton.isEnabled = false
} else {
cell.decreaseButton.isEnabled = true
}
//
self.calendar.setNumberOfWeeks(self.numberOfWeeks, animated: self.animationSwitch.isOn)
}
}
}

View File

@ -0,0 +1,44 @@
//
// FSWeeksSelectionCell.swift
// FSCalendarSwiftExample
//
// Created by Dipendra Khatri on 8/28/18.
// Copyright © 2018 wenchao. All rights reserved.
//
import UIKit
protocol FSWeeksSelectionCellDelegate: class {
func increaseButtonPressed(cell: FSWeeksSelectionCell)
func decreaseButtonPressed(cell: FSWeeksSelectionCell)
}
class FSWeeksSelectionCell: UITableViewCell {
@IBOutlet weak var increaseButton: UIButton!
@IBOutlet weak var decreaseButton: UIButton!
@IBOutlet weak var weeksLabel: UILabel!
weak var delegate: FSWeeksSelectionCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
// MARK:- Target actions
@IBAction func increaseButtonPressed(sender: AnyObject) {
delegate?.increaseButtonPressed(cell: self)
}
@IBAction func decreaseButtonPressed(sender: AnyObject) {
delegate?.decreaseButtonPressed(cell: self)
}
}

View File

@ -402,6 +402,11 @@ IB_DESIGNABLE
*/
@property (readonly, nonatomic) NSArray<NSDate *> *selectedDates;
/**
The number of weeks to display when scope of the calendar is set to week.
*/
@property (assign, nonatomic) NSInteger numberOfWeeks;
/**
Reload the dates and appearance of the calendar.
*/
@ -415,6 +420,14 @@ IB_DESIGNABLE
*/
- (void)setScope:(FSCalendarScope)scope animated:(BOOL)animated;
/**
Change the number of weeks of the calendar while scope is FSCalendarScopeWeek. Make sure `-calendar:boundingRectWillChange:animated` is correctly adopted.
@param numberOfWeeks The target number of weeks to change.
@param animated YES if you want to animate the change; NO if the change should be immediate.
*/
- (void)setNumberOfWeeks:(NSInteger)numberOfWeeks animated:(BOOL)animated;
/**
Selects a given date in the calendar.

View File

@ -184,6 +184,8 @@ typedef NS_ENUM(NSUInteger, FSCalendarOrientation) {
_orientation = self.currentCalendarOrientation;
_placeholderType = FSCalendarPlaceholderTypeFillSixRows;
_numberOfWeeks = 1;
_dataSourceProxy = [FSCalendarDelegationFactory dataSourceProxy];
_delegateProxy = [FSCalendarDelegationFactory delegateProxy];
@ -314,7 +316,7 @@ typedef NS_ENUM(NSUInteger, FSCalendarOrientation) {
break;
}
case FSCalendarScopeWeek: {
CGFloat contentHeight = rowHeight + padding*2;
CGFloat contentHeight = rowHeight*self.numberOfWeeks + padding*2;
_daysContainer.frame = CGRectMake(0, headerHeight+weekdayHeight, self.fs_width, contentHeight);
_collectionView.frame = CGRectMake(0, 0, _daysContainer.fs_width, contentHeight);
break;
@ -362,7 +364,7 @@ typedef NS_ENUM(NSUInteger, FSCalendarOrientation) {
return CGSizeMake(size.width, height);
}
case FSCalendarScopeWeek: {
CGFloat height = weekdayHeight + headerHeight + rowHeight + paddings;
CGFloat height = weekdayHeight + headerHeight + rowHeight*self.numberOfWeeks + paddings;
return CGSizeMake(size.width, height);
}
}
@ -391,7 +393,7 @@ typedef NS_ENUM(NSUInteger, FSCalendarOrientation) {
return 42;
}
case FSCalendarScopeWeek: {
return 7;
return self.numberOfWeeks*7;
}
}
return 7;
@ -613,7 +615,7 @@ typedef NS_ENUM(NSUInteger, FSCalendarOrientation) {
}
case FSCalendarScopeWeek: {
NSDate *minimumPage = [self.gregorian fs_firstDayOfWeek:_minimumDate];
targetPage = [self.gregorian dateByAddingUnit:NSCalendarUnitWeekOfYear value:sections toDate:minimumPage options:0];
targetPage = [self.gregorian dateByAddingUnit:NSCalendarUnitWeekOfYear value:(sections*self.numberOfWeeks) toDate:minimumPage options:0];
break;
}
}
@ -678,6 +680,11 @@ typedef NS_ENUM(NSUInteger, FSCalendarOrientation) {
return NO;
}
- (void)setNumberOfWeeks:(NSInteger)numberOfWeeks
{
[self setNumberOfWeeks:numberOfWeeks animated:NO];
}
- (void)setScope:(FSCalendarScope)scope
{
[self setScope:scope animated:NO];
@ -999,6 +1006,16 @@ typedef NS_ENUM(NSUInteger, FSCalendarOrientation) {
[self.collectionView reloadData];
}
- (void)setNumberOfWeeks:(NSInteger)numberOfWeeks animated:(BOOL)animated
{
_numberOfWeeks = numberOfWeeks;
if (_scope == FSCalendarScopeWeek) {
if (self.transitionCoordinator.state != FSCalendarTransitionStateIdle) return;
[self.transitionCoordinator performBoundingRectTransitionForScope:self.scope animated:animated];
}
}
- (void)setScope:(FSCalendarScope)scope animated:(BOOL)animated
{
if (self.floatingMode) return;
@ -1251,7 +1268,7 @@ typedef NS_ENUM(NSUInteger, FSCalendarOrientation) {
case FSCalendarScopeMonth:
return ![self.gregorian isDate:date equalToDate:_currentPage toUnitGranularity:NSCalendarUnitMonth];
case FSCalendarScopeWeek:
return ![self.gregorian isDate:date equalToDate:_currentPage toUnitGranularity:NSCalendarUnitWeekOfYear];
return ![date isInRange:_currentPage endDate:[self.gregorian fs_lastDayOfWeek:[self.gregorian fs_dayByAddingWeeks:(_numberOfWeeks - 1) toDate:_currentPage]]];
}
}

View File

@ -128,7 +128,8 @@
break;
}
case FSCalendarScopeWeek: {
section = [self.gregorian components:NSCalendarUnitWeekOfYear fromDate:[self.gregorian fs_firstDayOfWeek:self.minimumDate] toDate:[self.gregorian fs_firstDayOfWeek:date] options:0].weekOfYear;
NSInteger weekOfYear = [self.gregorian components:NSCalendarUnitWeekOfYear fromDate:[self.gregorian fs_firstDayOfWeek:self.minimumDate] toDate:[self.gregorian fs_firstDayOfWeek:date] options:0].weekOfYear;
section = weekOfYear/self.calendar.numberOfWeeks;
item = (([self.gregorian component:NSCalendarUnitWeekday fromDate:date] - self.gregorian.firstWeekday) + 7) % 7;
break;
}
@ -190,7 +191,7 @@
NSNumber *key = @(section);
NSDate *week = self.weeks[key];
if (!week) {
week = [self.gregorian dateByAddingUnit:NSCalendarUnitWeekOfYear value:section toDate:[self.gregorian fs_firstDayOfWeek:self.minimumDate] options:0];
week = [self.gregorian dateByAddingUnit:NSCalendarUnitWeekOfYear value:(section*self.calendar.numberOfWeeks) toDate:[self.gregorian fs_firstDayOfWeek:self.minimumDate] options:0];
self.weeks[key] = week;
}
return week;
@ -203,7 +204,7 @@
return self.numberOfMonths;
}
case FSCalendarScopeWeek: {
return self.numberOfWeeks;
return (self.numberOfWeeks/self.calendar.numberOfWeeks) + 1;
}
}
}
@ -236,7 +237,7 @@
- (NSInteger)numberOfRowsInSection:(NSInteger)section
{
if (self.calendar.transitionCoordinator.representingScope == FSCalendarScopeWeek) return 1;
if (self.calendar.transitionCoordinator.representingScope == FSCalendarScopeWeek) return self.calendar.numberOfWeeks;
NSDate *month = [self monthForSection:section];
return [self numberOfRowsInMonth:month];
}
@ -271,7 +272,7 @@
- (void)reloadSections
{
self.numberOfMonths = [self.gregorian components:NSCalendarUnitMonth fromDate:[self.gregorian fs_firstDayOfMonth:self.minimumDate] toDate:self.maximumDate options:0].month+1;
self.numberOfWeeks = [self.gregorian components:NSCalendarUnitWeekOfYear fromDate:[self.gregorian fs_firstDayOfWeek:self.minimumDate] toDate:self.maximumDate options:0].weekOfYear+1;
self.numberOfWeeks = [self.gregorian components:NSCalendarUnitWeekOfYear fromDate:[self.gregorian fs_firstDayOfWeek:self.minimumDate] toDate:self.maximumDate options:0].weekOfYear;
[self clearCaches];
}

View File

@ -126,7 +126,7 @@
break;
}
case FSCalendarScopeWeek: {
height = (self.collectionView.fs_height-self.sectionInsets.top-self.sectionInsets.bottom);
height = (self.collectionView.fs_height-self.sectionInsets.top-self.sectionInsets.bottom)/self.calendar.numberOfWeeks;
break;
}
default:
@ -167,7 +167,7 @@
// Calculate item heights and tops
free(self.heights);
self.heights = ({
NSInteger rowCount = self.calendar.transitionCoordinator.representingScope == FSCalendarScopeWeek ? 1 : 6;
NSInteger rowCount = self.calendar.transitionCoordinator.representingScope == FSCalendarScopeWeek ? self.calendar.numberOfWeeks : 6;
size_t rowSize = sizeof(CGFloat)*rowCount;
CGFloat *heights = malloc(rowSize);
if (!self.calendar.floatingMode) {
@ -183,7 +183,7 @@
free(self.tops);
self.tops = ({
NSInteger rowCount = self.calendar.transitionCoordinator.representingScope == FSCalendarScopeWeek ? 1 : 6;
NSInteger rowCount = self.calendar.transitionCoordinator.representingScope == FSCalendarScopeWeek ? self.calendar.numberOfWeeks : 6;
size_t rowSize = sizeof(CGFloat)*rowCount;
CGFloat *tops = malloc(rowSize);
tops[0] = self.sectionInsets.top;
@ -292,7 +292,7 @@
endColumn;
});
NSInteger numberOfRows = self.calendar.transitionCoordinator.representingScope == FSCalendarScopeMonth ? 6 : 1;
NSInteger numberOfRows = self.calendar.transitionCoordinator.representingScope == FSCalendarScopeMonth ? 6 : self.calendar.numberOfWeeks;
for (NSInteger column = startColumn; column <= endColumn; column++) {
for (NSInteger row = 0; row < numberOfRows; row++) {

View File

@ -23,6 +23,12 @@ NS_ASSUME_NONNULL_BEGIN
@end
@interface NSDate (FSCalendarExtensions)
- (BOOL)isInRange:(NSDate *)startDate endDate:(NSDate *)endDate;
@end
@interface CALayer (FSCalendarExtensions)
@ -44,6 +50,7 @@ NS_ASSUME_NONNULL_BEGIN
- (nullable NSDate *)fs_firstDayOfWeek:(NSDate *)week;
- (nullable NSDate *)fs_lastDayOfWeek:(NSDate *)week;
- (nullable NSDate *)fs_middleDayOfWeek:(NSDate *)week;
- (nullable NSDate *)fs_dayByAddingWeeks:(NSInteger)weeks toDate:(NSDate *)date;
- (NSInteger)fs_numberOfDaysInMonth:(NSDate *)month;
@end

View File

@ -73,6 +73,20 @@
@end
@implementation NSDate (FSCalendarExtensions)
- (BOOL)isInRange:(NSDate *)startDate endDate:(NSDate *)endDate
{
if ([self compare:startDate] == NSOrderedAscending)
return NO;
if ([self compare:endDate] == NSOrderedDescending)
return NO;
return YES;
}
@end
@implementation CALayer (FSCalendarExtensions)
@ -211,6 +225,12 @@
return days.length;
}
- (nullable NSDate *)fs_dayByAddingWeeks:(NSInteger)weeks toDate:(NSDate *)date
{
if (!date) return nil;
return [self dateByAddingUnit:NSCalendarUnitWeekOfYear value:weeks toDate:date options:0];
}
- (NSDateComponents *)fs_privateComponents
{
NSDateComponents *components = objc_getAssociatedObject(self, _cmd);

View File

@ -179,9 +179,22 @@
if ((indexPath.item == 0 || indexPath.item == [self.collectionView numberOfItemsInSection:0] - 1)) {
text = nil;
} else {
NSDate *firstPage = [self.calendar.gregorian fs_middleDayOfWeek:self.calendar.minimumDate];
NSDate *date = [self.calendar.gregorian dateByAddingUnit:NSCalendarUnitWeekOfYear value:indexPath.item-1 toDate:firstPage options:0];
text = [_calendar.formatter stringFromDate:date];
//Get first day of the first page
NSDate *firstPageHeadDate = [self.calendar.gregorian fs_firstDayOfWeek:self.calendar.minimumDate];
//Get first day of the current page
NSDate *currentPageHeadDate = [self.calendar.gregorian dateByAddingUnit:NSCalendarUnitWeekOfYear
value:(indexPath.item-1) * self.calendar.numberOfWeeks
toDate:firstPageHeadDate
options:0];
//Get mid day of the current page
NSDate *currentPageMidDate = [self.calendar.gregorian dateByAddingUnit:NSCalendarUnitDay
value:((self.calendar.numberOfWeeks*7)/2)
toDate:currentPageHeadDate
options:0];
text = [_calendar.formatter stringFromDate:currentPageMidDate];
}
break;
}

View File

@ -28,6 +28,7 @@ typedef NS_ENUM(NSUInteger, FSCalendarTransitionState) {
- (void)performScopeTransitionFromScope:(FSCalendarScope)fromScope toScope:(FSCalendarScope)toScope animated:(BOOL)animated;
- (void)performBoundingRectTransitionFromMonth:(NSDate *)fromMonth toMonth:(NSDate *)toMonth duration:(CGFloat)duration;
- (void)performBoundingRectTransitionForScope:(FSCalendarScope)scope animated:(BOOL)animated;
- (CGRect)boundingRectForScope:(FSCalendarScope)scope page:(NSDate *)page;
- (void)handleScopeGesture:(id)sender;

View File

@ -215,6 +215,37 @@
}
}
- (void)performBoundingRectTransitionForScope:(FSCalendarScope)scope animated:(BOOL)animated
{
CGFloat animationDuration = (animated) ? 0.25 : 0;
FSCalendarTransitionAttributes *attr = [self createTransitionAttributesTargetingScope:FSCalendarScopeWeek];
self.transitionAttributes = attr;
[self.calendar fs_setVariable:attr.targetPage forKey:@"_currentPage"];
CGRect bounds = [self boundingRectForScope:scope page:self.calendar.currentPage];
self.state = FSCalendarTransitionStateChanging;
void (^completion)(BOOL) = ^(BOOL finished) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.state = FSCalendarTransitionStateIdle;
self.calendar.needsAdjustingViewFrame = YES;
[self.collectionView reloadData];
[self.calendar.calendarHeaderView reloadData];
[self.calendar setNeedsLayout];
[self.calendar layoutIfNeeded];
});
};
if (FSCalendarInAppExtension) {
// Detect today extension: http://stackoverflow.com/questions/25048026/ios-8-extension-how-to-detect-running
[self boundingRectWillChange:bounds animated:YES];
completion(YES);
} else {
[UIView animateWithDuration:animationDuration delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
[self boundingRectWillChange:bounds animated:YES];
} completion:completion];
}
}
#pragma mark - Private properties
- (void)performTransitionCompletionAnimated:(BOOL)animated
@ -256,7 +287,14 @@
if (targetScope == FSCalendarScopeWeek) {
[dates addObject:self.calendar.currentPage];
} else {
[dates addObject:[self.calendar.gregorian dateByAddingUnit:NSCalendarUnitDay value:3 toDate:self.calendar.currentPage options:0]];
//Moving from Week to Month Scope
//Get mid day of the current page
NSDate *currentPageMidDate = [self.calendar.gregorian dateByAddingUnit:NSCalendarUnitDay
value:((self.calendar.numberOfWeeks*7)/2)
toDate:self.calendar.currentPage
options:0];
[dates addObject:currentPageMidDate];
}
dates.copy;
});
@ -274,7 +312,21 @@
coordinate.row;
});
attributes.targetPage = ({
NSDate *targetPage = targetScope == FSCalendarScopeMonth ? [self.calendar.gregorian fs_firstDayOfMonth:attributes.focusedDate] : [self.calendar.gregorian fs_middleDayOfWeek:attributes.focusedDate];
NSDate *targetPage;
if (targetScope == FSCalendarScopeMonth) {
targetPage = [self.calendar.gregorian fs_firstDayOfMonth:attributes.focusedDate];
} else {
//Moving from Month to Week Scope
//This should be the first day of the page
NSIndexPath *indexPath = [self.calendar.calculator indexPathForDate:attributes.focusedDate scope:FSCalendarScopeWeek];
NSDate *minimumPage = [self.calendar.gregorian fs_firstDayOfWeek:self.calendar.minimumDate];
targetPage = [self.calendar.gregorian dateByAddingUnit:NSCalendarUnitWeekOfYear
value:(indexPath.section*self.calendar.numberOfWeeks)
toDate:minimumPage
options:0];
}
targetPage;
});
attributes.targetBounds = [self boundingRectForScope:attributes.targetScope page:attributes.targetPage];