Versión estable 2018
This commit is contained in:
parent
e2649847ef
commit
459227bf4d
|
@ -7,7 +7,7 @@
|
||||||
<key>BaseLibrary.xcscheme</key>
|
<key>BaseLibrary.xcscheme</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>0</integer>
|
<integer>6</integer>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -2,5 +2,8 @@ use_frameworks!
|
||||||
|
|
||||||
platform :ios, '8.0'
|
platform :ios, '8.0'
|
||||||
pod 'FSCalendar'
|
pod 'FSCalendar'
|
||||||
|
target "Verdnaturaventas" do
|
||||||
|
pod 'Firebase/Core'
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,51 @@
|
||||||
PODS:
|
PODS:
|
||||||
- FSCalendar (2.0.1)
|
- Firebase/Core (5.3.0):
|
||||||
|
- Firebase/CoreOnly
|
||||||
|
- FirebaseAnalytics (= 5.0.1)
|
||||||
|
- Firebase/CoreOnly (5.3.0):
|
||||||
|
- FirebaseCore (= 5.0.4)
|
||||||
|
- FirebaseAnalytics (5.0.1):
|
||||||
|
- FirebaseCore (~> 5.0)
|
||||||
|
- FirebaseInstanceID (~> 3.0)
|
||||||
|
- "GoogleToolboxForMac/NSData+zlib (~> 2.1)"
|
||||||
|
- nanopb (~> 0.3)
|
||||||
|
- FirebaseCore (5.0.4):
|
||||||
|
- "GoogleToolboxForMac/NSData+zlib (~> 2.1)"
|
||||||
|
- FirebaseInstanceID (3.1.1):
|
||||||
|
- FirebaseCore (~> 5.0)
|
||||||
|
- FSCalendar (2.7.9)
|
||||||
|
- GoogleToolboxForMac/Defines (2.1.4)
|
||||||
|
- "GoogleToolboxForMac/NSData+zlib (2.1.4)":
|
||||||
|
- GoogleToolboxForMac/Defines (= 2.1.4)
|
||||||
|
- nanopb (0.3.8):
|
||||||
|
- nanopb/decode (= 0.3.8)
|
||||||
|
- nanopb/encode (= 0.3.8)
|
||||||
|
- nanopb/decode (0.3.8)
|
||||||
|
- nanopb/encode (0.3.8)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
|
- Firebase/Core
|
||||||
- FSCalendar
|
- FSCalendar
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC REPOS:
|
||||||
FSCalendar: 18117308f5876a8f6f0fe572085d543580c2c5e5
|
https://github.com/cocoapods/specs.git:
|
||||||
|
- Firebase
|
||||||
|
- FirebaseAnalytics
|
||||||
|
- FirebaseCore
|
||||||
|
- FirebaseInstanceID
|
||||||
|
- FSCalendar
|
||||||
|
- GoogleToolboxForMac
|
||||||
|
- nanopb
|
||||||
|
|
||||||
COCOAPODS: 0.39.0
|
SPEC CHECKSUMS:
|
||||||
|
Firebase: 68afeeb05461db02d7c9e3215cda28068670f4aa
|
||||||
|
FirebaseAnalytics: b3628aea54c50464c32c393fb2ea032566e7ecc2
|
||||||
|
FirebaseCore: 62f1b792a49bb9e8b4073f24606d2c93ffc352f0
|
||||||
|
FirebaseInstanceID: f3f0657372592ecdfdfe2cac604a5a75758376a6
|
||||||
|
FSCalendar: a04b09f16f811bc92e82f3cf852a15225233b9d5
|
||||||
|
GoogleToolboxForMac: 91c824d21e85b31c2aae9bb011c5027c9b4e738f
|
||||||
|
nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3
|
||||||
|
|
||||||
|
PODFILE CHECKSUM: 0645cd0766eb63ba97b1572c6e02dd27818977f2
|
||||||
|
|
||||||
|
COCOAPODS: 1.5.3
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
//
|
|
||||||
// CALayer+FSExtension.h
|
|
||||||
// FSCalendar
|
|
||||||
//
|
|
||||||
// Created by dingwenchao on 2/3/16.
|
|
||||||
// Copyright © 2016 wenchaoios. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <QuartzCore/QuartzCore.h>
|
|
||||||
|
|
||||||
@interface CALayer (FSExtension)
|
|
||||||
|
|
||||||
@property (nonatomic) CGFloat fs_width;
|
|
||||||
@property (nonatomic) CGFloat fs_height;
|
|
||||||
|
|
||||||
@property (nonatomic) CGFloat fs_top;
|
|
||||||
@property (nonatomic) CGFloat fs_left;
|
|
||||||
@property (nonatomic) CGFloat fs_bottom;
|
|
||||||
@property (nonatomic) CGFloat fs_right;
|
|
||||||
|
|
||||||
@end
|
|
|
@ -1,73 +0,0 @@
|
||||||
//
|
|
||||||
// CALayer+FSExtension.m
|
|
||||||
// FSCalendar
|
|
||||||
//
|
|
||||||
// Created by dingwenchao on 2/3/16.
|
|
||||||
// Copyright © 2016 wenchaoios. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "CALayer+FSExtension.h"
|
|
||||||
|
|
||||||
@implementation CALayer (FSExtension)
|
|
||||||
|
|
||||||
- (CGFloat)fs_width
|
|
||||||
{
|
|
||||||
return CGRectGetWidth(self.frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFs_width:(CGFloat)fs_width
|
|
||||||
{
|
|
||||||
self.frame = CGRectMake(self.fs_left, self.fs_top, fs_width, self.fs_height);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)fs_height
|
|
||||||
{
|
|
||||||
return CGRectGetHeight(self.frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFs_height:(CGFloat)fs_height
|
|
||||||
{
|
|
||||||
self.frame = CGRectMake(self.fs_left, self.fs_top, self.fs_width, fs_height);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)fs_top
|
|
||||||
{
|
|
||||||
return CGRectGetMinY(self.frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFs_top:(CGFloat)fs_top
|
|
||||||
{
|
|
||||||
self.frame = CGRectMake(self.fs_left, fs_top, self.fs_width, self.fs_height);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)fs_bottom
|
|
||||||
{
|
|
||||||
return CGRectGetMaxY(self.frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFs_bottom:(CGFloat)fs_bottom
|
|
||||||
{
|
|
||||||
self.fs_top = fs_bottom - self.fs_height;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)fs_left
|
|
||||||
{
|
|
||||||
return CGRectGetMinX(self.frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFs_left:(CGFloat)fs_left
|
|
||||||
{
|
|
||||||
self.frame = CGRectMake(fs_left, self.fs_top, self.fs_width, self.fs_height);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)fs_right
|
|
||||||
{
|
|
||||||
return CGRectGetMaxX(self.frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFs_right:(CGFloat)fs_right
|
|
||||||
{
|
|
||||||
self.fs_left = self.fs_right - self.fs_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
|
@ -0,0 +1,247 @@
|
||||||
|
//
|
||||||
|
// FSCalendar+Deprecated.m
|
||||||
|
// FSCalendar
|
||||||
|
//
|
||||||
|
// Created by dingwenchao on 4/29/16.
|
||||||
|
// Copyright © 2016 Wenchao Ding. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "FSCalendar.h"
|
||||||
|
#import "FSCalendarExtensions.h"
|
||||||
|
#import "FSCalendarDynamicHeader.h"
|
||||||
|
|
||||||
|
#pragma mark - Deprecate
|
||||||
|
|
||||||
|
@implementation FSCalendar (Deprecated)
|
||||||
|
|
||||||
|
@dynamic identifier, lineHeightMultiplier;
|
||||||
|
|
||||||
|
- (void)setShowsPlaceholders:(BOOL)showsPlaceholders
|
||||||
|
{
|
||||||
|
self.placeholderType = showsPlaceholders ? FSCalendarPlaceholderTypeFillSixRows : FSCalendarPlaceholderTypeNone;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)showsPlaceholders
|
||||||
|
{
|
||||||
|
return self.placeholderType == FSCalendarPlaceholderTypeFillSixRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Public methods
|
||||||
|
|
||||||
|
- (NSInteger)yearOfDate:(NSDate *)date
|
||||||
|
{
|
||||||
|
if (!date) return NSNotFound;
|
||||||
|
NSDateComponents *component = [self.gregorian components:NSCalendarUnitYear fromDate:date];
|
||||||
|
return component.year;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)monthOfDate:(NSDate *)date
|
||||||
|
{
|
||||||
|
if (!date) return NSNotFound;
|
||||||
|
NSDateComponents *component = [self.gregorian components:NSCalendarUnitMonth
|
||||||
|
fromDate:date];
|
||||||
|
return component.month;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)dayOfDate:(NSDate *)date
|
||||||
|
{
|
||||||
|
if (!date) return NSNotFound;
|
||||||
|
NSDateComponents *component = [self.gregorian components:NSCalendarUnitDay
|
||||||
|
fromDate:date];
|
||||||
|
return component.day;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)weekdayOfDate:(NSDate *)date
|
||||||
|
{
|
||||||
|
if (!date) return NSNotFound;
|
||||||
|
NSDateComponents *component = [self.gregorian components:NSCalendarUnitWeekday fromDate:date];
|
||||||
|
return component.weekday;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)weekOfDate:(NSDate *)date
|
||||||
|
{
|
||||||
|
if (!date) return NSNotFound;
|
||||||
|
NSDateComponents *component = [self.gregorian components:NSCalendarUnitWeekOfYear fromDate:date];
|
||||||
|
return component.weekOfYear;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDate *)dateByIgnoringTimeComponentsOfDate:(NSDate *)date
|
||||||
|
{
|
||||||
|
if (!date) return nil;
|
||||||
|
NSDateComponents *components = [self.gregorian components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:date];
|
||||||
|
return [self.gregorian dateFromComponents:components];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDate *)tomorrowOfDate:(NSDate *)date
|
||||||
|
{
|
||||||
|
if (!date) return nil;
|
||||||
|
NSDateComponents *components = [self.gregorian components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour fromDate:date];
|
||||||
|
components.day++;
|
||||||
|
components.hour = FSCalendarDefaultHourComponent;
|
||||||
|
return [self.gregorian dateFromComponents:components];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDate *)yesterdayOfDate:(NSDate *)date
|
||||||
|
{
|
||||||
|
if (!date) return nil;
|
||||||
|
NSDateComponents *components = [self.gregorian components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour fromDate:date];
|
||||||
|
components.day--;
|
||||||
|
components.hour = FSCalendarDefaultHourComponent;
|
||||||
|
return [self.gregorian dateFromComponents:components];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDate *)dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day
|
||||||
|
{
|
||||||
|
NSDateComponents *components = self.components;
|
||||||
|
components.year = year;
|
||||||
|
components.month = month;
|
||||||
|
components.day = day;
|
||||||
|
components.hour = FSCalendarDefaultHourComponent;
|
||||||
|
NSDate *date = [self.gregorian dateFromComponents:components];
|
||||||
|
components.year = NSIntegerMax;
|
||||||
|
components.month = NSIntegerMax;
|
||||||
|
components.day = NSIntegerMax;
|
||||||
|
components.hour = NSIntegerMax;
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDate *)dateByAddingYears:(NSInteger)years toDate:(NSDate *)date
|
||||||
|
{
|
||||||
|
if (!date) return nil;
|
||||||
|
NSDateComponents *components = self.components;
|
||||||
|
components.year = years;
|
||||||
|
NSDate *d = [self.gregorian dateByAddingComponents:components toDate:date options:0];
|
||||||
|
components.year = NSIntegerMax;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDate *)dateBySubstractingYears:(NSInteger)years fromDate:(NSDate *)date
|
||||||
|
{
|
||||||
|
if (!date) return nil;
|
||||||
|
return [self dateByAddingYears:-years toDate:date];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDate *)dateByAddingMonths:(NSInteger)months toDate:(NSDate *)date
|
||||||
|
{
|
||||||
|
if (!date) return nil;
|
||||||
|
NSDateComponents *components = self.components;
|
||||||
|
components.month = months;
|
||||||
|
NSDate *d = [self.gregorian dateByAddingComponents:components toDate:date options:0];
|
||||||
|
components.month = NSIntegerMax;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDate *)dateBySubstractingMonths:(NSInteger)months fromDate:(NSDate *)date
|
||||||
|
{
|
||||||
|
return [self dateByAddingMonths:-months toDate:date];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDate *)dateByAddingWeeks:(NSInteger)weeks toDate:(NSDate *)date
|
||||||
|
{
|
||||||
|
if (!date) return nil;
|
||||||
|
NSDateComponents *components = self.components;
|
||||||
|
components.weekOfYear = weeks;
|
||||||
|
NSDate *d = [self.gregorian dateByAddingComponents:components toDate:date options:0];
|
||||||
|
components.weekOfYear = NSIntegerMax;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDate *)dateBySubstractingWeeks:(NSInteger)weeks fromDate:(NSDate *)date
|
||||||
|
{
|
||||||
|
return [self dateByAddingWeeks:-weeks toDate:date];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDate *)dateByAddingDays:(NSInteger)days toDate:(NSDate *)date
|
||||||
|
{
|
||||||
|
if (!date) return nil;
|
||||||
|
NSDateComponents *components = self.components;
|
||||||
|
components.day = days;
|
||||||
|
NSDate *d = [self.gregorian dateByAddingComponents:components toDate:date options:0];
|
||||||
|
components.day = NSIntegerMax;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDate *)dateBySubstractingDays:(NSInteger)days fromDate:(NSDate *)date
|
||||||
|
{
|
||||||
|
return [self dateByAddingDays:-days toDate:date];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)yearsFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate
|
||||||
|
{
|
||||||
|
NSDateComponents *components = [self.gregorian components:NSCalendarUnitYear
|
||||||
|
fromDate:fromDate
|
||||||
|
toDate:toDate
|
||||||
|
options:0];
|
||||||
|
return components.year;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)monthsFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate
|
||||||
|
{
|
||||||
|
NSDateComponents *components = [self.gregorian components:NSCalendarUnitMonth
|
||||||
|
fromDate:fromDate
|
||||||
|
toDate:toDate
|
||||||
|
options:0];
|
||||||
|
return components.month;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)weeksFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate
|
||||||
|
{
|
||||||
|
NSDateComponents *components = [self.gregorian components:NSCalendarUnitWeekOfYear
|
||||||
|
fromDate:fromDate
|
||||||
|
toDate:toDate
|
||||||
|
options:0];
|
||||||
|
return components.weekOfYear;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)daysFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate
|
||||||
|
{
|
||||||
|
NSDateComponents *components = [self.gregorian components:NSCalendarUnitDay
|
||||||
|
fromDate:fromDate
|
||||||
|
toDate:toDate
|
||||||
|
options:0];
|
||||||
|
return components.day;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isDate:(NSDate *)date1 equalToDate:(NSDate *)date2 toCalendarUnit:(FSCalendarUnit)unit
|
||||||
|
{
|
||||||
|
switch (unit) {
|
||||||
|
case FSCalendarUnitMonth:
|
||||||
|
return [self.gregorian isDate:date1 equalToDate:date2 toUnitGranularity:NSCalendarUnitMonth];
|
||||||
|
case FSCalendarUnitWeekOfYear:
|
||||||
|
return [self.gregorian isDate:date1 equalToDate:date2 toUnitGranularity:NSCalendarUnitYear];
|
||||||
|
case FSCalendarUnitDay:
|
||||||
|
return [self.gregorian isDate:date1 inSameDayAsDate:date2];
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isDateInToday:(NSDate *)date
|
||||||
|
{
|
||||||
|
return [self isDate:date equalToDate:[NSDate date] toCalendarUnit:FSCalendarUnitDay];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setIdentifier:(NSString *)identifier
|
||||||
|
{
|
||||||
|
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:identifier];
|
||||||
|
[self setValue:gregorian forKey:@"gregorian"];
|
||||||
|
[self fs_performSelector:NSSelectorFromString(@"invalidateDateTools") withObjects:nil, nil];
|
||||||
|
|
||||||
|
if ([[self valueForKey:@"hasValidateVisibleLayout"] boolValue]) {
|
||||||
|
[self reloadData];
|
||||||
|
}
|
||||||
|
[self fs_setVariable:[self.gregorian dateBySettingHour:0 minute:0 second:0 ofDate:self.minimumDate options:0] forKey:@"_minimumDate"];
|
||||||
|
[self fs_setVariable:[self.gregorian dateBySettingHour:0 minute:0 second:0 ofDate:self.currentPage options:0] forKey:@"_currentPage"];
|
||||||
|
[self fs_performSelector:NSSelectorFromString(@"scrollToPageForDate:animated") withObjects:self.today, @NO, nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)identifier
|
||||||
|
{
|
||||||
|
return self.gregorian.calendarIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setLineHeightMultiplier:(CGFloat)lineHeightMultiplier
|
||||||
|
{
|
||||||
|
self.rowHeight = FSCalendarStandardRowHeight*MAX(1, FSCalendarDeviceIsIPad*1.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -1,55 +0,0 @@
|
||||||
//
|
|
||||||
// FSCalendar+IBExtension.h
|
|
||||||
// FSCalendar
|
|
||||||
//
|
|
||||||
// Created by Wenchao Ding on 8/14/15.
|
|
||||||
// Copyright © 2016 Wenchao Ding. All rights reserved.
|
|
||||||
//
|
|
||||||
// 注意: 这些方法仅仅为了在IB中使用,不建议直接调用。这些方法在calendar.appearance中使用。如: calendar.appearance.eventColor
|
|
||||||
// Warning: For IB usage only. Directly calling these methods is deprecated. Use calendar.appearance(FSCalendarAppearance) instead
|
|
||||||
|
|
||||||
#import "FSCalendar.h"
|
|
||||||
#import "FSCalendarConstance.h"
|
|
||||||
|
|
||||||
IB_DESIGNABLE
|
|
||||||
@interface FSCalendar (IBExtension)
|
|
||||||
|
|
||||||
@property (assign, nonatomic) IBInspectable BOOL adjustsFontSizeToFitContentSize;
|
|
||||||
@property (assign, nonatomic) IBInspectable CGFloat titleTextSize;
|
|
||||||
@property (assign, nonatomic) IBInspectable CGFloat subtitleTextSize;
|
|
||||||
@property (assign, nonatomic) IBInspectable CGFloat weekdayTextSize;
|
|
||||||
@property (assign, nonatomic) IBInspectable CGFloat headerTitleTextSize;
|
|
||||||
|
|
||||||
@property (strong, nonatomic) IBInspectable UIColor *eventColor;
|
|
||||||
@property (strong, nonatomic) IBInspectable UIColor *weekdayTextColor;
|
|
||||||
|
|
||||||
@property (strong, nonatomic) IBInspectable UIColor *headerTitleColor;
|
|
||||||
@property (strong, nonatomic) IBInspectable NSString *headerDateFormat;
|
|
||||||
@property (assign, nonatomic) IBInspectable CGFloat headerMinimumDissolvedAlpha;
|
|
||||||
|
|
||||||
@property (strong, nonatomic) IBInspectable UIColor *titleDefaultColor;
|
|
||||||
@property (strong, nonatomic) IBInspectable UIColor *titleSelectionColor;
|
|
||||||
@property (strong, nonatomic) IBInspectable UIColor *titleTodayColor;
|
|
||||||
@property (strong, nonatomic) IBInspectable UIColor *titlePlaceholderColor;
|
|
||||||
@property (strong, nonatomic) IBInspectable UIColor *titleWeekendColor;
|
|
||||||
|
|
||||||
@property (strong, nonatomic) IBInspectable UIColor *subtitleDefaultColor;
|
|
||||||
@property (strong, nonatomic) IBInspectable UIColor *subtitleSelectionColor;
|
|
||||||
@property (strong, nonatomic) IBInspectable UIColor *subtitleTodayColor;
|
|
||||||
@property (strong, nonatomic) IBInspectable UIColor *subtitlePlaceholderColor;
|
|
||||||
@property (strong, nonatomic) IBInspectable UIColor *subtitleWeekendColor;
|
|
||||||
|
|
||||||
@property (strong, nonatomic) IBInspectable UIColor *selectionColor;
|
|
||||||
@property (strong, nonatomic) IBInspectable UIColor *todayColor;
|
|
||||||
@property (strong, nonatomic) IBInspectable UIColor *todaySelectionColor;
|
|
||||||
|
|
||||||
@property (strong, nonatomic) IBInspectable UIColor *borderDefaultColor;
|
|
||||||
@property (strong, nonatomic) IBInspectable UIColor *borderSelectionColor;
|
|
||||||
|
|
||||||
@property (assign, nonatomic) IBInspectable FSCalendarCellShape cellShape;
|
|
||||||
@property (assign, nonatomic) IBInspectable BOOL useVeryShortWeekdaySymbols;
|
|
||||||
|
|
||||||
@property (assign, nonatomic) IBInspectable BOOL fakeSubtitles;
|
|
||||||
@property (assign, nonatomic) IBInspectable NSInteger fakedSelectedDay;
|
|
||||||
|
|
||||||
@end
|
|
|
@ -1,378 +0,0 @@
|
||||||
//
|
|
||||||
// FSCalendar+IBExtension.m
|
|
||||||
// FSCalendar
|
|
||||||
//
|
|
||||||
// Created by Wenchao Ding on 8/14/15.
|
|
||||||
// Copyright © 2016 Wenchao Ding. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "FSCalendar+IBExtension.h"
|
|
||||||
|
|
||||||
@implementation FSCalendar (IBExtension)
|
|
||||||
#if !TARGET_INTERFACE_BUILDER
|
|
||||||
@dynamic fakedSelectedDay,fakeSubtitles;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#pragma mark - adjustsFontSizeToFitContentSize
|
|
||||||
|
|
||||||
- (void)setAdjustsFontSizeToFitContentSize:(BOOL)adjustsFontSizeToFitContentSize
|
|
||||||
{
|
|
||||||
self.appearance.adjustsFontSizeToFitContentSize = adjustsFontSizeToFitContentSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)adjustsFontSizeToFitContentSize
|
|
||||||
{
|
|
||||||
return self.appearance.adjustsFontSizeToFitContentSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - eventColor
|
|
||||||
|
|
||||||
- (void)setEventColor:(UIColor *)eventColor
|
|
||||||
{
|
|
||||||
self.appearance.eventColor = eventColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIColor *)eventColor
|
|
||||||
{
|
|
||||||
return self.appearance.eventColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - weekdayTextColor
|
|
||||||
|
|
||||||
- (void)setWeekdayTextColor:(UIColor *)weekdayTextColor
|
|
||||||
{
|
|
||||||
self.appearance.weekdayTextColor = weekdayTextColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIColor *)weekdayTextColor
|
|
||||||
{
|
|
||||||
return self.appearance.weekdayTextColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - headerTitleColor
|
|
||||||
|
|
||||||
- (void)setHeaderTitleColor:(UIColor *)headerTitleColor
|
|
||||||
{
|
|
||||||
self.appearance.headerTitleColor = headerTitleColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIColor *)headerTitleColor
|
|
||||||
{
|
|
||||||
return self.appearance.headerTitleColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - headerDateFormat
|
|
||||||
|
|
||||||
- (void)setHeaderDateFormat:(NSString *)headerDateFormat
|
|
||||||
{
|
|
||||||
self.appearance.headerDateFormat = headerDateFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)headerDateFormat
|
|
||||||
{
|
|
||||||
return self.appearance.headerDateFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - headerMinimumDissolvedAlpha
|
|
||||||
|
|
||||||
- (void)setHeaderMinimumDissolvedAlpha:(CGFloat)headerMinimumDissolvedAlpha
|
|
||||||
{
|
|
||||||
self.appearance.headerMinimumDissolvedAlpha = headerMinimumDissolvedAlpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)headerMinimumDissolvedAlpha
|
|
||||||
{
|
|
||||||
return self.appearance.headerMinimumDissolvedAlpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - titleDefaultColor
|
|
||||||
|
|
||||||
- (void)setTitleDefaultColor:(UIColor *)titleDefaultColor
|
|
||||||
{
|
|
||||||
self.appearance.titleDefaultColor = titleDefaultColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIColor *)titleDefaultColor
|
|
||||||
{
|
|
||||||
return self.appearance.titleDefaultColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - titleSelectionColor
|
|
||||||
|
|
||||||
- (void)setTitleSelectionColor:(UIColor *)titleSelectionColor
|
|
||||||
{
|
|
||||||
self.appearance.titleSelectionColor = titleSelectionColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIColor *)titleSelectionColor
|
|
||||||
{
|
|
||||||
return self.appearance.titleSelectionColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - titleTodayColor
|
|
||||||
|
|
||||||
- (void)setTitleTodayColor:(UIColor *)titleTodayColor
|
|
||||||
{
|
|
||||||
self.appearance.titleTodayColor = titleTodayColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIColor *)titleTodayColor
|
|
||||||
{
|
|
||||||
return self.appearance.titleTodayColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - titlePlaceholderColor
|
|
||||||
|
|
||||||
- (void)setTitlePlaceholderColor:(UIColor *)titlePlaceholderColor
|
|
||||||
{
|
|
||||||
self.appearance.titlePlaceholderColor = titlePlaceholderColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIColor *)titlePlaceholderColor
|
|
||||||
{
|
|
||||||
return self.appearance.titlePlaceholderColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - titleWeekendColor
|
|
||||||
|
|
||||||
- (void)setTitleWeekendColor:(UIColor *)titleWeekendColor
|
|
||||||
{
|
|
||||||
self.appearance.titleWeekendColor = titleWeekendColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIColor *)titleWeekendColor
|
|
||||||
{
|
|
||||||
return self.appearance.titleWeekendColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - subtitleDefaultColor
|
|
||||||
|
|
||||||
- (void)setSubtitleDefaultColor:(UIColor *)subtitleDefaultColor
|
|
||||||
{
|
|
||||||
self.appearance.subtitleDefaultColor = subtitleDefaultColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIColor *)subtitleDefaultColor
|
|
||||||
{
|
|
||||||
return self.appearance.subtitleDefaultColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - subtitleSelectionColor
|
|
||||||
|
|
||||||
- (void)setSubtitleSelectionColor:(UIColor *)subtitleSelectionColor
|
|
||||||
{
|
|
||||||
self.appearance.subtitleSelectionColor = subtitleSelectionColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIColor *)subtitleSelectionColor
|
|
||||||
{
|
|
||||||
return self.appearance.subtitleSelectionColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - subtitleTodayColor
|
|
||||||
|
|
||||||
- (void)setSubtitleTodayColor:(UIColor *)subtitleTodayColor
|
|
||||||
{
|
|
||||||
self.appearance.subtitleTodayColor = subtitleTodayColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIColor *)subtitleTodayColor
|
|
||||||
{
|
|
||||||
return self.appearance.subtitleTodayColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - subtitlePlaceholderColor
|
|
||||||
|
|
||||||
- (void)setSubtitlePlaceholderColor:(UIColor *)subtitlePlaceholderColor
|
|
||||||
{
|
|
||||||
self.appearance.subtitlePlaceholderColor = subtitlePlaceholderColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIColor *)subtitlePlaceholderColor
|
|
||||||
{
|
|
||||||
return self.appearance.subtitlePlaceholderColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - subtitleWeekendColor
|
|
||||||
|
|
||||||
- (void)setSubtitleWeekendColor:(UIColor *)subtitleWeekendColor
|
|
||||||
{
|
|
||||||
self.appearance.subtitleWeekendColor = subtitleWeekendColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIColor *)subtitleWeekendColor
|
|
||||||
{
|
|
||||||
return self.appearance.subtitleWeekendColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - selectionColor
|
|
||||||
|
|
||||||
- (void)setSelectionColor:(UIColor *)selectionColor
|
|
||||||
{
|
|
||||||
self.appearance.selectionColor = selectionColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIColor *)selectionColor
|
|
||||||
{
|
|
||||||
return self.appearance.selectionColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - todayColor
|
|
||||||
|
|
||||||
- (void)setTodayColor:(UIColor *)todayColor
|
|
||||||
{
|
|
||||||
self.appearance.todayColor = todayColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIColor *)todayColor
|
|
||||||
{
|
|
||||||
return self.appearance.todayColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - todaySelectionColor
|
|
||||||
|
|
||||||
- (void)setTodaySelectionColor:(UIColor *)todaySelectionColor
|
|
||||||
{
|
|
||||||
self.appearance.todaySelectionColor = todaySelectionColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIColor *)todaySelectionColor
|
|
||||||
{
|
|
||||||
return self.appearance.todaySelectionColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - borderDefaultColor
|
|
||||||
|
|
||||||
- (void)setBorderDefaultColor:(UIColor *)borderDefaultColor
|
|
||||||
{
|
|
||||||
self.appearance.borderDefaultColor = borderDefaultColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIColor *)borderDefaultColor
|
|
||||||
{
|
|
||||||
return self.appearance.borderDefaultColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - borderSelectionColor
|
|
||||||
|
|
||||||
- (void)setBorderSelectionColor:(UIColor *)borderSelectionColor
|
|
||||||
{
|
|
||||||
self.appearance.borderSelectionColor = borderSelectionColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIColor *)borderSelectionColor
|
|
||||||
{
|
|
||||||
return self.appearance.borderSelectionColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - cellStyle
|
|
||||||
|
|
||||||
- (void)setCellShape:(FSCalendarCellShape)cellShape
|
|
||||||
{
|
|
||||||
self.appearance.cellShape = cellShape;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (FSCalendarCellShape)cellShape
|
|
||||||
{
|
|
||||||
return self.appearance.cellShape;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - useVeryShortWeekdaySymbols
|
|
||||||
|
|
||||||
- (void)setUseVeryShortWeekdaySymbols:(BOOL)useVeryShortWeekdaySymbols
|
|
||||||
{
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
||||||
self.appearance.useVeryShortWeekdaySymbols = useVeryShortWeekdaySymbols;
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)useVeryShortWeekdaySymbols
|
|
||||||
{
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
||||||
return self.appearance.useVeryShortWeekdaySymbols;
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - fakeSubtitles
|
|
||||||
|
|
||||||
- (void)setFakeSubtitles:(BOOL)fakeSubtitles
|
|
||||||
{
|
|
||||||
#if TARGET_INTERFACE_BUILDER
|
|
||||||
self.appearance.fakeSubtitles = fakeSubtitles;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#if TARGET_INTERFACE_BUILDER
|
|
||||||
- (BOOL)fakeSubtitles
|
|
||||||
{
|
|
||||||
return self.appearance.fakeSubtitles;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#pragma mark - fakedSelectedDay
|
|
||||||
|
|
||||||
- (void)setFakedSelectedDay:(NSInteger)fakedSelectedDay
|
|
||||||
{
|
|
||||||
#if TARGET_INTERFACE_BUILDER
|
|
||||||
self.appearance.fakedSelectedDay = fakedSelectedDay;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if TARGET_INTERFACE_BUILDER
|
|
||||||
- (NSInteger)fakedSelectedDay
|
|
||||||
{
|
|
||||||
return self.appearance.fakedSelectedDay;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
||||||
|
|
||||||
- (void)setTitleTextSize:(CGFloat)titleTextSize
|
|
||||||
{
|
|
||||||
self.appearance.titleTextSize = titleTextSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)titleTextSize
|
|
||||||
{
|
|
||||||
return self.appearance.titleTextSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
- (void)setSubtitleTextSize:(CGFloat)subtitleTextSize
|
|
||||||
{
|
|
||||||
self.appearance.subtitleTextSize = subtitleTextSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)subtitleTextSize
|
|
||||||
{
|
|
||||||
return self.appearance.subtitleTextSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setWeekdayTextSize:(CGFloat)weekdayTextSize
|
|
||||||
{
|
|
||||||
self.appearance.weekdayTextSize = weekdayTextSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)weekdayTextSize
|
|
||||||
{
|
|
||||||
return self.appearance.weekdayTextSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setHeaderTitleTextSize:(CGFloat)headerTitleTextSize
|
|
||||||
{
|
|
||||||
self.appearance.headerTitleTextSize = headerTitleTextSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)headerTitleTextSize
|
|
||||||
{
|
|
||||||
return self.appearance.headerTitleTextSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
|
|
||||||
@end
|
|
|
@ -7,22 +7,20 @@
|
||||||
//
|
//
|
||||||
// https://github.com/WenchaoD
|
// https://github.com/WenchaoD
|
||||||
//
|
//
|
||||||
|
// FSCalendar is a superior awesome calendar control with high performance, high customizablility and very simple usage.
|
||||||
|
//
|
||||||
|
// @see FSCalendarDataSource
|
||||||
|
// @see FSCalendarDelegate
|
||||||
|
// @see FSCalendarDelegateAppearance
|
||||||
|
// @see FSCalendarAppearance
|
||||||
|
//
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#import "FSCalendarAppearance.h"
|
#import "FSCalendarAppearance.h"
|
||||||
#import "FSCalendarConstance.h"
|
#import "FSCalendarConstants.h"
|
||||||
|
#import "FSCalendarCell.h"
|
||||||
/**
|
#import "FSCalendarWeekdayView.h"
|
||||||
* FSCalendar is a superior awesome calendar control with high performance, high customizablility and very simple usage.
|
#import "FSCalendarHeaderView.h"
|
||||||
*
|
|
||||||
* @warning All NSDate instances used in the calendar should be managed by the DateTools category. See FSCalendar+DateTools.
|
|
||||||
*
|
|
||||||
* @see FSCalendarDataSource
|
|
||||||
* @see FSCalendarDelegate
|
|
||||||
* @see FSCalendarDelegateAppearance
|
|
||||||
* @see FSCalendarAppearance
|
|
||||||
* @see FSCalendar+DateTools
|
|
||||||
*/
|
|
||||||
|
|
||||||
//! Project version number for FSCalendar.
|
//! Project version number for FSCalendar.
|
||||||
FOUNDATION_EXPORT double FSCalendarVersionNumber;
|
FOUNDATION_EXPORT double FSCalendarVersionNumber;
|
||||||
|
@ -40,10 +38,18 @@ typedef NS_ENUM(NSUInteger, FSCalendarScrollDirection) {
|
||||||
FSCalendarScrollDirectionHorizontal
|
FSCalendarScrollDirectionHorizontal
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef NS_ENUM(NSUInteger, FSCalendarUnit) {
|
typedef NS_ENUM(NSUInteger, FSCalendarPlaceholderType) {
|
||||||
FSCalendarUnitMonth = NSCalendarUnitMonth,
|
FSCalendarPlaceholderTypeNone = 0,
|
||||||
FSCalendarUnitWeekOfYear = NSCalendarUnitWeekOfYear,
|
FSCalendarPlaceholderTypeFillHeadTail = 1,
|
||||||
FSCalendarUnitDay = NSCalendarUnitDay
|
FSCalendarPlaceholderTypeFillSixRows = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSUInteger, FSCalendarMonthPosition) {
|
||||||
|
FSCalendarMonthPositionPrevious,
|
||||||
|
FSCalendarMonthPositionCurrent,
|
||||||
|
FSCalendarMonthPositionNext,
|
||||||
|
|
||||||
|
FSCalendarMonthPositionNotFound = NSNotFound
|
||||||
};
|
};
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
@ -51,12 +57,17 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
@class FSCalendar;
|
@class FSCalendar;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FSCalendarDataSource is a source set of FSCalendar. The basic job is to provide event、subtitle and min/max day to display for calendar.
|
* FSCalendarDataSource is a source set of FSCalendar. The basic role is to provide event、subtitle and min/max day to display, or customized day cell for the calendar.
|
||||||
*/
|
*/
|
||||||
@protocol FSCalendarDataSource <NSObject>
|
@protocol FSCalendarDataSource <NSObject>
|
||||||
|
|
||||||
@optional
|
@optional
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asks the dataSource for a title for the specific date as a replacement of the day text
|
||||||
|
*/
|
||||||
|
- (nullable NSString *)calendar:(FSCalendar *)calendar titleForDate:(NSDate *)date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asks the dataSource for a subtitle for the specific date under the day text.
|
* Asks the dataSource for a subtitle for the specific date under the day text.
|
||||||
*/
|
*/
|
||||||
|
@ -77,11 +88,15 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
*/
|
*/
|
||||||
- (NSDate *)maximumDateForCalendar:(FSCalendar *)calendar;
|
- (NSDate *)maximumDateForCalendar:(FSCalendar *)calendar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asks the data source for a cell to insert in a particular data of the calendar.
|
||||||
|
*/
|
||||||
|
- (__kindof FSCalendarCell *)calendar:(FSCalendar *)calendar cellForDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)position;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asks the dataSource the number of event dots for a specific date.
|
* Asks the dataSource the number of event dots for a specific date.
|
||||||
*
|
*
|
||||||
* @see
|
* @see
|
||||||
*
|
|
||||||
* - (UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance eventColorForDate:(NSDate *)date;
|
* - (UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance eventColorForDate:(NSDate *)date;
|
||||||
* - (NSArray *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance eventColorsForDate:(NSDate *)date;
|
* - (NSArray *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance eventColorsForDate:(NSDate *)date;
|
||||||
*/
|
*/
|
||||||
|
@ -103,56 +118,70 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
@optional
|
@optional
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asks the delegate whether the specific date is allowed to be selected by tapping.
|
Asks the delegate whether the specific date is allowed to be selected by tapping.
|
||||||
*/
|
*/
|
||||||
- (BOOL)calendar:(FSCalendar *)calendar shouldSelectDate:(NSDate *)date;
|
- (BOOL)calendar:(FSCalendar *)calendar shouldSelectDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)monthPosition;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells the delegate a date in the calendar is selected by tapping.
|
Tells the delegate a date in the calendar is selected by tapping.
|
||||||
*/
|
*/
|
||||||
- (void)calendar:(FSCalendar *)calendar didSelectDate:(NSDate *)date;
|
- (void)calendar:(FSCalendar *)calendar didSelectDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)monthPosition;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asks the delegate whether the specific date is allowed to be deselected by tapping.
|
Asks the delegate whether the specific date is allowed to be deselected by tapping.
|
||||||
*/
|
*/
|
||||||
- (BOOL)calendar:(FSCalendar *)calendar shouldDeselectDate:(NSDate *)date;
|
- (BOOL)calendar:(FSCalendar *)calendar shouldDeselectDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)monthPosition;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells the delegate a date in the calendar is deselected by tapping.
|
Tells the delegate a date in the calendar is deselected by tapping.
|
||||||
*/
|
*/
|
||||||
- (void)calendar:(FSCalendar *)calendar didDeselectDate:(NSDate *)date;
|
- (void)calendar:(FSCalendar *)calendar didDeselectDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)monthPosition;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells the delegate the calendar is about to change the bounding rect.
|
Tells the delegate the calendar is about to change the bounding rect.
|
||||||
*/
|
*/
|
||||||
- (void)calendar:(FSCalendar *)calendar boundingRectWillChange:(CGRect)bounds animated:(BOOL)animated;
|
- (void)calendar:(FSCalendar *)calendar boundingRectWillChange:(CGRect)bounds animated:(BOOL)animated;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells the delegate the calendar is about to change the current page.
|
Tells the delegate that the specified cell is about to be displayed in the calendar.
|
||||||
|
*/
|
||||||
|
- (void)calendar:(FSCalendar *)calendar willDisplayCell:(FSCalendarCell *)cell forDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)monthPosition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Tells the delegate the calendar is about to change the current page.
|
||||||
*/
|
*/
|
||||||
- (void)calendarCurrentPageDidChange:(FSCalendar *)calendar;
|
- (void)calendarCurrentPageDidChange:(FSCalendar *)calendar;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* These functions are deprecated
|
These functions are deprecated
|
||||||
*/
|
*/
|
||||||
- (void)calendarCurrentScopeWillChange:(FSCalendar *)calendar animated:(BOOL)animated FSCalendarDeprecated(-calendar:boundingRectWillChange:animated:);
|
- (void)calendarCurrentScopeWillChange:(FSCalendar *)calendar animated:(BOOL)animated FSCalendarDeprecated(-calendar:boundingRectWillChange:animated:);
|
||||||
- (void)calendarCurrentMonthDidChange:(FSCalendar *)calendar FSCalendarDeprecated(-calendarCurrentPageDidChange:);
|
- (void)calendarCurrentMonthDidChange:(FSCalendar *)calendar FSCalendarDeprecated(-calendarCurrentPageDidChange:);
|
||||||
|
- (BOOL)calendar:(FSCalendar *)calendar shouldSelectDate:(NSDate *)date FSCalendarDeprecated(-calendar:shouldSelectDate:atMonthPosition:);- (void)calendar:(FSCalendar *)calendar didSelectDate:(NSDate *)date FSCalendarDeprecated(-calendar:didSelectDate:atMonthPosition:);
|
||||||
|
- (BOOL)calendar:(FSCalendar *)calendar shouldDeselectDate:(NSDate *)date FSCalendarDeprecated(-calendar:shouldDeselectDate:atMonthPosition:);
|
||||||
|
- (void)calendar:(FSCalendar *)calendar didDeselectDate:(NSDate *)date FSCalendarDeprecated(-calendar:didDeselectDate:atMonthPosition:);
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FSCalendarDelegateAppearance determines the fonts and colors of components in the calendar, but more specificly. Basely, if you need to make a global customization of appearance of the calendar, use FSCalendarAppearance. But if you need different appearance for different day, use FSCalendarDelegateAppearance.
|
* FSCalendarDelegateAppearance determines the fonts and colors of components in the calendar, but more specificly. Basically, if you need to make a global customization of appearance of the calendar, use FSCalendarAppearance. But if you need different appearance for different days, use FSCalendarDelegateAppearance.
|
||||||
*
|
*
|
||||||
* @see FSCalendarAppearance
|
* @see FSCalendarAppearance
|
||||||
*/
|
*/
|
||||||
@protocol FSCalendarDelegateAppearance <NSObject>
|
@protocol FSCalendarDelegateAppearance <FSCalendarDelegate>
|
||||||
|
|
||||||
@optional
|
@optional
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asks the delegate for a fill color in unselected state for the specific date.
|
||||||
|
*/
|
||||||
|
- (nullable UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance fillDefaultColorForDate:(NSDate *)date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asks the delegate for a fill color in selected state for the specific date.
|
* Asks the delegate for a fill color in selected state for the specific date.
|
||||||
*/
|
*/
|
||||||
- (nullable UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance selectionColorForDate:(NSDate *)date;
|
- (nullable UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance fillSelectionColorForDate:(NSDate *)date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asks the delegate for day text color in unselected state for the specific date.
|
* Asks the delegate for day text color in unselected state for the specific date.
|
||||||
|
@ -175,14 +204,14 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
- (nullable UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance subtitleSelectionColorForDate:(NSDate *)date;
|
- (nullable UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance subtitleSelectionColorForDate:(NSDate *)date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asks the delegate for single event color for the specific date.
|
* Asks the delegate for event colors for the specific date.
|
||||||
*/
|
*/
|
||||||
- (nullable UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance eventColorForDate:(NSDate *)date;
|
- (nullable NSArray<UIColor *> *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance eventDefaultColorsForDate:(NSDate *)date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asks the delegate for multiple event colors for the specific date.
|
* Asks the delegate for multiple event colors in selected state for the specific date.
|
||||||
*/
|
*/
|
||||||
- (nullable NSArray *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance eventColorsForDate:(NSDate *)date;
|
- (nullable NSArray<UIColor *> *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance eventSelectionColorsForDate:(NSDate *)date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asks the delegate for a border color in unselected state for the specific date.
|
* Asks the delegate for a border color in unselected state for the specific date.
|
||||||
|
@ -195,15 +224,39 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
- (nullable UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance borderSelectionColorForDate:(NSDate *)date;
|
- (nullable UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance borderSelectionColorForDate:(NSDate *)date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asks the delegate for a shape for the specific date.
|
* Asks the delegate for an offset for day text for the specific date.
|
||||||
*/
|
*/
|
||||||
- (FSCalendarCellShape)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance cellShapeForDate:(NSDate *)date;
|
- (CGPoint)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance titleOffsetForDate:(NSDate *)date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function is deprecated
|
* Asks the delegate for an offset for subtitle for the specific date.
|
||||||
*/
|
*/
|
||||||
- (FSCalendarCellStyle)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance cellStyleForDate:(NSDate *)date FSCalendarDeprecated(-calendar:appearance:cellShapeForDate:);
|
- (CGPoint)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance subtitleOffsetForDate:(NSDate *)date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asks the delegate for an offset for image for the specific date.
|
||||||
|
*/
|
||||||
|
- (CGPoint)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance imageOffsetForDate:(NSDate *)date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asks the delegate for an offset for event dots for the specific date.
|
||||||
|
*/
|
||||||
|
- (CGPoint)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance eventOffsetForDate:(NSDate *)date;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asks the delegate for a border radius for the specific date.
|
||||||
|
*/
|
||||||
|
- (CGFloat)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance borderRadiusForDate:(NSDate *)date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These functions are deprecated
|
||||||
|
*/
|
||||||
|
- (nullable UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance fillColorForDate:(NSDate *)date FSCalendarDeprecated(-calendar:appearance:fillDefaultColorForDate:);
|
||||||
|
- (nullable UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance selectionColorForDate:(NSDate *)date FSCalendarDeprecated(-calendar:appearance:fillSelectionColorForDate:);
|
||||||
|
- (nullable UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance eventColorForDate:(NSDate *)date FSCalendarDeprecated(-calendar:appearance:eventDefaultColorsForDate:);
|
||||||
|
- (nullable NSArray *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance eventColorsForDate:(NSDate *)date FSCalendarDeprecated(-calendar:appearance:eventDefaultColorsForDate:);
|
||||||
|
- (FSCalendarCellShape)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance cellShapeForDate:(NSDate *)date FSCalendarDeprecated(-calendar:appearance:borderRadiusForDate:);
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#pragma mark - Primary
|
#pragma mark - Primary
|
||||||
|
@ -222,9 +275,9 @@ IB_DESIGNABLE
|
||||||
@property (weak, nonatomic) IBOutlet id<FSCalendarDataSource> dataSource;
|
@property (weak, nonatomic) IBOutlet id<FSCalendarDataSource> dataSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A special mark will be put on today of the calendar
|
* A special mark will be put on 'today' of the calendar.
|
||||||
*/
|
*/
|
||||||
@property (strong, nonatomic) NSDate *today;
|
@property (nullable, strong, nonatomic) NSDate *today;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current page of calendar
|
* The current page of calendar
|
||||||
|
@ -240,16 +293,7 @@ IB_DESIGNABLE
|
||||||
*
|
*
|
||||||
* calendar.locale = [NSLocale localeWithLocaleIdentifier:@"zh-CN"];
|
* calendar.locale = [NSLocale localeWithLocaleIdentifier:@"zh-CN"];
|
||||||
*/
|
*/
|
||||||
@property (strong, nonatomic) NSLocale *locale;
|
@property (copy, nonatomic) NSLocale *locale;
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the NSCalendarIdentifier of calendar. Default is NSCalendarIdentifierGregorian.
|
|
||||||
*
|
|
||||||
* e.g. To display a Persian calendar
|
|
||||||
*
|
|
||||||
* calendar.identifier = NSCalendarIdentifierPersian;
|
|
||||||
*/
|
|
||||||
@property (strong, nonatomic) NSString *identifier;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The scroll direction of FSCalendar.
|
* The scroll direction of FSCalendar.
|
||||||
|
@ -268,330 +312,319 @@ IB_DESIGNABLE
|
||||||
@property (assign, nonatomic) FSCalendarScope scope;
|
@property (assign, nonatomic) FSCalendarScope scope;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The index of the first weekday of the calendar. Give a '2' to make Monday in the first column.
|
A UIPanGestureRecognizer instance which enables the control of scope on the whole day-area. Not available if the scrollDirection is vertical.
|
||||||
|
|
||||||
|
@deprecated Use -handleScopeGesture: instead
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
|
||||||
|
UIPanGestureRecognizer *scopeGesture = [[UIPanGestureRecognizer alloc] initWithTarget:calendar action:@selector(handleScopeGesture:)];
|
||||||
|
[calendar addGestureRecognizer:scopeGesture];
|
||||||
|
|
||||||
|
@see DIYExample
|
||||||
|
@see FSCalendarScopeExample
|
||||||
|
*/
|
||||||
|
@property (readonly, nonatomic) UIPanGestureRecognizer *scopeGesture FSCalendarDeprecated(handleScopeGesture:);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A UILongPressGestureRecognizer instance which enables the swipe-to-choose feature of the calendar.
|
||||||
|
*
|
||||||
|
* e.g.
|
||||||
|
*
|
||||||
|
* calendar.swipeToChooseGesture.enabled = YES;
|
||||||
|
*/
|
||||||
|
@property (readonly, nonatomic) UILongPressGestureRecognizer *swipeToChooseGesture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The placeholder type of FSCalendar. Default is FSCalendarPlaceholderTypeFillSixRows.
|
||||||
|
*
|
||||||
|
* e.g. To hide all placeholder of the calendar
|
||||||
|
*
|
||||||
|
* calendar.placeholderType = FSCalendarPlaceholderTypeNone;
|
||||||
|
*/
|
||||||
|
#if TARGET_INTERFACE_BUILDER
|
||||||
|
@property (assign, nonatomic) IBInspectable NSUInteger placeholderType;
|
||||||
|
#else
|
||||||
|
@property (assign, nonatomic) FSCalendarPlaceholderType placeholderType;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
The index of the first weekday of the calendar. Give a '2' to make Monday in the first column.
|
||||||
*/
|
*/
|
||||||
@property (assign, nonatomic) IBInspectable NSUInteger firstWeekday;
|
@property (assign, nonatomic) IBInspectable NSUInteger firstWeekday;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The height of month header of the calendar. Give a '0' to remove the header.
|
The height of month header of the calendar. Give a '0' to remove the header.
|
||||||
*/
|
*/
|
||||||
@property (assign, nonatomic) IBInspectable CGFloat headerHeight;
|
@property (assign, nonatomic) IBInspectable CGFloat headerHeight;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The height of weekday header of the calendar.
|
The height of weekday header of the calendar.
|
||||||
*/
|
*/
|
||||||
@property (assign, nonatomic) IBInspectable CGFloat weekdayHeight;
|
@property (assign, nonatomic) IBInspectable CGFloat weekdayHeight;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Boolean value that determines whether users can select a date.
|
The weekday view of the calendar
|
||||||
|
*/
|
||||||
|
@property (strong, nonatomic) FSCalendarWeekdayView *calendarWeekdayView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
The header view of the calendar
|
||||||
|
*/
|
||||||
|
@property (strong, nonatomic) FSCalendarHeaderView *calendarHeaderView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A Boolean value that determines whether users can select a date.
|
||||||
*/
|
*/
|
||||||
@property (assign, nonatomic) IBInspectable BOOL allowsSelection;
|
@property (assign, nonatomic) IBInspectable BOOL allowsSelection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Boolean value that determines whether users can select more than one date.
|
A Boolean value that determines whether users can select more than one date.
|
||||||
*/
|
*/
|
||||||
@property (assign, nonatomic) IBInspectable BOOL allowsMultipleSelection;
|
@property (assign, nonatomic) IBInspectable BOOL allowsMultipleSelection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Boolean value that determines whether paging is enabled for the calendar.
|
A Boolean value that determines whether paging is enabled for the calendar.
|
||||||
*/
|
*/
|
||||||
@property (assign, nonatomic) IBInspectable BOOL pagingEnabled;
|
@property (assign, nonatomic) IBInspectable BOOL pagingEnabled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Boolean value that determines whether scrolling is enabled for the calendar.
|
A Boolean value that determines whether scrolling is enabled for the calendar.
|
||||||
*/
|
*/
|
||||||
@property (assign, nonatomic) IBInspectable BOOL scrollEnabled;
|
@property (assign, nonatomic) IBInspectable BOOL scrollEnabled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Boolean value that determines whether scoping animation is centered a visible selected date. Default is YES.
|
A Boolean value that determines whether the calendar should show a handle for control the scope. Default is NO;
|
||||||
|
|
||||||
|
@deprecated Use -handleScopeGesture: instead
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
|
||||||
|
UIPanGestureRecognizer *scopeGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self.calendar action:@selector(handleScopeGesture:)];
|
||||||
|
scopeGesture.delegate = ...
|
||||||
|
[anyOtherView addGestureRecognizer:scopeGesture];
|
||||||
|
|
||||||
|
@see FSCalendarScopeExample
|
||||||
|
|
||||||
*/
|
*/
|
||||||
@property (assign, nonatomic) IBInspectable BOOL focusOnSingleSelectedDate;
|
@property (assign, nonatomic) IBInspectable BOOL showsScopeHandle FSCalendarDeprecated(handleScopeGesture:);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Boolean value that determines whether the calendar should show days out of month. Default is YES.
|
The row height of the calendar if paging enabled is NO.;
|
||||||
*/
|
*/
|
||||||
@property (assign, nonatomic) IBInspectable BOOL showsPlaceholders;
|
@property (assign, nonatomic) IBInspectable CGFloat rowHeight;
|
||||||
|
|
||||||
|
/**
|
||||||
|
The calendar appearance used to control the global fonts、colors .etc
|
||||||
|
*/
|
||||||
@property (readonly, nonatomic) FSCalendarAppearance *appearance;
|
@property (readonly, nonatomic) FSCalendarAppearance *appearance;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A date object representing the minimum day enable、visible and selectable. (read-only)
|
A date object representing the minimum day enable、visible and selectable. (read-only)
|
||||||
*/
|
*/
|
||||||
@property (readonly, nonatomic) NSDate *minimumDate;
|
@property (readonly, nonatomic) NSDate *minimumDate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A date object representing the maximum day enable、visible and selectable. (read-only)
|
A date object representing the maximum day enable、visible and selectable. (read-only)
|
||||||
*/
|
*/
|
||||||
@property (readonly, nonatomic) NSDate *maximumDate;
|
@property (readonly, nonatomic) NSDate *maximumDate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A date object identifying the section of the selected date. (read-only)
|
A date object identifying the section of the selected date. (read-only)
|
||||||
*/
|
*/
|
||||||
@property (readonly, nonatomic) NSDate *selectedDate;
|
@property (nullable, readonly, nonatomic) NSDate *selectedDate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The dates representing the selected dates. (read-only)
|
The dates representing the selected dates. (read-only)
|
||||||
*/
|
*/
|
||||||
@property (readonly, nonatomic) NSArray *selectedDates;
|
@property (readonly, nonatomic) NSArray<NSDate *> *selectedDates;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reload the dates and appearance of the calendar.
|
Reload the dates and appearance of the calendar.
|
||||||
*/
|
*/
|
||||||
- (void)reloadData;
|
- (void)reloadData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the scope of the calendar. Make sure `-calendar:boundingRectWillChange:animated` is correctly adopted.
|
Change the scope of the calendar. Make sure `-calendar:boundingRectWillChange:animated` is correctly adopted.
|
||||||
*
|
|
||||||
* @param scope The target scope to change.
|
@param scope The target scope to change.
|
||||||
* @param animated YES if you want to animate the scoping; NO if the change should be immediate.
|
@param animated YES if you want to animate the scoping; NO if the change should be immediate.
|
||||||
*/
|
*/
|
||||||
- (void)setScope:(FSCalendarScope)scope animated:(BOOL)animated;
|
- (void)setScope:(FSCalendarScope)scope animated:(BOOL)animated;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selects a given date in the calendar.
|
Selects a given date in the calendar.
|
||||||
*
|
|
||||||
* @param date A date in the calendar.
|
@param date A date in the calendar.
|
||||||
*/
|
*/
|
||||||
- (void)selectDate:(NSDate *)date;
|
- (void)selectDate:(nullable NSDate *)date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selects a given date in the calendar, optionally scrolling the date to visible area.
|
Selects a given date in the calendar, optionally scrolling the date to visible area.
|
||||||
*
|
|
||||||
* @param date A date in the calendar.
|
@param date A date in the calendar.
|
||||||
* @param scrollToDate A Boolean value that determines whether the calendar should scroll to the selected date to visible area.
|
@param scrollToDate A Boolean value that determines whether the calendar should scroll to the selected date to visible area.
|
||||||
*/
|
*/
|
||||||
- (void)selectDate:(NSDate *)date scrollToDate:(BOOL)scrollToDate;
|
- (void)selectDate:(nullable NSDate *)date scrollToDate:(BOOL)scrollToDate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deselects a given date of the calendar.
|
Deselects a given date of the calendar.
|
||||||
* @param date A date in the calendar.
|
|
||||||
|
@param date A date in the calendar.
|
||||||
*/
|
*/
|
||||||
- (void)deselectDate:(NSDate *)date;
|
- (void)deselectDate:(NSDate *)date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the current page of the calendar.
|
Changes the current page of the calendar.
|
||||||
*
|
|
||||||
* @param currentPage Representing weekOfYear in week mode, or month in month mode.
|
@param currentPage Representing weekOfYear in week mode, or month in month mode.
|
||||||
* @param animated YES if you want to animate the change in position; NO if it should be immediate.
|
@param animated YES if you want to animate the change in position; NO if it should be immediate.
|
||||||
*/
|
*/
|
||||||
- (void)setCurrentPage:(NSDate *)currentPage animated:(BOOL)animated;
|
- (void)setCurrentPage:(NSDate *)currentPage animated:(BOOL)animated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Register a class for use in creating new calendar cells.
|
||||||
|
|
||||||
|
@param cellClass The class of a cell that you want to use in the calendar.
|
||||||
|
@param identifier The reuse identifier to associate with the specified class. This parameter must not be nil and must not be an empty string.
|
||||||
|
*/
|
||||||
|
- (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns a reusable calendar cell object located by its identifier.
|
||||||
|
|
||||||
|
@param identifier The reuse identifier for the specified cell. This parameter must not be nil.
|
||||||
|
@param date The specific date of the cell.
|
||||||
|
@return A valid FSCalendarCell object.
|
||||||
|
*/
|
||||||
|
- (__kindof FSCalendarCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier forDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)position;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the calendar cell for the specified date.
|
||||||
|
|
||||||
|
@param date The date of the cell
|
||||||
|
@param position The month position for the cell
|
||||||
|
@return An object representing a cell of the calendar, or nil if the cell is not visible or date is out of range.
|
||||||
|
*/
|
||||||
|
- (nullable FSCalendarCell *)cellForDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)position;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the date of the specified cell.
|
||||||
|
|
||||||
|
@param cell The cell object whose date you want.
|
||||||
|
@return The date of the cell or nil if the specified cell is not in the calendar.
|
||||||
|
*/
|
||||||
|
- (nullable NSDate *)dateForCell:(FSCalendarCell *)cell;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the month position of the specified cell.
|
||||||
|
|
||||||
|
@param cell The cell object whose month position you want.
|
||||||
|
@return The month position of the cell or FSCalendarMonthPositionNotFound if the specified cell is not in the calendar.
|
||||||
|
*/
|
||||||
|
- (FSCalendarMonthPosition)monthPositionForCell:(FSCalendarCell *)cell;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns an array of visible cells currently displayed by the calendar.
|
||||||
|
|
||||||
|
@return An array of FSCalendarCell objects. If no cells are visible, this method returns an empty array.
|
||||||
|
*/
|
||||||
|
- (NSArray<__kindof FSCalendarCell *> *)visibleCells;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the frame for a non-placeholder cell relative to the super view of the calendar.
|
||||||
|
|
||||||
|
@param date A date is the calendar.
|
||||||
|
*/
|
||||||
|
- (CGRect)frameForDate:(NSDate *)date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
An action selector for UIPanGestureRecognizer instance to control the scope transition
|
||||||
|
|
||||||
|
@param sender A UIPanGestureRecognizer instance which controls the scope of the calendar
|
||||||
|
*/
|
||||||
|
- (void)handleScopeGesture:(UIPanGestureRecognizer *)sender;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#pragma mark - DateTools
|
|
||||||
|
|
||||||
/**
|
IB_DESIGNABLE
|
||||||
* Job for this category:
|
@interface FSCalendar (IBExtension)
|
||||||
*
|
|
||||||
* 1. Manage date object simplier、faster
|
|
||||||
* 2. Bring date object into a no-timezone system.
|
|
||||||
*
|
|
||||||
* @warning All NSDate instances used in the calendar should be created by:
|
|
||||||
*
|
|
||||||
* - (NSDate *)dateFromString:(NSString *)string format:(NSString *)format;
|
|
||||||
* - (NSDate *)dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day;
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@interface FSCalendar (DateTools)
|
|
||||||
|
|
||||||
/**
|
#if TARGET_INTERFACE_BUILDER
|
||||||
* Returns the number of year of the given date
|
|
||||||
*/
|
|
||||||
- (NSInteger)yearOfDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
@property (assign, nonatomic) IBInspectable CGFloat titleTextSize;
|
||||||
* Returns the number of month of the given date
|
@property (assign, nonatomic) IBInspectable CGFloat subtitleTextSize;
|
||||||
*/
|
@property (assign, nonatomic) IBInspectable CGFloat weekdayTextSize;
|
||||||
- (NSInteger)monthOfDate:(NSDate *)date;
|
@property (assign, nonatomic) IBInspectable CGFloat headerTitleTextSize;
|
||||||
|
|
||||||
/**
|
@property (strong, nonatomic) IBInspectable UIColor *eventDefaultColor;
|
||||||
* Returns the number of day of the given date
|
@property (strong, nonatomic) IBInspectable UIColor *eventSelectionColor;
|
||||||
*/
|
@property (strong, nonatomic) IBInspectable UIColor *weekdayTextColor;
|
||||||
- (NSInteger)dayOfDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
@property (strong, nonatomic) IBInspectable UIColor *headerTitleColor;
|
||||||
* Returns the number of weekday of the given date
|
@property (strong, nonatomic) IBInspectable NSString *headerDateFormat;
|
||||||
*/
|
@property (assign, nonatomic) IBInspectable CGFloat headerMinimumDissolvedAlpha;
|
||||||
- (NSInteger)weekdayOfDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
@property (strong, nonatomic) IBInspectable UIColor *titleDefaultColor;
|
||||||
* Returns the number of weekOfYear of the given date
|
@property (strong, nonatomic) IBInspectable UIColor *titleSelectionColor;
|
||||||
*/
|
@property (strong, nonatomic) IBInspectable UIColor *titleTodayColor;
|
||||||
- (NSInteger)weekOfDate:(NSDate *)date;
|
@property (strong, nonatomic) IBInspectable UIColor *titlePlaceholderColor;
|
||||||
|
@property (strong, nonatomic) IBInspectable UIColor *titleWeekendColor;
|
||||||
|
|
||||||
/**
|
@property (strong, nonatomic) IBInspectable UIColor *subtitleDefaultColor;
|
||||||
* Returns the number of hour of the given date
|
@property (strong, nonatomic) IBInspectable UIColor *subtitleSelectionColor;
|
||||||
*/
|
@property (strong, nonatomic) IBInspectable UIColor *subtitleTodayColor;
|
||||||
- (NSInteger)hourOfDate:(NSDate *)date;
|
@property (strong, nonatomic) IBInspectable UIColor *subtitlePlaceholderColor;
|
||||||
|
@property (strong, nonatomic) IBInspectable UIColor *subtitleWeekendColor;
|
||||||
|
|
||||||
/**
|
@property (strong, nonatomic) IBInspectable UIColor *selectionColor;
|
||||||
* Returns the number of minite of the given date
|
@property (strong, nonatomic) IBInspectable UIColor *todayColor;
|
||||||
*/
|
@property (strong, nonatomic) IBInspectable UIColor *todaySelectionColor;
|
||||||
- (NSInteger)miniuteOfDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
@property (strong, nonatomic) IBInspectable UIColor *borderDefaultColor;
|
||||||
* Returns the number of seconds of the given date
|
@property (strong, nonatomic) IBInspectable UIColor *borderSelectionColor;
|
||||||
*/
|
|
||||||
- (NSInteger)secondOfDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
@property (assign, nonatomic) IBInspectable CGFloat borderRadius;
|
||||||
* Returns the number of rows of the given month
|
@property (assign, nonatomic) IBInspectable BOOL useVeryShortWeekdaySymbols;
|
||||||
*/
|
|
||||||
- (NSInteger)numberOfRowsInMonth:(NSDate *)month;
|
|
||||||
|
|
||||||
/**
|
@property (assign, nonatomic) IBInspectable BOOL fakeSubtitles;
|
||||||
* Zeronizing hour、minute and second components of the given date
|
@property (assign, nonatomic) IBInspectable BOOL fakeEventDots;
|
||||||
*/
|
@property (assign, nonatomic) IBInspectable NSInteger fakedSelectedDay;
|
||||||
- (NSDate *)dateByIgnoringTimeComponentsOfDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
#endif
|
||||||
* Returns the first day of month of the given date
|
|
||||||
*/
|
|
||||||
- (NSDate *)beginingOfMonthOfDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the last day of month of the given date
|
|
||||||
*/
|
|
||||||
- (NSDate *)endOfMonthOfDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the first day of week of the given date
|
|
||||||
*/
|
|
||||||
- (NSDate *)beginingOfWeekOfDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the middle day of week of the given date
|
|
||||||
*/
|
|
||||||
- (NSDate *)middleOfWeekFromDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the next day of the given date
|
|
||||||
*/
|
|
||||||
- (NSDate *)tomorrowOfDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the previous day of the given date
|
|
||||||
*/
|
|
||||||
- (NSDate *)yesterdayOfDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of days in the month of the given date
|
|
||||||
*/
|
|
||||||
- (NSInteger)numberOfDatesInMonthOfDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instantiating a date by given string and date format.
|
|
||||||
*
|
|
||||||
* e.g.
|
|
||||||
*
|
|
||||||
* NSDate *date = [calendar dateFromString:@"2000-10-10" format:@"yyyy-MM-dd"];
|
|
||||||
*/
|
|
||||||
- (NSDate *)dateFromString:(NSString *)string format:(NSString *)format;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instantiating a date by given numbers of year、month and day.
|
|
||||||
*
|
|
||||||
* e.g.
|
|
||||||
*
|
|
||||||
* NSDate *date = [calendar dateWithYear:2000 month:10 day:10];
|
|
||||||
*/
|
|
||||||
- (NSDate *)dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new NSDate object representing the time calculated by adding given number of year to a given date.
|
|
||||||
*/
|
|
||||||
- (NSDate *)dateByAddingYears:(NSInteger)years toDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new NSDate object representing the time calculated by substracting given number of year from a given date.
|
|
||||||
*/
|
|
||||||
- (NSDate *)dateBySubstractingYears:(NSInteger)years fromDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new NSDate object representing the time calculated by adding given number of month to a given date.
|
|
||||||
*/
|
|
||||||
- (NSDate *)dateByAddingMonths:(NSInteger)months toDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new NSDate object representing the time calculated by substracting given number of month from a given date.
|
|
||||||
*/
|
|
||||||
- (NSDate *)dateBySubstractingMonths:(NSInteger)months fromDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new NSDate object representing the time calculated by adding given number of week to a given date.
|
|
||||||
*/
|
|
||||||
- (NSDate *)dateByAddingWeeks:(NSInteger)weeks toDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new NSDate object representing the time calculated by substracting given number of week from a given date.
|
|
||||||
*/
|
|
||||||
- (NSDate *)dateBySubstractingWeeks:(NSInteger)weeks fromDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new NSDate object representing the time calculated by adding given number of day to a given date.
|
|
||||||
*/
|
|
||||||
- (NSDate *)dateByAddingDays:(NSInteger)days toDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new NSDate object representing the time calculated by substracting given number of day from a given date.
|
|
||||||
*/
|
|
||||||
- (NSDate *)dateBySubstractingDays:(NSInteger)days fromDate:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the year-difference between the given dates
|
|
||||||
*/
|
|
||||||
- (NSInteger)yearsFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the month-difference between the given dates
|
|
||||||
*/
|
|
||||||
- (NSInteger)monthsFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the day-difference between the given dates
|
|
||||||
*/
|
|
||||||
- (NSInteger)daysFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the week-difference between the given dates
|
|
||||||
*/
|
|
||||||
- (NSInteger)weeksFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether two dates are equal to a given unit of calendar.
|
|
||||||
*/
|
|
||||||
- (BOOL)isDate:(NSDate *)date1 equalToDate:(NSDate *)date2 toCalendarUnit:(FSCalendarUnit)unit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the given date is in 'today' of the calendar.
|
|
||||||
*/
|
|
||||||
- (BOOL)isDateInToday:(NSDate *)date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a string representation of a given date formatted using a specific date format.
|
|
||||||
*/
|
|
||||||
- (NSString *)stringFromDate:(NSDate *)date format:(NSString *)format;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a string representation of a given date formatted using a yyyy-MM-dd.
|
|
||||||
*/
|
|
||||||
- (NSString *)stringFromDate:(NSDate *)date;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - Deprecate
|
#pragma mark - Deprecate
|
||||||
|
|
||||||
/**
|
|
||||||
* These attributes and functions are deprecated.
|
|
||||||
*/
|
|
||||||
@interface FSCalendar (Deprecated)
|
@interface FSCalendar (Deprecated)
|
||||||
@property (strong, nonatomic) NSDate *currentMonth FSCalendarDeprecated('currentPage');
|
@property (assign, nonatomic) CGFloat lineHeightMultiplier FSCalendarDeprecated(rowHeight);
|
||||||
@property (assign, nonatomic) FSCalendarFlow flow FSCalendarDeprecated('scrollDirection');
|
@property (assign, nonatomic) IBInspectable BOOL showsPlaceholders FSCalendarDeprecated('placeholderType');
|
||||||
- (void)setSelectedDate:(NSDate *)selectedDate FSCalendarDeprecated(-selectDate:);
|
@property (strong, nonatomic) NSString *identifier DEPRECATED_MSG_ATTRIBUTE("Changing calendar identifier is NOT RECOMMENDED. ");
|
||||||
- (void)setSelectedDate:(NSDate *)selectedDate animate:(BOOL)animate FSCalendarDeprecated(-selectDate:scrollToDate:);
|
|
||||||
- (BOOL)date:(NSDate *)date sharesSameMonthWithDate:(NSDate *)anotherDate FSCalendarDeprecated(-isDate:equalToDate:toCalendarUnit);
|
// Use NSCalendar.
|
||||||
- (BOOL)date:(NSDate *)date sharesSameWeekWithDate:(NSDate *)anotherDate FSCalendarDeprecated(-isDate:equalToDate:toCalendarUnit);
|
- (NSDate *)dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day FSCalendarDeprecated([NSDateFormatter dateFromString:]);
|
||||||
- (BOOL)date:(NSDate *)date sharesSameDayWithDate:(NSDate *)anotherDate FSCalendarDeprecated(-isDate:equalToDate:toCalendarUnit);
|
- (NSInteger)yearOfDate:(NSDate *)date FSCalendarDeprecated(NSCalendar component:fromDate:]);
|
||||||
|
- (NSInteger)monthOfDate:(NSDate *)date FSCalendarDeprecated(NSCalendar component:fromDate:]);
|
||||||
|
- (NSInteger)dayOfDate:(NSDate *)date FSCalendarDeprecated(NSCalendar component:fromDate:]);
|
||||||
|
- (NSInteger)weekdayOfDate:(NSDate *)date FSCalendarDeprecated(NSCalendar component:fromDate:]);
|
||||||
|
- (NSInteger)weekOfDate:(NSDate *)date FSCalendarDeprecated(NSCalendar component:fromDate:]);
|
||||||
|
- (NSDate *)dateByAddingYears:(NSInteger)years toDate:(NSDate *)date FSCalendarDeprecated([NSCalendar dateByAddingUnit:value:toDate:options:]);
|
||||||
|
- (NSDate *)dateBySubstractingYears:(NSInteger)years fromDate:(NSDate *)date FSCalendarDeprecated([NSCalendar dateByAddingUnit:value:toDate:options:]);
|
||||||
|
- (NSDate *)dateByAddingMonths:(NSInteger)months toDate:(NSDate *)date FSCalendarDeprecated([NSCalendar dateByAddingUnit:value:toDate:options:]);
|
||||||
|
- (NSDate *)dateBySubstractingMonths:(NSInteger)months fromDate:(NSDate *)date FSCalendarDeprecated([NSCalendar dateByAddingUnit:value:toDate:options:]);
|
||||||
|
- (NSDate *)dateByAddingWeeks:(NSInteger)weeks toDate:(NSDate *)date FSCalendarDeprecated([NSCalendar dateByAddingUnit:value:toDate:options:]);
|
||||||
|
- (NSDate *)dateBySubstractingWeeks:(NSInteger)weeks fromDate:(NSDate *)date FSCalendarDeprecated([NSCalendar dateByAddingUnit:value:toDate:options:]);
|
||||||
|
- (NSDate *)dateByAddingDays:(NSInteger)days toDate:(NSDate *)date FSCalendarDeprecated([NSCalendar dateByAddingUnit:value:toDate:options:]);
|
||||||
|
- (NSDate *)dateBySubstractingDays:(NSInteger)days fromDate:(NSDate *)date FSCalendarDeprecated([NSCalendar dateByAddingUnit:value:toDate:options:]);
|
||||||
|
- (BOOL)isDate:(NSDate *)date1 equalToDate:(NSDate *)date2 toCalendarUnit:(FSCalendarUnit)unit FSCalendarDeprecated([NSCalendar -isDate:equalToDate:toUnitGranularity:]);
|
||||||
|
- (BOOL)isDateInToday:(NSDate *)date FSCalendarDeprecated([NSCalendar -isDateInToday:]);
|
||||||
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,37 +0,0 @@
|
||||||
//
|
|
||||||
// FSCalendarAnimator.h
|
|
||||||
// FSCalendar
|
|
||||||
//
|
|
||||||
// Created by dingwenchao on 3/13/16.
|
|
||||||
// Copyright © 2016 wenchaoios. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import "FSCalendar.h"
|
|
||||||
#import "FSCalendarCollectionView.h"
|
|
||||||
#import "FSCalendarFlowLayout.h"
|
|
||||||
#import "FSCalendarDynamicHeader.h"
|
|
||||||
|
|
||||||
typedef NS_ENUM(NSUInteger, FSCalendarTransition) {
|
|
||||||
FSCalendarTransitionNone,
|
|
||||||
FSCalendarTransitionMonthToWeek,
|
|
||||||
FSCalendarTransitionWeekToMonth
|
|
||||||
};
|
|
||||||
typedef NS_ENUM(NSUInteger, FSCalendarTransitionState) {
|
|
||||||
FSCalendarTransitionStateIdle,
|
|
||||||
FSCalendarTransitionStateInProgress
|
|
||||||
};
|
|
||||||
|
|
||||||
@interface FSCalendarAnimator : NSObject
|
|
||||||
|
|
||||||
@property (weak, nonatomic) FSCalendar *calendar;
|
|
||||||
@property (weak, nonatomic) FSCalendarCollectionView *collectionView;
|
|
||||||
@property (weak, nonatomic) FSCalendarFlowLayout *collectionViewLayout;
|
|
||||||
|
|
||||||
@property (assign, nonatomic) FSCalendarTransition transition;
|
|
||||||
@property (assign, nonatomic) FSCalendarTransitionState state;
|
|
||||||
|
|
||||||
- (void)performScopeTransitionFromScope:(FSCalendarScope)fromScope toScope:(FSCalendarScope)toScope animated:(BOOL)animated;
|
|
||||||
- (void)performBoudingRectTransitionFromMonth:(NSDate *)fromMonth toMonth:(NSDate *)toMonth duration:(CGFloat)duration;
|
|
||||||
|
|
||||||
@end
|
|
|
@ -1,394 +0,0 @@
|
||||||
//
|
|
||||||
// FSCalendarAnimator.m
|
|
||||||
// FSCalendar
|
|
||||||
//
|
|
||||||
// Created by Wenchao Ding on 3/13/16.
|
|
||||||
// Copyright © 2016 Wenchao Ding. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "FSCalendarAnimator.h"
|
|
||||||
#import <objc/runtime.h>
|
|
||||||
#import "UIView+FSExtension.h"
|
|
||||||
|
|
||||||
@implementation FSCalendarAnimator
|
|
||||||
|
|
||||||
#pragma mark - Public methods
|
|
||||||
|
|
||||||
- (void)performScopeTransitionFromScope:(FSCalendarScope)fromScope toScope:(FSCalendarScope)toScope animated:(BOOL)animated
|
|
||||||
{
|
|
||||||
if (fromScope == toScope) {
|
|
||||||
self.transition = FSCalendarTransitionNone;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (fromScope == FSCalendarScopeMonth && toScope == FSCalendarScopeWeek) {
|
|
||||||
self.transition = FSCalendarTransitionMonthToWeek;
|
|
||||||
} else if (fromScope == FSCalendarScopeWeek && toScope == FSCalendarScopeMonth) {
|
|
||||||
self.transition = FSCalendarTransitionWeekToMonth;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start transition
|
|
||||||
self.state = FSCalendarTransitionStateInProgress;
|
|
||||||
|
|
||||||
switch (self.transition) {
|
|
||||||
|
|
||||||
case FSCalendarTransitionMonthToWeek: {
|
|
||||||
|
|
||||||
CGSize contentSize = [self.calendar sizeThatFits:self.calendar.frame.size scope:FSCalendarScopeWeek];
|
|
||||||
CGRect targetBounds = (CGRect){CGPointZero,contentSize};
|
|
||||||
|
|
||||||
NSInteger focusedRowNumber = 0;
|
|
||||||
if (self.calendar.focusOnSingleSelectedDate) {
|
|
||||||
NSDate *focusedDate = self.calendar.selectedDate;
|
|
||||||
if (focusedDate) {
|
|
||||||
UICollectionViewLayoutAttributes *attributes = [self.collectionViewLayout layoutAttributesForItemAtIndexPath:[self.calendar indexPathForDate:focusedDate scope:FSCalendarScopeMonth]];
|
|
||||||
CGPoint focuedCenter = attributes.center;
|
|
||||||
if (CGRectContainsPoint(self.collectionView.bounds, focuedCenter)) {
|
|
||||||
switch (self.collectionViewLayout.scrollDirection) {
|
|
||||||
case UICollectionViewScrollDirectionHorizontal: {
|
|
||||||
focusedRowNumber = attributes.indexPath.item%6;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case UICollectionViewScrollDirectionVertical: {
|
|
||||||
focusedRowNumber = attributes.indexPath.item/7;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
focusedDate = nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!focusedDate) {
|
|
||||||
focusedDate = self.calendar.today;
|
|
||||||
if (focusedDate) {
|
|
||||||
UICollectionViewLayoutAttributes *attributes = [self.collectionViewLayout layoutAttributesForItemAtIndexPath:[self.calendar indexPathForDate:focusedDate scope:FSCalendarScopeMonth]];
|
|
||||||
CGPoint focuedCenter = attributes.center;
|
|
||||||
if (CGRectContainsPoint(self.collectionView.bounds, focuedCenter)) {
|
|
||||||
switch (self.collectionViewLayout.scrollDirection) {
|
|
||||||
case UICollectionViewScrollDirectionHorizontal: {
|
|
||||||
focusedRowNumber = attributes.indexPath.item%6;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case UICollectionViewScrollDirectionVertical: {
|
|
||||||
focusedRowNumber = attributes.indexPath.item/7;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NSDate *currentPage = self.calendar.currentPage;
|
|
||||||
NSDate *minimumPage = [self.calendar beginingOfMonthOfDate:self.calendar.minimumDate];
|
|
||||||
NSInteger visibleSection = [self.calendar monthsFromDate:minimumPage toDate:currentPage];
|
|
||||||
NSIndexPath *firstIndexPath = [NSIndexPath indexPathForItem:0 inSection:visibleSection];
|
|
||||||
NSDate *firstDate = [self.calendar dateForIndexPath:firstIndexPath scope:FSCalendarScopeMonth];
|
|
||||||
currentPage = [self.calendar dateByAddingDays:focusedRowNumber*7 toDate:firstDate];
|
|
||||||
|
|
||||||
Ivar currentPageIvar = class_getInstanceVariable(FSCalendar.class, "_currentPage");
|
|
||||||
object_setIvar(self.calendar, currentPageIvar, currentPage);
|
|
||||||
|
|
||||||
self.calendar.contentView.clipsToBounds = YES;
|
|
||||||
self.calendar.daysContainer.clipsToBounds = YES;
|
|
||||||
if (animated) {
|
|
||||||
CGFloat duration = 0.3;
|
|
||||||
// Perform alpha animation
|
|
||||||
CABasicAnimation *opacity = [CABasicAnimation animationWithKeyPath:@"opacity"];
|
|
||||||
opacity.duration = duration*0.6;
|
|
||||||
opacity.removedOnCompletion = NO;
|
|
||||||
opacity.fillMode = kCAFillModeForwards;
|
|
||||||
opacity.toValue = @0;
|
|
||||||
[self.collectionView.visibleCells enumerateObjectsUsingBlock:^(FSCalendarCell *cell, NSUInteger idx, BOOL *stop) {
|
|
||||||
if (CGRectContainsPoint(self.collectionView.bounds, cell.center)) {
|
|
||||||
BOOL shouldPerformAlpha = NO;
|
|
||||||
NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];
|
|
||||||
switch (self.collectionViewLayout.scrollDirection) {
|
|
||||||
case UICollectionViewScrollDirectionHorizontal: {
|
|
||||||
shouldPerformAlpha = indexPath.item%6 != focusedRowNumber;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case UICollectionViewScrollDirectionVertical: {
|
|
||||||
shouldPerformAlpha = indexPath.item/7 != focusedRowNumber;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (shouldPerformAlpha) {
|
|
||||||
[cell.contentView.layer addAnimation:opacity forKey:@"opacity"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
|
|
||||||
// Perform path and frame animation
|
|
||||||
CABasicAnimation *path = [CABasicAnimation animationWithKeyPath:@"path"];
|
|
||||||
path.fromValue = (id)self.calendar.maskLayer.path;
|
|
||||||
path.toValue = (id)[UIBezierPath bezierPathWithRect:targetBounds].CGPath;
|
|
||||||
path.duration = duration;
|
|
||||||
path.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
|
|
||||||
[CATransaction begin];
|
|
||||||
[CATransaction setCompletionBlock:^{
|
|
||||||
self.state = FSCalendarTransitionStateIdle;
|
|
||||||
self.transition = FSCalendarTransitionNone;
|
|
||||||
self.collectionViewLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
|
||||||
self.calendar.header.scrollDirection = self.collectionViewLayout.scrollDirection;
|
|
||||||
self.calendar.maskLayer.path = [UIBezierPath bezierPathWithRect:targetBounds].CGPath;
|
|
||||||
[self.collectionView reloadData];
|
|
||||||
[self.collectionView layoutIfNeeded];
|
|
||||||
[self.calendar.header reloadData];
|
|
||||||
[self.calendar.header layoutIfNeeded];
|
|
||||||
self.calendar.needsAdjustingMonthPosition = YES;
|
|
||||||
self.calendar.needsAdjustingViewFrame = YES;
|
|
||||||
[self.calendar setNeedsLayout];
|
|
||||||
self.calendar.contentView.clipsToBounds = NO;
|
|
||||||
self.calendar.daysContainer.clipsToBounds = NO;
|
|
||||||
}];
|
|
||||||
[CATransaction setAnimationDuration:duration];
|
|
||||||
[self.calendar.maskLayer addAnimation:path forKey:@"path"];
|
|
||||||
[CATransaction commit];
|
|
||||||
|
|
||||||
if (self.calendar.delegate && ([self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)] || [self.calendar.delegate respondsToSelector:@selector(calendarCurrentScopeWillChange:animated:)])) {
|
|
||||||
|
|
||||||
[UIView beginAnimations:@"delegateTranslation" context:"translation"];
|
|
||||||
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
|
|
||||||
[UIView setAnimationDuration:duration];
|
|
||||||
self.collectionView.fs_top = -focusedRowNumber*self.calendar.preferredRowHeight;
|
|
||||||
self.calendar.bottomBorder.fs_top = CGRectGetMaxY(targetBounds);
|
|
||||||
if ([self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)]) {
|
|
||||||
[self.calendar.delegate calendar:self.calendar boundingRectWillChange:targetBounds animated:animated];
|
|
||||||
} else {
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
||||||
[self.calendar.delegate calendarCurrentScopeWillChange:self.calendar animated:animated];
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
}
|
|
||||||
[UIView commitAnimations];
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
self.state = FSCalendarTransitionStateIdle;
|
|
||||||
self.transition = FSCalendarTransitionNone;
|
|
||||||
self.collectionViewLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
|
||||||
self.calendar.header.scrollDirection = self.collectionViewLayout.scrollDirection;
|
|
||||||
self.calendar.needsAdjustingViewFrame = YES;
|
|
||||||
self.calendar.bottomBorder.frame = CGRectMake(0, contentSize.height, self.calendar.fs_width, 1);
|
|
||||||
self.calendar.maskLayer.path = [UIBezierPath bezierPathWithRect:targetBounds].CGPath;
|
|
||||||
self.calendar.bottomBorder.fs_top = CGRectGetMaxY(targetBounds);
|
|
||||||
[self.collectionView reloadData];
|
|
||||||
[self.collectionView layoutIfNeeded];
|
|
||||||
[self.calendar.header reloadData];
|
|
||||||
[self.calendar.header layoutIfNeeded];
|
|
||||||
self.calendar.needsAdjustingMonthPosition = YES;
|
|
||||||
self.calendar.needsAdjustingViewFrame = YES;
|
|
||||||
[self.calendar setNeedsLayout];
|
|
||||||
|
|
||||||
self.calendar.contentView.clipsToBounds = NO;
|
|
||||||
self.calendar.daysContainer.clipsToBounds = NO;
|
|
||||||
|
|
||||||
if (self.calendar.delegate && [self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)]) {
|
|
||||||
[self.calendar.delegate calendar:self.calendar boundingRectWillChange:targetBounds animated:animated];
|
|
||||||
} else if (self.calendar.delegate && [self.calendar.delegate respondsToSelector:@selector(calendarCurrentScopeWillChange:animated:)]) {
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
||||||
[self.calendar.delegate calendarCurrentScopeWillChange:self.calendar animated:animated];
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case FSCalendarTransitionWeekToMonth: {
|
|
||||||
|
|
||||||
CGSize contentSize = [self.calendar sizeThatFits:self.calendar.frame.size scope:FSCalendarScopeMonth];
|
|
||||||
CGRect targetBounds = (CGRect){CGPointZero,contentSize};
|
|
||||||
|
|
||||||
NSInteger focusedRowNumber = 0;
|
|
||||||
NSDate *currentPage = self.calendar.currentPage;
|
|
||||||
NSDate *firstDayOfMonth = nil;
|
|
||||||
if (self.calendar.focusOnSingleSelectedDate) {
|
|
||||||
NSDate *focusedDate = self.calendar.selectedDate;
|
|
||||||
if (focusedDate) {
|
|
||||||
UICollectionViewLayoutAttributes *attributes = [self.collectionViewLayout layoutAttributesForItemAtIndexPath:[self.calendar indexPathForDate:focusedDate scope:FSCalendarScopeWeek]];
|
|
||||||
CGPoint focuedCenter = attributes.center;
|
|
||||||
if (CGRectContainsPoint(self.collectionView.bounds, focuedCenter)) {
|
|
||||||
firstDayOfMonth = [self.calendar beginingOfMonthOfDate:focusedDate];
|
|
||||||
} else {
|
|
||||||
focusedDate = nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!focusedDate) {
|
|
||||||
focusedDate = self.calendar.today;
|
|
||||||
if (focusedDate) {
|
|
||||||
UICollectionViewLayoutAttributes *attributes = [self.collectionViewLayout layoutAttributesForItemAtIndexPath:[self.calendar indexPathForDate:focusedDate scope:FSCalendarScopeWeek]];
|
|
||||||
CGPoint focuedCenter = attributes.center;
|
|
||||||
if (CGRectContainsPoint(self.collectionView.bounds, focuedCenter)) {
|
|
||||||
firstDayOfMonth = [self.calendar beginingOfMonthOfDate:focusedDate];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
firstDayOfMonth = firstDayOfMonth ?: [self.calendar beginingOfMonthOfDate:currentPage];
|
|
||||||
NSInteger numberOfPlaceholdersForPrev = [self.calendar numberOfHeadPlaceholdersForMonth:firstDayOfMonth];
|
|
||||||
NSDate *firstDateOfPage = [self.calendar dateBySubstractingDays:numberOfPlaceholdersForPrev fromDate:firstDayOfMonth];
|
|
||||||
for (int i = 0; i < 6; i++) {
|
|
||||||
NSDate *currentRow = [self.calendar dateByAddingWeeks:i toDate:firstDateOfPage];
|
|
||||||
if ([self.calendar isDate:currentRow equalToDate:currentPage toCalendarUnit:FSCalendarUnitDay]) {
|
|
||||||
focusedRowNumber = i;
|
|
||||||
currentPage = firstDayOfMonth;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ivar currentPageIvar = class_getInstanceVariable(FSCalendar.class, "_currentPage");
|
|
||||||
object_setIvar(self.calendar, currentPageIvar, currentPage);
|
|
||||||
|
|
||||||
self.collectionViewLayout.scrollDirection = (UICollectionViewScrollDirection)self.calendar.scrollDirection;
|
|
||||||
self.calendar.header.scrollDirection = self.collectionViewLayout.scrollDirection;
|
|
||||||
|
|
||||||
self.calendar.needsAdjustingMonthPosition = YES;
|
|
||||||
self.calendar.needsAdjustingViewFrame = YES;
|
|
||||||
[self.calendar layoutSubviews];
|
|
||||||
[self.collectionView reloadData];
|
|
||||||
[self.collectionView layoutIfNeeded];
|
|
||||||
[self.calendar.header reloadData];
|
|
||||||
[self.calendar.header layoutIfNeeded];
|
|
||||||
|
|
||||||
self.calendar.contentView.clipsToBounds = YES;
|
|
||||||
self.calendar.daysContainer.clipsToBounds = YES;
|
|
||||||
|
|
||||||
if (animated) {
|
|
||||||
// Perform alpha animation
|
|
||||||
CGFloat duration = 0.3;
|
|
||||||
CABasicAnimation *opacity = [CABasicAnimation animationWithKeyPath:@"opacity"];
|
|
||||||
opacity.duration = duration;
|
|
||||||
opacity.fromValue = @0;
|
|
||||||
opacity.toValue = @1;
|
|
||||||
opacity.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
|
|
||||||
[self.collectionView.visibleCells enumerateObjectsUsingBlock:^(FSCalendarCell *cell, NSUInteger idx, BOOL *stop) {
|
|
||||||
if (CGRectContainsPoint(self.collectionView.bounds, cell.center)) {
|
|
||||||
BOOL shouldPerformAlpha = NO;
|
|
||||||
NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];
|
|
||||||
switch (self.collectionViewLayout.scrollDirection) {
|
|
||||||
case UICollectionViewScrollDirectionHorizontal: {
|
|
||||||
shouldPerformAlpha = indexPath.item%6 != focusedRowNumber;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case UICollectionViewScrollDirectionVertical: {
|
|
||||||
shouldPerformAlpha = indexPath.item/7 != focusedRowNumber;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (shouldPerformAlpha) {
|
|
||||||
[cell.contentView.layer addAnimation:opacity forKey:@"opacity"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
|
|
||||||
// Perform path and frame animation
|
|
||||||
BOOL oldDisableActions = [CATransaction disableActions];
|
|
||||||
[CATransaction setDisableActions:NO];
|
|
||||||
|
|
||||||
CABasicAnimation *path = [CABasicAnimation animationWithKeyPath:@"path"];
|
|
||||||
path.fromValue = (id)self.calendar.maskLayer.path;
|
|
||||||
path.toValue = (id)[UIBezierPath bezierPathWithRect:targetBounds].CGPath;
|
|
||||||
path.duration = duration;
|
|
||||||
path.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
|
|
||||||
[CATransaction begin];
|
|
||||||
[CATransaction setCompletionBlock:^{
|
|
||||||
self.state = FSCalendarTransitionStateIdle;
|
|
||||||
self.transition = FSCalendarTransitionNone;
|
|
||||||
self.calendar.maskLayer.path = [UIBezierPath bezierPathWithRect:targetBounds].CGPath;
|
|
||||||
self.calendar.contentView.clipsToBounds = NO;
|
|
||||||
self.calendar.daysContainer.clipsToBounds = NO;
|
|
||||||
}];
|
|
||||||
[CATransaction setAnimationDuration:duration];
|
|
||||||
|
|
||||||
self.calendar.needsAdjustingViewFrame = YES;
|
|
||||||
[self.calendar.maskLayer addAnimation:path forKey:@"path"];
|
|
||||||
|
|
||||||
[CATransaction commit];
|
|
||||||
|
|
||||||
if (self.calendar.delegate && ([self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)] || [self.calendar.delegate respondsToSelector:@selector(calendarCurrentScopeWillChange:animated:)])) {
|
|
||||||
self.collectionView.fs_top = -focusedRowNumber*self.calendar.preferredRowHeight;
|
|
||||||
[UIView setAnimationsEnabled:YES];
|
|
||||||
[UIView beginAnimations:@"delegateTranslation" context:"translation"];
|
|
||||||
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
|
|
||||||
[UIView setAnimationDuration:duration];
|
|
||||||
self.collectionView.fs_top = 0;
|
|
||||||
self.self.calendar.bottomBorder.frame = CGRectMake(0, contentSize.height, self.calendar.fs_width, 1);
|
|
||||||
if ([self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)]) {
|
|
||||||
[self.calendar.delegate calendar:self.calendar boundingRectWillChange:targetBounds animated:animated];
|
|
||||||
} else {
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
||||||
[self.calendar.delegate calendarCurrentScopeWillChange:self.calendar animated:animated];
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
}
|
|
||||||
[UIView commitAnimations];
|
|
||||||
}
|
|
||||||
[CATransaction setDisableActions:oldDisableActions];
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
self.state = FSCalendarTransitionStateIdle;
|
|
||||||
self.transition = FSCalendarTransitionNone;
|
|
||||||
self.calendar.needsAdjustingViewFrame = YES;
|
|
||||||
self.calendar.bottomBorder.frame = CGRectMake(0, contentSize.height, self.calendar.fs_width, 1);
|
|
||||||
self.calendar.maskLayer.path = [UIBezierPath bezierPathWithRect:targetBounds].CGPath;
|
|
||||||
self.calendar.contentView.clipsToBounds = NO;
|
|
||||||
self.calendar.daysContainer.clipsToBounds = NO;
|
|
||||||
|
|
||||||
if (self.calendar.delegate && [self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)]) {
|
|
||||||
[self.calendar.delegate calendar:self.calendar boundingRectWillChange:targetBounds animated:animated];
|
|
||||||
} else if (self.calendar.delegate && [self.calendar.delegate respondsToSelector:@selector(calendarCurrentScopeWillChange:animated:)]) {
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
||||||
[self.calendar.delegate calendarCurrentScopeWillChange:self.calendar animated:animated];
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)performBoudingRectTransitionFromMonth:(NSDate *)fromMonth toMonth:(NSDate *)toMonth duration:(CGFloat)duration
|
|
||||||
{
|
|
||||||
NSInteger lastRowCount = [self.calendar numberOfRowsInMonth:fromMonth];
|
|
||||||
NSInteger currentRowCount = [self.calendar numberOfRowsInMonth:toMonth];
|
|
||||||
if (lastRowCount != currentRowCount) {
|
|
||||||
CGFloat animationDuration = duration;
|
|
||||||
CGRect bounds = (CGRect){CGPointZero,[self.calendar sizeThatFits:self.calendar.frame.size]};
|
|
||||||
self.state = FSCalendarTransitionStateInProgress;
|
|
||||||
[UIView animateWithDuration:animationDuration delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
|
|
||||||
if (self.calendar.delegate && [self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)]) {
|
|
||||||
if (!CGRectEqualToRect((CGRect){CGPointZero,self.calendar.frame.size}, bounds)) {
|
|
||||||
[self.calendar.delegate calendar:self.calendar boundingRectWillChange:bounds animated:YES];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.calendar.bottomBorder.fs_top = CGRectGetMaxY(bounds);
|
|
||||||
} completion:^(BOOL finished) {
|
|
||||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(MAX(0, duration-animationDuration) * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
||||||
self.calendar.needsAdjustingViewFrame = YES;
|
|
||||||
[self.calendar setNeedsLayout];
|
|
||||||
self.state = FSCalendarTransitionStateIdle;
|
|
||||||
});
|
|
||||||
}];
|
|
||||||
|
|
||||||
CABasicAnimation *path = [CABasicAnimation animationWithKeyPath:@"path"];
|
|
||||||
path.fromValue = (id)self.calendar.maskLayer.path;
|
|
||||||
path.toValue = (id)[UIBezierPath bezierPathWithRect:bounds].CGPath;
|
|
||||||
path.duration = animationDuration*(currentRowCount>lastRowCount?1.25:0.75);
|
|
||||||
path.removedOnCompletion = NO;
|
|
||||||
path.fillMode = kCAFillModeForwards;
|
|
||||||
[self.calendar.maskLayer addAnimation:path forKey:@"path"];
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@end
|
|
|
@ -8,7 +8,7 @@
|
||||||
// https://github.com/WenchaoD
|
// https://github.com/WenchaoD
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "FSCalendarConstance.h"
|
#import "FSCalendarConstants.h"
|
||||||
|
|
||||||
@class FSCalendar;
|
@class FSCalendar;
|
||||||
|
|
||||||
|
@ -22,9 +22,9 @@ typedef NS_ENUM(NSInteger, FSCalendarCellState) {
|
||||||
FSCalendarCellStateTodaySelected = FSCalendarCellStateToday|FSCalendarCellStateSelected
|
FSCalendarCellStateTodaySelected = FSCalendarCellStateToday|FSCalendarCellStateSelected
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef NS_ENUM(NSUInteger, FSCalendarCellShape) {
|
typedef NS_ENUM(NSUInteger, FSCalendarSeparators) {
|
||||||
FSCalendarCellShapeCircle = 0,
|
FSCalendarSeparatorNone = 0,
|
||||||
FSCalendarCellShapeRectangle = 1
|
FSCalendarSeparatorInterRows = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef NS_OPTIONS(NSUInteger, FSCalendarCaseOptions) {
|
typedef NS_OPTIONS(NSUInteger, FSCalendarCaseOptions) {
|
||||||
|
@ -45,46 +45,53 @@ typedef NS_OPTIONS(NSUInteger, FSCalendarCaseOptions) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The font of the day text.
|
* The font of the day text.
|
||||||
*
|
|
||||||
* @warning The size of font is adjusted by calendar size. To turn it off, set adjustsFontSizeToFitContentSize to NO;
|
|
||||||
*/
|
*/
|
||||||
@property (strong, nonatomic) UIFont *titleFont;
|
@property (strong, nonatomic) UIFont *titleFont;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The font of the subtitle text.
|
* The font of the subtitle text.
|
||||||
*
|
|
||||||
* @warning The size of font is adjusted by calendar size. To turn it off, set adjustsFontSizeToFitContentSize to NO;
|
|
||||||
*/
|
*/
|
||||||
@property (strong, nonatomic) UIFont *subtitleFont;
|
@property (strong, nonatomic) UIFont *subtitleFont;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The font of the weekday text.
|
* The font of the weekday text.
|
||||||
*
|
|
||||||
* @warning The size of font is adjusted by calendar size. To turn it off, set adjustsFontSizeToFitContentSize to NO;
|
|
||||||
*/
|
*/
|
||||||
@property (strong, nonatomic) UIFont *weekdayFont;
|
@property (strong, nonatomic) UIFont *weekdayFont;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The font of the month text.
|
* The font of the month text.
|
||||||
*
|
|
||||||
* @warning The size of font is adjusted by calendar size. To turn it off, set adjustsFontSizeToFitContentSize to NO;
|
|
||||||
*/
|
*/
|
||||||
@property (strong, nonatomic) UIFont *headerTitleFont;
|
@property (strong, nonatomic) UIFont *headerTitleFont;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The vertical offset of the day text from default position.
|
* The offset of the day text from default position.
|
||||||
*/
|
*/
|
||||||
@property (assign, nonatomic) CGFloat titleVerticalOffset;
|
@property (assign, nonatomic) CGPoint titleOffset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The vertical offset of the suntitle text from default position.
|
* The offset of the day text from default position.
|
||||||
*/
|
*/
|
||||||
@property (assign, nonatomic) CGFloat subtitleVerticalOffset;
|
@property (assign, nonatomic) CGPoint subtitleOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The offset of the event dots from default position.
|
||||||
|
*/
|
||||||
|
@property (assign, nonatomic) CGPoint eventOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The offset of the image from default position.
|
||||||
|
*/
|
||||||
|
@property (assign, nonatomic) CGPoint imageOffset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The color of event dots.
|
* The color of event dots.
|
||||||
*/
|
*/
|
||||||
@property (strong, nonatomic) UIColor *eventColor;
|
@property (strong, nonatomic) UIColor *eventDefaultColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color of event dots.
|
||||||
|
*/
|
||||||
|
@property (strong, nonatomic) UIColor *eventSelectionColor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The color of weekday text.
|
* The color of weekday text.
|
||||||
|
@ -182,11 +189,9 @@ typedef NS_OPTIONS(NSUInteger, FSCalendarCaseOptions) {
|
||||||
@property (strong, nonatomic) UIColor *borderSelectionColor;
|
@property (strong, nonatomic) UIColor *borderSelectionColor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The shape appears when a day is selected or today.
|
* The border radius, while 1 means a circle, 0 means a rectangle, and the middle value will give it a corner radius.
|
||||||
*
|
|
||||||
* @see FSCalendarCellShape
|
|
||||||
*/
|
*/
|
||||||
@property (assign, nonatomic) FSCalendarCellShape cellShape;
|
@property (assign, nonatomic) CGFloat borderRadius;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The case options manage the case of month label and weekday symbols.
|
* The case options manage the case of month label and weekday symbols.
|
||||||
|
@ -196,28 +201,20 @@ typedef NS_OPTIONS(NSUInteger, FSCalendarCaseOptions) {
|
||||||
@property (assign, nonatomic) FSCalendarCaseOptions caseOptions;
|
@property (assign, nonatomic) FSCalendarCaseOptions caseOptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Boolean value indicates whether the calendar should adjust font size by its content size.
|
* The line integrations for calendar.
|
||||||
*
|
*
|
||||||
* @see titleFont
|
|
||||||
* @see subtitleFont
|
|
||||||
* @see weekdayFont
|
|
||||||
* @see headerTitleFont
|
|
||||||
*/
|
*/
|
||||||
@property (assign, nonatomic) BOOL adjustsFontSizeToFitContentSize;
|
@property (assign, nonatomic) FSCalendarSeparators separators;
|
||||||
|
|
||||||
#if TARGET_INTERFACE_BUILDER
|
#if TARGET_INTERFACE_BUILDER
|
||||||
|
|
||||||
// For preview only
|
// For preview only
|
||||||
@property (assign, nonatomic) BOOL fakeSubtitles;
|
@property (assign, nonatomic) BOOL fakeSubtitles;
|
||||||
|
@property (assign, nonatomic) BOOL fakeEventDots;
|
||||||
@property (assign, nonatomic) NSInteger fakedSelectedDay;
|
@property (assign, nonatomic) NSInteger fakedSelectedDay;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggers an appearance update.
|
|
||||||
*/
|
|
||||||
- (void)invalidateAppearance;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -225,15 +222,13 @@ typedef NS_OPTIONS(NSUInteger, FSCalendarCaseOptions) {
|
||||||
*/
|
*/
|
||||||
@interface FSCalendarAppearance (Deprecated)
|
@interface FSCalendarAppearance (Deprecated)
|
||||||
|
|
||||||
@property (assign, nonatomic) FSCalendarCellStyle cellStyle FSCalendarDeprecated('cellShape');
|
|
||||||
@property (assign, nonatomic) BOOL useVeryShortWeekdaySymbols FSCalendarDeprecated('caseOptions');
|
@property (assign, nonatomic) BOOL useVeryShortWeekdaySymbols FSCalendarDeprecated('caseOptions');
|
||||||
@property (assign, nonatomic) BOOL autoAdjustTitleSize FSCalendarDeprecated('adjustFontSizeToFitContentSize');
|
@property (assign, nonatomic) CGFloat titleVerticalOffset FSCalendarDeprecated('titleOffset');
|
||||||
@property (assign, nonatomic) BOOL adjustsFontSizeToFitCellSize FSCalendarDeprecated('adjustFontSizeToFitContentSize');
|
@property (assign, nonatomic) CGFloat subtitleVerticalOffset FSCalendarDeprecated('subtitleOffset');
|
||||||
|
@property (strong, nonatomic) UIColor *eventColor FSCalendarDeprecated('eventDefaultColor');
|
||||||
@property (assign, nonatomic) CGFloat titleTextSize FSCalendarDeprecated('titleFont');
|
@property (assign, nonatomic) FSCalendarCellShape cellShape FSCalendarDeprecated('borderRadius');
|
||||||
@property (assign, nonatomic) CGFloat subtitleTextSize FSCalendarDeprecated('subtitleFont');
|
@property (assign, nonatomic) BOOL adjustsFontSizeToFitContentSize DEPRECATED_MSG_ATTRIBUTE("The attribute \'adjustsFontSizeToFitContentSize\' is not neccesary anymore.");
|
||||||
@property (assign, nonatomic) CGFloat weekdayTextSize FSCalendarDeprecated('weekdayFont');
|
- (void)invalidateAppearance FSCalendarDeprecated('FSCalendar setNeedsConfigureAppearance');
|
||||||
@property (assign, nonatomic) CGFloat headerTitleTextSize FSCalendarDeprecated('headerTitleFont');
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
#import "FSCalendarAppearance.h"
|
#import "FSCalendarAppearance.h"
|
||||||
#import "FSCalendarDynamicHeader.h"
|
#import "FSCalendarDynamicHeader.h"
|
||||||
#import "UIView+FSExtension.h"
|
#import "FSCalendarExtensions.h"
|
||||||
|
|
||||||
@interface FSCalendarAppearance ()
|
@interface FSCalendarAppearance ()
|
||||||
|
|
||||||
|
@ -21,44 +21,6 @@
|
||||||
@property (strong, nonatomic) NSMutableDictionary *subtitleColors;
|
@property (strong, nonatomic) NSMutableDictionary *subtitleColors;
|
||||||
@property (strong, nonatomic) NSMutableDictionary *borderColors;
|
@property (strong, nonatomic) NSMutableDictionary *borderColors;
|
||||||
|
|
||||||
@property (strong, nonatomic) NSString *titleFontName;
|
|
||||||
@property (strong, nonatomic) NSString *subtitleFontName;
|
|
||||||
@property (strong, nonatomic) NSString *weekdayFontName;
|
|
||||||
@property (strong, nonatomic) NSString *headerTitleFontName;
|
|
||||||
|
|
||||||
@property (assign, nonatomic) CGFloat titleFontSize;
|
|
||||||
@property (assign, nonatomic) CGFloat subtitleFontSize;
|
|
||||||
@property (assign, nonatomic) CGFloat weekdayFontSize;
|
|
||||||
@property (assign, nonatomic) CGFloat headerTitleFontSize;
|
|
||||||
|
|
||||||
@property (assign, nonatomic) CGFloat preferredTitleFontSize;
|
|
||||||
@property (assign, nonatomic) CGFloat preferredSubtitleFontSize;
|
|
||||||
@property (assign, nonatomic) CGFloat preferredWeekdayFontSize;
|
|
||||||
@property (assign, nonatomic) CGFloat preferredHeaderTitleFontSize;
|
|
||||||
|
|
||||||
@property (readonly, nonatomic) UIFont *preferredTitleFont;
|
|
||||||
@property (readonly, nonatomic) UIFont *preferredSubtitleFont;
|
|
||||||
@property (readonly, nonatomic) UIFont *preferredWeekdayFont;
|
|
||||||
@property (readonly, nonatomic) UIFont *preferredHeaderTitleFont;
|
|
||||||
|
|
||||||
- (void)adjustTitleIfNecessary;
|
|
||||||
|
|
||||||
- (void)invalidateFonts;
|
|
||||||
- (void)invalidateTextColors;
|
|
||||||
- (void)invalidateTitleFont;
|
|
||||||
- (void)invalidateSubtitleFont;
|
|
||||||
- (void)invalidateWeekdayFont;
|
|
||||||
- (void)invalidateHeaderFont;
|
|
||||||
- (void)invalidateTitleTextColor;
|
|
||||||
- (void)invalidateSubtitleTextColor;
|
|
||||||
- (void)invalidateWeekdayTextColor;
|
|
||||||
- (void)invalidateHeaderTextColor;
|
|
||||||
|
|
||||||
- (void)invalidateBorderColors;
|
|
||||||
- (void)invalidateBackgroundColors;
|
|
||||||
- (void)invalidateEventColors;
|
|
||||||
- (void)invalidateCellShapes;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation FSCalendarAppearance
|
@implementation FSCalendarAppearance
|
||||||
|
@ -68,17 +30,10 @@
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self) {
|
if (self) {
|
||||||
|
|
||||||
_adjustsFontSizeToFitContentSize = YES;
|
_titleFont = [UIFont systemFontOfSize:FSCalendarStandardTitleTextSize];
|
||||||
|
_subtitleFont = [UIFont systemFontOfSize:FSCalendarStandardSubtitleTextSize];
|
||||||
_titleFontSize = _preferredTitleFontSize = FSCalendarStandardTitleTextSize;
|
_weekdayFont = [UIFont systemFontOfSize:FSCalendarStandardWeekdayTextSize];
|
||||||
_subtitleFontSize = _preferredSubtitleFontSize = FSCalendarStandardSubtitleTextSize;
|
_headerTitleFont = [UIFont systemFontOfSize:FSCalendarStandardHeaderTextSize];
|
||||||
_weekdayFontSize = _preferredWeekdayFontSize = FSCalendarStandardWeekdayTextSize;
|
|
||||||
_headerTitleFontSize = _preferredHeaderTitleFontSize = FSCalendarStandardHeaderTextSize;
|
|
||||||
|
|
||||||
_titleFontName = [UIFont systemFontOfSize:1].fontName;
|
|
||||||
_subtitleFontName = [UIFont systemFontOfSize:1].fontName;
|
|
||||||
_weekdayFontName = [UIFont systemFontOfSize:1].fontName;
|
|
||||||
_headerTitleFontName = [UIFont systemFontOfSize:1].fontName;
|
|
||||||
|
|
||||||
_headerTitleColor = FSCalendarStandardTitleTextColor;
|
_headerTitleColor = FSCalendarStandardTitleTextColor;
|
||||||
_headerDateFormat = @"MMMM yyyy";
|
_headerDateFormat = @"MMMM yyyy";
|
||||||
|
@ -110,113 +65,82 @@
|
||||||
_borderColors[@(FSCalendarCellStateSelected)] = [UIColor clearColor];
|
_borderColors[@(FSCalendarCellStateSelected)] = [UIColor clearColor];
|
||||||
_borderColors[@(FSCalendarCellStateNormal)] = [UIColor clearColor];
|
_borderColors[@(FSCalendarCellStateNormal)] = [UIColor clearColor];
|
||||||
|
|
||||||
_cellShape = FSCalendarCellShapeCircle;
|
_borderRadius = 1.0;
|
||||||
_eventColor = FSCalendarStandardEventDotColor;
|
_eventDefaultColor = FSCalendarStandardEventDotColor;
|
||||||
|
_eventSelectionColor = FSCalendarStandardEventDotColor;
|
||||||
|
|
||||||
_borderColors = [NSMutableDictionary dictionaryWithCapacity:2];
|
_borderColors = [NSMutableDictionary dictionaryWithCapacity:2];
|
||||||
|
|
||||||
|
#if TARGET_INTERFACE_BUILDER
|
||||||
|
_fakeEventDots = YES;
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setTitleFont:(UIFont *)titleFont
|
- (void)setTitleFont:(UIFont *)titleFont
|
||||||
{
|
{
|
||||||
BOOL needsInvalidating = NO;
|
if (![_titleFont isEqual:titleFont]) {
|
||||||
if (![_titleFontName isEqualToString:titleFont.fontName]) {
|
_titleFont = titleFont;
|
||||||
_titleFontName = titleFont.fontName;
|
[self.calendar configureAppearance];
|
||||||
needsInvalidating = YES;
|
|
||||||
}
|
}
|
||||||
if (_titleFontSize != titleFont.pointSize) {
|
|
||||||
_titleFontSize = titleFont.pointSize;
|
|
||||||
needsInvalidating = YES;
|
|
||||||
}
|
|
||||||
if (needsInvalidating) {
|
|
||||||
[self invalidateTitleFont];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIFont *)titleFont
|
|
||||||
{
|
|
||||||
return [UIFont fontWithName:_titleFontName size:_titleFontSize];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setSubtitleFont:(UIFont *)subtitleFont
|
- (void)setSubtitleFont:(UIFont *)subtitleFont
|
||||||
{
|
{
|
||||||
BOOL needsInvalidating = NO;
|
if (![_subtitleFont isEqual:subtitleFont]) {
|
||||||
if (![_subtitleFontName isEqualToString:subtitleFont.fontName]) {
|
_subtitleFont = subtitleFont;
|
||||||
_subtitleFontName = subtitleFont.fontName;
|
[self.calendar configureAppearance];
|
||||||
needsInvalidating = YES;
|
|
||||||
}
|
}
|
||||||
if (_subtitleFontSize != subtitleFont.pointSize) {
|
|
||||||
_subtitleFontSize = subtitleFont.pointSize;
|
|
||||||
needsInvalidating = YES;
|
|
||||||
}
|
|
||||||
if (needsInvalidating) {
|
|
||||||
[self invalidateSubtitleFont];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIFont *)subtitleFont
|
|
||||||
{
|
|
||||||
return [UIFont fontWithName:_subtitleFontName size:_subtitleFontSize];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setWeekdayFont:(UIFont *)weekdayFont
|
- (void)setWeekdayFont:(UIFont *)weekdayFont
|
||||||
{
|
{
|
||||||
BOOL needsInvalidating = NO;
|
if (![_weekdayFont isEqual:weekdayFont]) {
|
||||||
if (![_weekdayFontName isEqualToString:weekdayFont.fontName]) {
|
_weekdayFont = weekdayFont;
|
||||||
_weekdayFontName = weekdayFont.fontName;
|
[self.calendar configureAppearance];
|
||||||
needsInvalidating = YES;
|
|
||||||
}
|
}
|
||||||
if (_weekdayFontSize != weekdayFont.pointSize) {
|
|
||||||
_weekdayFontSize = weekdayFont.pointSize;
|
|
||||||
needsInvalidating = YES;
|
|
||||||
}
|
|
||||||
if (needsInvalidating) {
|
|
||||||
[self invalidateWeekdayFont];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIFont *)weekdayFont
|
|
||||||
{
|
|
||||||
return [UIFont fontWithName:_weekdayFontName size:_weekdayFontSize];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setHeaderTitleFont:(UIFont *)headerTitleFont
|
- (void)setHeaderTitleFont:(UIFont *)headerTitleFont
|
||||||
{
|
{
|
||||||
BOOL needsInvalidating = NO;
|
if (![_headerTitleFont isEqual:headerTitleFont]) {
|
||||||
if (![_headerTitleFontName isEqualToString:headerTitleFont.fontName]) {
|
_headerTitleFont = headerTitleFont;
|
||||||
_headerTitleFontName = headerTitleFont.fontName;
|
[self.calendar configureAppearance];
|
||||||
needsInvalidating = YES;
|
|
||||||
}
|
|
||||||
if (_headerTitleFontSize != headerTitleFont.pointSize) {
|
|
||||||
_headerTitleFontSize = headerTitleFont.pointSize;
|
|
||||||
needsInvalidating = YES;
|
|
||||||
}
|
|
||||||
if (needsInvalidating) {
|
|
||||||
[self invalidateHeaderFont];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setTitleVerticalOffset:(CGFloat)titleVerticalOffset
|
- (void)setTitleOffset:(CGPoint)titleOffset
|
||||||
{
|
{
|
||||||
if (_titleVerticalOffset != titleVerticalOffset) {
|
if (!CGPointEqualToPoint(_titleOffset, titleOffset)) {
|
||||||
_titleVerticalOffset = titleVerticalOffset;
|
_titleOffset = titleOffset;
|
||||||
[_calendar.collectionView.visibleCells makeObjectsPerformSelector:@selector(setNeedsLayout)];
|
[_calendar.visibleCells makeObjectsPerformSelector:@selector(setNeedsLayout)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setSubtitleVerticalOffset:(CGFloat)subtitleVerticalOffset
|
- (void)setSubtitleOffset:(CGPoint)subtitleOffset
|
||||||
{
|
{
|
||||||
if (_subtitleVerticalOffset != subtitleVerticalOffset) {
|
if (!CGPointEqualToPoint(_subtitleOffset, subtitleOffset)) {
|
||||||
_subtitleVerticalOffset = subtitleVerticalOffset;
|
_subtitleOffset = subtitleOffset;
|
||||||
[_calendar.collectionView.visibleCells makeObjectsPerformSelector:@selector(setNeedsLayout)];
|
[_calendar.visibleCells makeObjectsPerformSelector:@selector(setNeedsLayout)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIFont *)headerTitleFont
|
- (void)setImageOffset:(CGPoint)imageOffset
|
||||||
{
|
{
|
||||||
return [UIFont fontWithName:_headerTitleFontName size:_headerTitleFontSize];
|
if (!CGPointEqualToPoint(_imageOffset, imageOffset)) {
|
||||||
|
_imageOffset = imageOffset;
|
||||||
|
[_calendar.visibleCells makeObjectsPerformSelector:@selector(setNeedsLayout)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setEventOffset:(CGPoint)eventOffset
|
||||||
|
{
|
||||||
|
if (!CGPointEqualToPoint(_eventOffset, eventOffset)) {
|
||||||
|
_eventOffset = eventOffset;
|
||||||
|
[_calendar.visibleCells makeObjectsPerformSelector:@selector(setNeedsLayout)];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setTitleDefaultColor:(UIColor *)color
|
- (void)setTitleDefaultColor:(UIColor *)color
|
||||||
|
@ -226,7 +150,7 @@
|
||||||
} else {
|
} else {
|
||||||
[_titleColors removeObjectForKey:@(FSCalendarCellStateNormal)];
|
[_titleColors removeObjectForKey:@(FSCalendarCellStateNormal)];
|
||||||
}
|
}
|
||||||
[self invalidateTitleTextColor];
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIColor *)titleDefaultColor
|
- (UIColor *)titleDefaultColor
|
||||||
|
@ -241,7 +165,7 @@
|
||||||
} else {
|
} else {
|
||||||
[_titleColors removeObjectForKey:@(FSCalendarCellStateSelected)];
|
[_titleColors removeObjectForKey:@(FSCalendarCellStateSelected)];
|
||||||
}
|
}
|
||||||
[self invalidateTitleTextColor];
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIColor *)titleSelectionColor
|
- (UIColor *)titleSelectionColor
|
||||||
|
@ -256,7 +180,7 @@
|
||||||
} else {
|
} else {
|
||||||
[_titleColors removeObjectForKey:@(FSCalendarCellStateToday)];
|
[_titleColors removeObjectForKey:@(FSCalendarCellStateToday)];
|
||||||
}
|
}
|
||||||
[self invalidateTitleTextColor];
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIColor *)titleTodayColor
|
- (UIColor *)titleTodayColor
|
||||||
|
@ -271,7 +195,7 @@
|
||||||
} else {
|
} else {
|
||||||
[_titleColors removeObjectForKey:@(FSCalendarCellStatePlaceholder)];
|
[_titleColors removeObjectForKey:@(FSCalendarCellStatePlaceholder)];
|
||||||
}
|
}
|
||||||
[self invalidateTitleTextColor];
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIColor *)titlePlaceholderColor
|
- (UIColor *)titlePlaceholderColor
|
||||||
|
@ -286,7 +210,7 @@
|
||||||
} else {
|
} else {
|
||||||
[_titleColors removeObjectForKey:@(FSCalendarCellStateWeekend)];
|
[_titleColors removeObjectForKey:@(FSCalendarCellStateWeekend)];
|
||||||
}
|
}
|
||||||
[self invalidateTitleTextColor];
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIColor *)titleWeekendColor
|
- (UIColor *)titleWeekendColor
|
||||||
|
@ -301,7 +225,7 @@
|
||||||
} else {
|
} else {
|
||||||
[_subtitleColors removeObjectForKey:@(FSCalendarCellStateNormal)];
|
[_subtitleColors removeObjectForKey:@(FSCalendarCellStateNormal)];
|
||||||
}
|
}
|
||||||
[self invalidateSubtitleTextColor];
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
|
|
||||||
-(UIColor *)subtitleDefaultColor
|
-(UIColor *)subtitleDefaultColor
|
||||||
|
@ -316,7 +240,7 @@
|
||||||
} else {
|
} else {
|
||||||
[_subtitleColors removeObjectForKey:@(FSCalendarCellStateSelected)];
|
[_subtitleColors removeObjectForKey:@(FSCalendarCellStateSelected)];
|
||||||
}
|
}
|
||||||
[self invalidateSubtitleTextColor];
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIColor *)subtitleSelectionColor
|
- (UIColor *)subtitleSelectionColor
|
||||||
|
@ -331,7 +255,7 @@
|
||||||
} else {
|
} else {
|
||||||
[_subtitleColors removeObjectForKey:@(FSCalendarCellStateToday)];
|
[_subtitleColors removeObjectForKey:@(FSCalendarCellStateToday)];
|
||||||
}
|
}
|
||||||
[self invalidateSubtitleTextColor];
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIColor *)subtitleTodayColor
|
- (UIColor *)subtitleTodayColor
|
||||||
|
@ -346,7 +270,7 @@
|
||||||
} else {
|
} else {
|
||||||
[_subtitleColors removeObjectForKey:@(FSCalendarCellStatePlaceholder)];
|
[_subtitleColors removeObjectForKey:@(FSCalendarCellStatePlaceholder)];
|
||||||
}
|
}
|
||||||
[self invalidateSubtitleTextColor];
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIColor *)subtitlePlaceholderColor
|
- (UIColor *)subtitlePlaceholderColor
|
||||||
|
@ -361,7 +285,7 @@
|
||||||
} else {
|
} else {
|
||||||
[_subtitleColors removeObjectForKey:@(FSCalendarCellStateWeekend)];
|
[_subtitleColors removeObjectForKey:@(FSCalendarCellStateWeekend)];
|
||||||
}
|
}
|
||||||
[self invalidateSubtitleTextColor];
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIColor *)subtitleWeekendColor
|
- (UIColor *)subtitleWeekendColor
|
||||||
|
@ -376,7 +300,7 @@
|
||||||
} else {
|
} else {
|
||||||
[_backgroundColors removeObjectForKey:@(FSCalendarCellStateSelected)];
|
[_backgroundColors removeObjectForKey:@(FSCalendarCellStateSelected)];
|
||||||
}
|
}
|
||||||
[self invalidateBackgroundColors];
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIColor *)selectionColor
|
- (UIColor *)selectionColor
|
||||||
|
@ -391,7 +315,7 @@
|
||||||
} else {
|
} else {
|
||||||
[_backgroundColors removeObjectForKey:@(FSCalendarCellStateToday)];
|
[_backgroundColors removeObjectForKey:@(FSCalendarCellStateToday)];
|
||||||
}
|
}
|
||||||
[self invalidateBackgroundColors];
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIColor *)todayColor
|
- (UIColor *)todayColor
|
||||||
|
@ -406,7 +330,7 @@
|
||||||
} else {
|
} else {
|
||||||
[_backgroundColors removeObjectForKey:@(FSCalendarCellStateToday|FSCalendarCellStateSelected)];
|
[_backgroundColors removeObjectForKey:@(FSCalendarCellStateToday|FSCalendarCellStateSelected)];
|
||||||
}
|
}
|
||||||
[self invalidateBackgroundColors];
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIColor *)todaySelectionColor
|
- (UIColor *)todaySelectionColor
|
||||||
|
@ -414,11 +338,11 @@
|
||||||
return _backgroundColors[@(FSCalendarCellStateToday|FSCalendarCellStateSelected)];
|
return _backgroundColors[@(FSCalendarCellStateToday|FSCalendarCellStateSelected)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setEventColor:(UIColor *)eventColor
|
- (void)setEventDefaultColor:(UIColor *)eventDefaultColor
|
||||||
{
|
{
|
||||||
if (![_eventColor isEqual:eventColor]) {
|
if (![_eventDefaultColor isEqual:eventDefaultColor]) {
|
||||||
_eventColor = eventColor;
|
_eventDefaultColor = eventDefaultColor;
|
||||||
[self invalidateEventColors];
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -429,7 +353,7 @@
|
||||||
} else {
|
} else {
|
||||||
[_borderColors removeObjectForKey:@(FSCalendarCellStateNormal)];
|
[_borderColors removeObjectForKey:@(FSCalendarCellStateNormal)];
|
||||||
}
|
}
|
||||||
[self invalidateBorderColors];
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIColor *)borderDefaultColor
|
- (UIColor *)borderDefaultColor
|
||||||
|
@ -444,7 +368,7 @@
|
||||||
} else {
|
} else {
|
||||||
[_borderColors removeObjectForKey:@(FSCalendarCellStateSelected)];
|
[_borderColors removeObjectForKey:@(FSCalendarCellStateSelected)];
|
||||||
}
|
}
|
||||||
[self invalidateBorderColors];
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIColor *)borderSelectionColor
|
- (UIColor *)borderSelectionColor
|
||||||
|
@ -452,11 +376,13 @@
|
||||||
return _borderColors[@(FSCalendarCellStateSelected)];
|
return _borderColors[@(FSCalendarCellStateSelected)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setCellShape:(FSCalendarCellShape)cellShape
|
- (void)setBorderRadius:(CGFloat)borderRadius
|
||||||
{
|
{
|
||||||
if (_cellShape != cellShape) {
|
borderRadius = MAX(0.0, borderRadius);
|
||||||
_cellShape = cellShape;
|
borderRadius = MIN(1.0, borderRadius);
|
||||||
[self invalidateCellShapes];
|
if (_borderRadius != borderRadius) {
|
||||||
|
_borderRadius = borderRadius;
|
||||||
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,7 +390,7 @@
|
||||||
{
|
{
|
||||||
if (![_weekdayTextColor isEqual:weekdayTextColor]) {
|
if (![_weekdayTextColor isEqual:weekdayTextColor]) {
|
||||||
_weekdayTextColor = weekdayTextColor;
|
_weekdayTextColor = weekdayTextColor;
|
||||||
[self invalidateWeekdayTextColor];
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -472,7 +398,7 @@
|
||||||
{
|
{
|
||||||
if (![_headerTitleColor isEqual:color]) {
|
if (![_headerTitleColor isEqual:color]) {
|
||||||
_headerTitleColor = color;
|
_headerTitleColor = color;
|
||||||
[self invalidateHeaderTextColor];
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,8 +406,7 @@
|
||||||
{
|
{
|
||||||
if (_headerMinimumDissolvedAlpha != headerMinimumDissolvedAlpha) {
|
if (_headerMinimumDissolvedAlpha != headerMinimumDissolvedAlpha) {
|
||||||
_headerMinimumDissolvedAlpha = headerMinimumDissolvedAlpha;
|
_headerMinimumDissolvedAlpha = headerMinimumDissolvedAlpha;
|
||||||
[_calendar.header.collectionView.visibleCells makeObjectsPerformSelector:@selector(setNeedsLayout)];
|
[self.calendar configureAppearance];
|
||||||
[_calendar.visibleStickyHeaders makeObjectsPerformSelector:@selector(setNeedsLayout)];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -489,169 +414,24 @@
|
||||||
{
|
{
|
||||||
if (![_headerDateFormat isEqual:headerDateFormat]) {
|
if (![_headerDateFormat isEqual:headerDateFormat]) {
|
||||||
_headerDateFormat = headerDateFormat;
|
_headerDateFormat = headerDateFormat;
|
||||||
[_calendar invalidateHeaders];
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setAdjustsFontSizeToFitContentSize:(BOOL)adjustsFontSizeToFitContentSize
|
|
||||||
{
|
|
||||||
if (_adjustsFontSizeToFitContentSize != adjustsFontSizeToFitContentSize) {
|
|
||||||
_adjustsFontSizeToFitContentSize = adjustsFontSizeToFitContentSize;
|
|
||||||
if (adjustsFontSizeToFitContentSize) {
|
|
||||||
[self invalidateFonts];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIFont *)preferredTitleFont
|
|
||||||
{
|
|
||||||
return [UIFont fontWithName:_titleFontName size:_adjustsFontSizeToFitContentSize?_preferredTitleFontSize:_titleFontSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIFont *)preferredSubtitleFont
|
|
||||||
{
|
|
||||||
return [UIFont fontWithName:_subtitleFontName size:_adjustsFontSizeToFitContentSize?_preferredSubtitleFontSize:_subtitleFontSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIFont *)preferredWeekdayFont
|
|
||||||
{
|
|
||||||
return [UIFont fontWithName:_weekdayFontName size:_adjustsFontSizeToFitContentSize?_preferredWeekdayFontSize:_weekdayFontSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIFont *)preferredHeaderTitleFont
|
|
||||||
{
|
|
||||||
return [UIFont fontWithName:_headerTitleFontName size:_adjustsFontSizeToFitContentSize?_preferredHeaderTitleFontSize:_headerTitleFontSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)adjustTitleIfNecessary
|
|
||||||
{
|
|
||||||
if (!self.calendar.floatingMode) {
|
|
||||||
if (_adjustsFontSizeToFitContentSize) {
|
|
||||||
CGFloat factor = (_calendar.scope==FSCalendarScopeMonth) ? 6 : 1.1;
|
|
||||||
_preferredTitleFontSize = _calendar.collectionView.fs_height/3/factor;
|
|
||||||
_preferredTitleFontSize -= (_preferredTitleFontSize-FSCalendarStandardTitleTextSize)*0.5;
|
|
||||||
_preferredSubtitleFontSize = _calendar.collectionView.fs_height/4.5/factor;
|
|
||||||
_preferredSubtitleFontSize -= (_preferredSubtitleFontSize-FSCalendarStandardSubtitleTextSize)*0.75;
|
|
||||||
_preferredHeaderTitleFontSize = _preferredTitleFontSize * 1.25;
|
|
||||||
_preferredWeekdayFontSize = _preferredTitleFontSize;
|
|
||||||
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_preferredHeaderTitleFontSize = 20;
|
|
||||||
if (FSCalendarDeviceIsIPad) {
|
|
||||||
_preferredHeaderTitleFontSize = FSCalendarStandardHeaderTextSize * 1.5;
|
|
||||||
_preferredTitleFontSize = FSCalendarStandardTitleTextSize * 1.3;
|
|
||||||
_preferredSubtitleFontSize = FSCalendarStandardSubtitleTextSize * 1.15;
|
|
||||||
_preferredWeekdayFontSize = _preferredTitleFontSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// reload appearance
|
|
||||||
[self invalidateFonts];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setCaseOptions:(FSCalendarCaseOptions)caseOptions
|
- (void)setCaseOptions:(FSCalendarCaseOptions)caseOptions
|
||||||
{
|
{
|
||||||
if (_caseOptions != caseOptions) {
|
if (_caseOptions != caseOptions) {
|
||||||
_caseOptions = caseOptions;
|
_caseOptions = caseOptions;
|
||||||
[_calendar invalidateWeekdaySymbols];
|
[self.calendar configureAppearance];
|
||||||
[_calendar invalidateHeaders];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)invalidateAppearance
|
- (void)setSeparators:(FSCalendarSeparators)separators
|
||||||
{
|
{
|
||||||
[self invalidateFonts];
|
if (_separators != separators) {
|
||||||
[self invalidateTextColors];
|
_separators = separators;
|
||||||
[self invalidateBorderColors];
|
[_calendar.collectionView.collectionViewLayout invalidateLayout];
|
||||||
[self invalidateBackgroundColors];
|
}
|
||||||
/*
|
|
||||||
[_calendar.collectionView.visibleCells enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
|
|
||||||
[_calendar invalidateAppearanceForCell:obj];
|
|
||||||
}];
|
|
||||||
[_calendar.header.collectionView.visibleCells makeObjectsPerformSelector:@selector(setNeedsLayout)];
|
|
||||||
[_calendar.visibleStickyHeaders makeObjectsPerformSelector:@selector(setNeedsLayout)];
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateFonts
|
|
||||||
{
|
|
||||||
[self invalidateTitleFont];
|
|
||||||
[self invalidateSubtitleFont];
|
|
||||||
[self invalidateWeekdayFont];
|
|
||||||
[self invalidateHeaderFont];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateTextColors
|
|
||||||
{
|
|
||||||
[self invalidateTitleTextColor];
|
|
||||||
[self invalidateSubtitleTextColor];
|
|
||||||
[self invalidateWeekdayTextColor];
|
|
||||||
[self invalidateHeaderTextColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateBorderColors
|
|
||||||
{
|
|
||||||
[_calendar.collectionView.visibleCells makeObjectsPerformSelector:_cmd];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateBackgroundColors
|
|
||||||
{
|
|
||||||
[_calendar.collectionView.visibleCells makeObjectsPerformSelector:_cmd];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateEventColors
|
|
||||||
{
|
|
||||||
[_calendar.collectionView.visibleCells makeObjectsPerformSelector:_cmd];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateCellShapes
|
|
||||||
{
|
|
||||||
[_calendar.collectionView.visibleCells makeObjectsPerformSelector:_cmd];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateTitleFont
|
|
||||||
{
|
|
||||||
[_calendar.collectionView.visibleCells makeObjectsPerformSelector:_cmd];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateSubtitleFont
|
|
||||||
{
|
|
||||||
[_calendar.collectionView.visibleCells makeObjectsPerformSelector:_cmd];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateTitleTextColor
|
|
||||||
{
|
|
||||||
[_calendar.collectionView.visibleCells makeObjectsPerformSelector:_cmd];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateSubtitleTextColor
|
|
||||||
{
|
|
||||||
[_calendar.collectionView.visibleCells makeObjectsPerformSelector:_cmd];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateWeekdayFont
|
|
||||||
{
|
|
||||||
[_calendar invalidateWeekdayFont];
|
|
||||||
[_calendar.visibleStickyHeaders makeObjectsPerformSelector:_cmd];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateWeekdayTextColor
|
|
||||||
{
|
|
||||||
[_calendar invalidateWeekdayTextColor];
|
|
||||||
[_calendar.visibleStickyHeaders makeObjectsPerformSelector:_cmd];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateHeaderFont
|
|
||||||
{
|
|
||||||
[_calendar.header.collectionView.visibleCells makeObjectsPerformSelector:_cmd];
|
|
||||||
[_calendar.visibleStickyHeaders makeObjectsPerformSelector:_cmd];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateHeaderTextColor
|
|
||||||
{
|
|
||||||
[_calendar.header.collectionView.visibleCells makeObjectsPerformSelector:_cmd];
|
|
||||||
[_calendar.visibleStickyHeaders makeObjectsPerformSelector:_cmd];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -659,16 +439,6 @@
|
||||||
|
|
||||||
@implementation FSCalendarAppearance (Deprecated)
|
@implementation FSCalendarAppearance (Deprecated)
|
||||||
|
|
||||||
- (void)setCellStyle:(FSCalendarCellStyle)cellStyle
|
|
||||||
{
|
|
||||||
self.cellShape = (FSCalendarCellShape)cellStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (FSCalendarCellStyle)cellStyle
|
|
||||||
{
|
|
||||||
return (FSCalendarCellStyle)self.cellShape;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setUseVeryShortWeekdaySymbols:(BOOL)useVeryShortWeekdaySymbols
|
- (void)setUseVeryShortWeekdaySymbols:(BOOL)useVeryShortWeekdaySymbols
|
||||||
{
|
{
|
||||||
_caseOptions &= 15;
|
_caseOptions &= 15;
|
||||||
|
@ -680,65 +450,73 @@
|
||||||
return (_caseOptions & (15<<4) ) == FSCalendarCaseOptionsWeekdayUsesSingleUpperCase;
|
return (_caseOptions & (15<<4) ) == FSCalendarCaseOptionsWeekdayUsesSingleUpperCase;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setAutoAdjustTitleSize:(BOOL)autoAdjustTitleSize
|
- (void)setTitleVerticalOffset:(CGFloat)titleVerticalOffset
|
||||||
{
|
{
|
||||||
self.adjustsFontSizeToFitContentSize = autoAdjustTitleSize;
|
self.titleOffset = CGPointMake(0, titleVerticalOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)autoAdjustTitleSize
|
- (CGFloat)titleVerticalOffset
|
||||||
{
|
{
|
||||||
return self.adjustsFontSizeToFitContentSize;
|
return self.titleOffset.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setSubtitleVerticalOffset:(CGFloat)subtitleVerticalOffset
|
||||||
|
{
|
||||||
|
self.subtitleOffset = CGPointMake(0, subtitleVerticalOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)subtitleVerticalOffset
|
||||||
|
{
|
||||||
|
return self.subtitleOffset.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setEventColor:(UIColor *)eventColor
|
||||||
|
{
|
||||||
|
self.eventDefaultColor = eventColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIColor *)eventColor
|
||||||
|
{
|
||||||
|
return self.eventDefaultColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setCellShape:(FSCalendarCellShape)cellShape
|
||||||
|
{
|
||||||
|
self.borderRadius = 1-cellShape;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (FSCalendarCellShape)cellShape
|
||||||
|
{
|
||||||
|
return self.borderRadius==1.0?FSCalendarCellShapeCircle:FSCalendarCellShapeRectangle;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setTitleTextSize:(CGFloat)titleTextSize
|
- (void)setTitleTextSize:(CGFloat)titleTextSize
|
||||||
{
|
{
|
||||||
self.titleFont = [UIFont fontWithName:_titleFontName size:titleTextSize];
|
self.titleFont = [UIFont fontWithName:self.titleFont.fontName size:titleTextSize];
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)titleTextSize
|
|
||||||
{
|
|
||||||
return _titleFontSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setSubtitleTextSize:(CGFloat)subtitleTextSize
|
- (void)setSubtitleTextSize:(CGFloat)subtitleTextSize
|
||||||
{
|
{
|
||||||
self.subtitleFont = [UIFont fontWithName:_subtitleFontName size:subtitleTextSize];
|
self.subtitleFont = [UIFont fontWithName:self.subtitleFont.fontName size:subtitleTextSize];
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)subtitleTextSize
|
|
||||||
{
|
|
||||||
return _subtitleFontSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setWeekdayTextSize:(CGFloat)weekdayTextSize
|
- (void)setWeekdayTextSize:(CGFloat)weekdayTextSize
|
||||||
{
|
{
|
||||||
self.weekdayFont = [UIFont fontWithName:_weekdayFontName size:weekdayTextSize];
|
self.weekdayFont = [UIFont fontWithName:self.weekdayFont.fontName size:weekdayTextSize];
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)weekdayTextSize
|
|
||||||
{
|
|
||||||
return _weekdayFontSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setHeaderTitleTextSize:(CGFloat)headerTitleTextSize
|
- (void)setHeaderTitleTextSize:(CGFloat)headerTitleTextSize
|
||||||
{
|
{
|
||||||
self.headerTitleFont = [UIFont fontWithName:_headerTitleFontName size:headerTitleTextSize];
|
self.headerTitleFont = [UIFont fontWithName:self.headerTitleFont.fontName size:headerTitleTextSize];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CGFloat)headerTitleTextSize
|
- (void)invalidateAppearance
|
||||||
{
|
{
|
||||||
return _headerTitleFontSize;
|
[self.calendar configureAppearance];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setAdjustsFontSizeToFitCellSize:(BOOL)adjustsFontSizeToFitCellSize
|
- (void)setAdjustsFontSizeToFitContentSize:(BOOL)adjustsFontSizeToFitContentSize {}
|
||||||
{
|
- (BOOL)adjustsFontSizeToFitContentSize { return YES; }
|
||||||
self.adjustsFontSizeToFitContentSize = adjustsFontSizeToFitCellSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)adjustsFontSizeToFitCellSize
|
|
||||||
{
|
|
||||||
return self.adjustsFontSizeToFitContentSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
//
|
||||||
|
// FSCalendarCalculator.h
|
||||||
|
// FSCalendar
|
||||||
|
//
|
||||||
|
// Created by dingwenchao on 30/10/2016.
|
||||||
|
// Copyright © 2016 Wenchao Ding. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
struct FSCalendarCoordinate {
|
||||||
|
NSInteger row;
|
||||||
|
NSInteger column;
|
||||||
|
};
|
||||||
|
typedef struct FSCalendarCoordinate FSCalendarCoordinate;
|
||||||
|
|
||||||
|
@interface FSCalendarCalculator : NSObject
|
||||||
|
|
||||||
|
@property (weak , nonatomic) FSCalendar *calendar;
|
||||||
|
|
||||||
|
@property (readonly, nonatomic) NSInteger numberOfSections;
|
||||||
|
|
||||||
|
- (instancetype)initWithCalendar:(FSCalendar *)calendar;
|
||||||
|
|
||||||
|
- (NSDate *)safeDateForDate:(NSDate *)date;
|
||||||
|
|
||||||
|
- (NSDate *)dateForIndexPath:(NSIndexPath *)indexPath;
|
||||||
|
- (NSDate *)dateForIndexPath:(NSIndexPath *)indexPath scope:(FSCalendarScope)scope;
|
||||||
|
- (NSIndexPath *)indexPathForDate:(NSDate *)date;
|
||||||
|
- (NSIndexPath *)indexPathForDate:(NSDate *)date scope:(FSCalendarScope)scope;
|
||||||
|
- (NSIndexPath *)indexPathForDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)position;
|
||||||
|
- (NSIndexPath *)indexPathForDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)position scope:(FSCalendarScope)scope;
|
||||||
|
|
||||||
|
- (NSDate *)pageForSection:(NSInteger)section;
|
||||||
|
- (NSDate *)weekForSection:(NSInteger)section;
|
||||||
|
- (NSDate *)monthForSection:(NSInteger)section;
|
||||||
|
- (NSDate *)monthHeadForSection:(NSInteger)section;
|
||||||
|
|
||||||
|
- (NSInteger)numberOfHeadPlaceholdersForMonth:(NSDate *)month;
|
||||||
|
- (NSInteger)numberOfRowsInMonth:(NSDate *)month;
|
||||||
|
- (NSInteger)numberOfRowsInSection:(NSInteger)section;
|
||||||
|
|
||||||
|
- (FSCalendarMonthPosition)monthPositionForIndexPath:(NSIndexPath *)indexPath;
|
||||||
|
- (FSCalendarCoordinate)coordinateForIndexPath:(NSIndexPath *)indexPath;
|
||||||
|
|
||||||
|
- (void)reloadSections;
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,299 @@
|
||||||
|
//
|
||||||
|
// FSCalendarCalculator.m
|
||||||
|
// FSCalendar
|
||||||
|
//
|
||||||
|
// Created by dingwenchao on 30/10/2016.
|
||||||
|
// Copyright © 2016 Wenchao Ding. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "FSCalendar.h"
|
||||||
|
#import "FSCalendarCalculator.h"
|
||||||
|
#import "FSCalendarDynamicHeader.h"
|
||||||
|
#import "FSCalendarExtensions.h"
|
||||||
|
|
||||||
|
@interface FSCalendarCalculator ()
|
||||||
|
|
||||||
|
@property (assign, nonatomic) NSInteger numberOfMonths;
|
||||||
|
@property (strong, nonatomic) NSMutableDictionary<NSNumber *, NSDate *> *months;
|
||||||
|
@property (strong, nonatomic) NSMutableDictionary<NSNumber *, NSDate *> *monthHeads;
|
||||||
|
|
||||||
|
@property (assign, nonatomic) NSInteger numberOfWeeks;
|
||||||
|
@property (strong, nonatomic) NSMutableDictionary<NSNumber *, NSDate *> *weeks;
|
||||||
|
@property (strong, nonatomic) NSMutableDictionary<NSDate *, NSNumber *> *rowCounts;
|
||||||
|
|
||||||
|
@property (readonly, nonatomic) NSCalendar *gregorian;
|
||||||
|
@property (readonly, nonatomic) NSDate *minimumDate;
|
||||||
|
@property (readonly, nonatomic) NSDate *maximumDate;
|
||||||
|
|
||||||
|
- (void)didReceiveNotifications:(NSNotification *)notification;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FSCalendarCalculator
|
||||||
|
|
||||||
|
@dynamic gregorian,minimumDate,maximumDate;
|
||||||
|
|
||||||
|
- (instancetype)initWithCalendar:(FSCalendar *)calendar
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
self.calendar = calendar;
|
||||||
|
|
||||||
|
self.months = [NSMutableDictionary dictionary];
|
||||||
|
self.monthHeads = [NSMutableDictionary dictionary];
|
||||||
|
self.weeks = [NSMutableDictionary dictionary];
|
||||||
|
self.rowCounts = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveNotifications:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)forwardingTargetForSelector:(SEL)selector
|
||||||
|
{
|
||||||
|
if ([self.calendar respondsToSelector:selector]) {
|
||||||
|
return self.calendar;
|
||||||
|
}
|
||||||
|
return [super forwardingTargetForSelector:selector];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Public functions
|
||||||
|
|
||||||
|
- (NSDate *)safeDateForDate:(NSDate *)date
|
||||||
|
{
|
||||||
|
if ([self.gregorian compareDate:date toDate:self.minimumDate toUnitGranularity:NSCalendarUnitDay] == NSOrderedAscending) {
|
||||||
|
date = self.minimumDate;
|
||||||
|
} else if ([self.gregorian compareDate:date toDate:self.maximumDate toUnitGranularity:NSCalendarUnitDay] == NSOrderedDescending) {
|
||||||
|
date = self.maximumDate;
|
||||||
|
}
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDate *)dateForIndexPath:(NSIndexPath *)indexPath scope:(FSCalendarScope)scope
|
||||||
|
{
|
||||||
|
if (!indexPath) return nil;
|
||||||
|
switch (scope) {
|
||||||
|
case FSCalendarScopeMonth: {
|
||||||
|
NSDate *head = [self monthHeadForSection:indexPath.section];
|
||||||
|
NSUInteger daysOffset = indexPath.item;
|
||||||
|
NSDate *date = [self.gregorian dateByAddingUnit:NSCalendarUnitDay value:daysOffset toDate:head options:0];
|
||||||
|
return date;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FSCalendarScopeWeek: {
|
||||||
|
NSDate *currentPage = [self weekForSection:indexPath.section];
|
||||||
|
NSDate *date = [self.gregorian dateByAddingUnit:NSCalendarUnitDay value:indexPath.item toDate:currentPage options:0];
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDate *)dateForIndexPath:(NSIndexPath *)indexPath
|
||||||
|
{
|
||||||
|
if (!indexPath) return nil;
|
||||||
|
return [self dateForIndexPath:indexPath scope:self.calendar.transitionCoordinator.representingScope];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSIndexPath *)indexPathForDate:(NSDate *)date
|
||||||
|
{
|
||||||
|
return [self indexPathForDate:date atMonthPosition:FSCalendarMonthPositionCurrent scope:self.calendar.transitionCoordinator.representingScope];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSIndexPath *)indexPathForDate:(NSDate *)date scope:(FSCalendarScope)scope
|
||||||
|
{
|
||||||
|
return [self indexPathForDate:date atMonthPosition:FSCalendarMonthPositionCurrent scope:scope];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSIndexPath *)indexPathForDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)position scope:(FSCalendarScope)scope
|
||||||
|
{
|
||||||
|
if (!date) return nil;
|
||||||
|
NSInteger item = 0;
|
||||||
|
NSInteger section = 0;
|
||||||
|
switch (scope) {
|
||||||
|
case FSCalendarScopeMonth: {
|
||||||
|
section = [self.gregorian components:NSCalendarUnitMonth fromDate:[self.gregorian fs_firstDayOfMonth:self.minimumDate] toDate:[self.gregorian fs_firstDayOfMonth:date] options:0].month;
|
||||||
|
if (position == FSCalendarMonthPositionPrevious) {
|
||||||
|
section++;
|
||||||
|
} else if (position == FSCalendarMonthPositionNext) {
|
||||||
|
section--;
|
||||||
|
}
|
||||||
|
NSDate *head = [self monthHeadForSection:section];
|
||||||
|
item = [self.gregorian components:NSCalendarUnitDay fromDate:head toDate:date options:0].day;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FSCalendarScopeWeek: {
|
||||||
|
section = [self.gregorian components:NSCalendarUnitWeekOfYear fromDate:[self.gregorian fs_firstDayOfWeek:self.minimumDate] toDate:[self.gregorian fs_firstDayOfWeek:date] options:0].weekOfYear;
|
||||||
|
item = (([self.gregorian component:NSCalendarUnitWeekday fromDate:date] - self.gregorian.firstWeekday) + 7) % 7;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item < 0 || section < 0) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:section];
|
||||||
|
return indexPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSIndexPath *)indexPathForDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)position
|
||||||
|
{
|
||||||
|
return [self indexPathForDate:date atMonthPosition:position scope:self.calendar.transitionCoordinator.representingScope];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDate *)pageForSection:(NSInteger)section
|
||||||
|
{
|
||||||
|
switch (self.calendar.transitionCoordinator.representingScope) {
|
||||||
|
case FSCalendarScopeWeek:
|
||||||
|
return [self.gregorian fs_middleDayOfWeek:[self weekForSection:section]];
|
||||||
|
case FSCalendarScopeMonth:
|
||||||
|
return [self monthForSection:section];
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDate *)monthForSection:(NSInteger)section
|
||||||
|
{
|
||||||
|
NSNumber *key = @(section);
|
||||||
|
NSDate *month = self.months[key];
|
||||||
|
if (!month) {
|
||||||
|
month = [self.gregorian dateByAddingUnit:NSCalendarUnitMonth value:section toDate:[self.gregorian fs_firstDayOfMonth:self.minimumDate] options:0];
|
||||||
|
NSInteger numberOfHeadPlaceholders = [self numberOfHeadPlaceholdersForMonth:month];
|
||||||
|
NSDate *monthHead = [self.gregorian dateByAddingUnit:NSCalendarUnitDay value:-numberOfHeadPlaceholders toDate:month options:0];
|
||||||
|
self.months[key] = month;
|
||||||
|
self.monthHeads[key] = monthHead;
|
||||||
|
}
|
||||||
|
return month;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDate *)monthHeadForSection:(NSInteger)section
|
||||||
|
{
|
||||||
|
NSNumber *key = @(section);
|
||||||
|
NSDate *monthHead = self.monthHeads[key];
|
||||||
|
if (!monthHead) {
|
||||||
|
NSDate *month = [self.gregorian dateByAddingUnit:NSCalendarUnitMonth value:section toDate:[self.gregorian fs_firstDayOfMonth:self.minimumDate] options:0];
|
||||||
|
NSInteger numberOfHeadPlaceholders = [self numberOfHeadPlaceholdersForMonth:month];
|
||||||
|
monthHead = [self.gregorian dateByAddingUnit:NSCalendarUnitDay value:-numberOfHeadPlaceholders toDate:month options:0];
|
||||||
|
self.months[key] = month;
|
||||||
|
self.monthHeads[key] = monthHead;
|
||||||
|
}
|
||||||
|
return monthHead;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDate *)weekForSection:(NSInteger)section
|
||||||
|
{
|
||||||
|
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];
|
||||||
|
self.weeks[key] = week;
|
||||||
|
}
|
||||||
|
return week;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)numberOfSections
|
||||||
|
{
|
||||||
|
if (self.calendar.transitionCoordinator.transition == FSCalendarTransitionWeekToMonth) {
|
||||||
|
return self.numberOfMonths;
|
||||||
|
} else {
|
||||||
|
switch (self.calendar.transitionCoordinator.representingScope) {
|
||||||
|
case FSCalendarScopeMonth: {
|
||||||
|
return self.numberOfMonths;
|
||||||
|
}
|
||||||
|
case FSCalendarScopeWeek: {
|
||||||
|
return self.numberOfWeeks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)numberOfHeadPlaceholdersForMonth:(NSDate *)month
|
||||||
|
{
|
||||||
|
NSInteger currentWeekday = [self.gregorian component:NSCalendarUnitWeekday fromDate:month];
|
||||||
|
NSInteger number = ((currentWeekday- self.gregorian.firstWeekday) + 7) % 7 ?: (7 * (!self.calendar.floatingMode&&(self.calendar.placeholderType == FSCalendarPlaceholderTypeFillSixRows)));
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)numberOfRowsInMonth:(NSDate *)month
|
||||||
|
{
|
||||||
|
if (!month) return 0;
|
||||||
|
if (self.calendar.placeholderType == FSCalendarPlaceholderTypeFillSixRows) return 6;
|
||||||
|
|
||||||
|
NSNumber *rowCount = self.rowCounts[month];
|
||||||
|
if (!rowCount) {
|
||||||
|
NSDate *firstDayOfMonth = [self.gregorian fs_firstDayOfMonth:month];
|
||||||
|
NSInteger weekdayOfFirstDay = [self.gregorian component:NSCalendarUnitWeekday fromDate:firstDayOfMonth];
|
||||||
|
NSInteger numberOfDaysInMonth = [self.gregorian fs_numberOfDaysInMonth:month];
|
||||||
|
NSInteger numberOfPlaceholdersForPrev = ((weekdayOfFirstDay - self.gregorian.firstWeekday) + 7) % 7;
|
||||||
|
NSInteger headDayCount = numberOfDaysInMonth + numberOfPlaceholdersForPrev;
|
||||||
|
NSInteger numberOfRows = (headDayCount/7) + (headDayCount%7>0);
|
||||||
|
rowCount = @(numberOfRows);
|
||||||
|
self.rowCounts[month] = rowCount;
|
||||||
|
}
|
||||||
|
return rowCount.integerValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)numberOfRowsInSection:(NSInteger)section
|
||||||
|
{
|
||||||
|
if (self.calendar.transitionCoordinator.representingScope == FSCalendarScopeWeek) return 1;
|
||||||
|
NSDate *month = [self monthForSection:section];
|
||||||
|
return [self numberOfRowsInMonth:month];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (FSCalendarMonthPosition)monthPositionForIndexPath:(NSIndexPath *)indexPath
|
||||||
|
{
|
||||||
|
if (!indexPath) return FSCalendarMonthPositionNotFound;
|
||||||
|
if (self.calendar.transitionCoordinator.representingScope == FSCalendarScopeWeek) {
|
||||||
|
return FSCalendarMonthPositionCurrent;
|
||||||
|
}
|
||||||
|
NSDate *date = [self dateForIndexPath:indexPath];
|
||||||
|
NSDate *page = [self pageForSection:indexPath.section];
|
||||||
|
NSComparisonResult comparison = [self.gregorian compareDate:date toDate:page toUnitGranularity:NSCalendarUnitMonth];
|
||||||
|
switch (comparison) {
|
||||||
|
case NSOrderedAscending:
|
||||||
|
return FSCalendarMonthPositionPrevious;
|
||||||
|
case NSOrderedSame:
|
||||||
|
return FSCalendarMonthPositionCurrent;
|
||||||
|
case NSOrderedDescending:
|
||||||
|
return FSCalendarMonthPositionNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (FSCalendarCoordinate)coordinateForIndexPath:(NSIndexPath *)indexPath
|
||||||
|
{
|
||||||
|
FSCalendarCoordinate coordinate;
|
||||||
|
coordinate.row = indexPath.item / 7;
|
||||||
|
coordinate.column = indexPath.item % 7;
|
||||||
|
return coordinate;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (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 clearCaches];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)clearCaches
|
||||||
|
{
|
||||||
|
[self.months removeAllObjects];
|
||||||
|
[self.monthHeads removeAllObjects];
|
||||||
|
[self.weeks removeAllObjects];
|
||||||
|
[self.rowCounts removeAllObjects];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private functinos
|
||||||
|
|
||||||
|
- (void)didReceiveNotifications:(NSNotification *)notification
|
||||||
|
{
|
||||||
|
if ([notification.name isEqualToString:UIApplicationDidReceiveMemoryWarningNotification]) {
|
||||||
|
[self clearCaches];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -7,57 +7,101 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#import "FSCalendar.h"
|
|
||||||
#import "FSCalendarEventIndicator.h"
|
@class FSCalendar, FSCalendarAppearance, FSCalendarEventIndicator;
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSUInteger, FSCalendarMonthPosition);
|
||||||
|
|
||||||
@interface FSCalendarCell : UICollectionViewCell
|
@interface FSCalendarCell : UICollectionViewCell
|
||||||
|
|
||||||
|
#pragma mark - Public properties
|
||||||
|
|
||||||
|
/**
|
||||||
|
The day text label of the cell
|
||||||
|
*/
|
||||||
|
@property (weak, nonatomic) UILabel *titleLabel;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
The subtitle label of the cell
|
||||||
|
*/
|
||||||
|
@property (weak, nonatomic) UILabel *subtitleLabel;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
The shape layer of the cell
|
||||||
|
*/
|
||||||
|
@property (weak, nonatomic) CAShapeLayer *shapeLayer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
The imageView below shape layer of the cell
|
||||||
|
*/
|
||||||
|
@property (weak, nonatomic) UIImageView *imageView;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
The collection of event dots of the cell
|
||||||
|
*/
|
||||||
|
@property (weak, nonatomic) FSCalendarEventIndicator *eventIndicator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A boolean value indicates that whether the cell is "placeholder". Default is NO.
|
||||||
|
*/
|
||||||
|
@property (assign, nonatomic, getter=isPlaceholder) BOOL placeholder;
|
||||||
|
|
||||||
|
#pragma mark - Private properties
|
||||||
|
|
||||||
@property (weak, nonatomic) FSCalendar *calendar;
|
@property (weak, nonatomic) FSCalendar *calendar;
|
||||||
@property (weak, nonatomic) FSCalendarAppearance *appearance;
|
@property (weak, nonatomic) FSCalendarAppearance *appearance;
|
||||||
|
|
||||||
@property (weak, nonatomic) UILabel *titleLabel;
|
|
||||||
@property (weak, nonatomic) UILabel *subtitleLabel;
|
|
||||||
@property (weak, nonatomic) UIImageView *imageView;
|
|
||||||
|
|
||||||
@property (weak, nonatomic) CAShapeLayer *backgroundLayer;
|
|
||||||
@property (weak, nonatomic) FSCalendarEventIndicator *eventIndicator;
|
|
||||||
|
|
||||||
@property (strong, nonatomic) NSDate *date;
|
|
||||||
@property (strong, nonatomic) NSString *subtitle;
|
@property (strong, nonatomic) NSString *subtitle;
|
||||||
@property (strong, nonatomic) UIImage *image;
|
@property (strong, nonatomic) UIImage *image;
|
||||||
|
@property (assign, nonatomic) FSCalendarMonthPosition monthPosition;
|
||||||
|
|
||||||
@property (assign, nonatomic) BOOL needsAdjustingViewFrame;
|
|
||||||
@property (assign, nonatomic) NSInteger numberOfEvents;
|
@property (assign, nonatomic) NSInteger numberOfEvents;
|
||||||
|
|
||||||
@property (assign, nonatomic) BOOL dateIsPlaceholder;
|
|
||||||
@property (assign, nonatomic) BOOL dateIsSelected;
|
|
||||||
@property (assign, nonatomic) BOOL dateIsToday;
|
@property (assign, nonatomic) BOOL dateIsToday;
|
||||||
|
@property (assign, nonatomic) BOOL weekend;
|
||||||
|
|
||||||
@property (readonly, nonatomic) BOOL weekend;
|
@property (strong, nonatomic) UIColor *preferredFillDefaultColor;
|
||||||
|
@property (strong, nonatomic) UIColor *preferredFillSelectionColor;
|
||||||
@property (strong, nonatomic) UIColor *preferredSelectionColor;
|
|
||||||
@property (strong, nonatomic) UIColor *preferredTitleDefaultColor;
|
@property (strong, nonatomic) UIColor *preferredTitleDefaultColor;
|
||||||
@property (strong, nonatomic) UIColor *preferredTitleSelectionColor;
|
@property (strong, nonatomic) UIColor *preferredTitleSelectionColor;
|
||||||
@property (strong, nonatomic) UIColor *preferredSubtitleDefaultColor;
|
@property (strong, nonatomic) UIColor *preferredSubtitleDefaultColor;
|
||||||
@property (strong, nonatomic) UIColor *preferredSubtitleSelectionColor;
|
@property (strong, nonatomic) UIColor *preferredSubtitleSelectionColor;
|
||||||
@property (strong, nonatomic) UIColor *preferredBorderDefaultColor;
|
@property (strong, nonatomic) UIColor *preferredBorderDefaultColor;
|
||||||
@property (strong, nonatomic) UIColor *preferredBorderSelectionColor;
|
@property (strong, nonatomic) UIColor *preferredBorderSelectionColor;
|
||||||
@property (strong, nonatomic) id preferredEventColor;
|
@property (assign, nonatomic) CGPoint preferredTitleOffset;
|
||||||
@property (assign, nonatomic) FSCalendarCellShape preferredCellShape;
|
@property (assign, nonatomic) CGPoint preferredSubtitleOffset;
|
||||||
|
@property (assign, nonatomic) CGPoint preferredImageOffset;
|
||||||
|
@property (assign, nonatomic) CGPoint preferredEventOffset;
|
||||||
|
|
||||||
- (void)invalidateTitleFont;
|
@property (strong, nonatomic) NSArray<UIColor *> *preferredEventDefaultColors;
|
||||||
- (void)invalidateSubtitleFont;
|
@property (strong, nonatomic) NSArray<UIColor *> *preferredEventSelectionColors;
|
||||||
- (void)invalidateTitleTextColor;
|
@property (assign, nonatomic) CGFloat preferredBorderRadius;
|
||||||
- (void)invalidateSubtitleTextColor;
|
|
||||||
|
|
||||||
- (void)invalidateBorderColors;
|
// Add subviews to self.contentView and set up constraints
|
||||||
- (void)invalidateBackgroundColors;
|
- (instancetype)initWithFrame:(CGRect)frame NS_REQUIRES_SUPER;
|
||||||
- (void)invalidateEventColors;
|
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_REQUIRES_SUPER;
|
||||||
- (void)invalidateCellShapes;
|
|
||||||
|
|
||||||
- (void)invalidateImage;
|
// For DIY overridden
|
||||||
|
- (void)layoutSubviews NS_REQUIRES_SUPER; // Configure frames of subviews
|
||||||
|
- (void)configureAppearance NS_REQUIRES_SUPER; // Configure appearance for cell
|
||||||
|
|
||||||
- (UIColor *)colorForCurrentStateInDictionary:(NSDictionary *)dictionary;
|
- (UIColor *)colorForCurrentStateInDictionary:(NSDictionary *)dictionary;
|
||||||
- (void)performSelecting;
|
- (void)performSelecting;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@interface FSCalendarEventIndicator : UIView
|
||||||
|
|
||||||
|
@property (assign, nonatomic) NSInteger numberOfEvents;
|
||||||
|
@property (strong, nonatomic) id color;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface FSCalendarBlankCell : UICollectionViewCell
|
||||||
|
|
||||||
|
- (void)configureAppearance;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
|
@ -8,17 +8,18 @@
|
||||||
|
|
||||||
#import "FSCalendarCell.h"
|
#import "FSCalendarCell.h"
|
||||||
#import "FSCalendar.h"
|
#import "FSCalendar.h"
|
||||||
#import "UIView+FSExtension.h"
|
#import "FSCalendarExtensions.h"
|
||||||
#import "FSCalendarDynamicHeader.h"
|
#import "FSCalendarDynamicHeader.h"
|
||||||
#import "FSCalendarConstance.h"
|
#import "FSCalendarConstants.h"
|
||||||
|
|
||||||
@interface FSCalendarCell ()
|
@interface FSCalendarCell ()
|
||||||
|
|
||||||
@property (readonly, nonatomic) UIColor *colorForBackgroundLayer;
|
@property (readonly, nonatomic) UIColor *colorForCellFill;
|
||||||
@property (readonly, nonatomic) UIColor *colorForTitleLabel;
|
@property (readonly, nonatomic) UIColor *colorForTitleLabel;
|
||||||
@property (readonly, nonatomic) UIColor *colorForSubtitleLabel;
|
@property (readonly, nonatomic) UIColor *colorForSubtitleLabel;
|
||||||
@property (readonly, nonatomic) UIColor *colorForCellBorder;
|
@property (readonly, nonatomic) UIColor *colorForCellBorder;
|
||||||
@property (readonly, nonatomic) FSCalendarCellShape cellShape;
|
@property (readonly, nonatomic) NSArray<UIColor *> *colorsForEvents;
|
||||||
|
@property (readonly, nonatomic) CGFloat borderRadius;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -30,79 +31,137 @@
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:frame];
|
self = [super initWithFrame:frame];
|
||||||
if (self) {
|
if (self) {
|
||||||
|
[self commonInit];
|
||||||
_needsAdjustingViewFrame = YES;
|
|
||||||
|
|
||||||
UILabel *label;
|
|
||||||
CAShapeLayer *shapeLayer;
|
|
||||||
UIImageView *imageView;
|
|
||||||
FSCalendarEventIndicator *eventIndicator;
|
|
||||||
|
|
||||||
label = [[UILabel alloc] initWithFrame:CGRectZero];
|
|
||||||
label.textAlignment = NSTextAlignmentCenter;
|
|
||||||
label.textColor = [UIColor blackColor];
|
|
||||||
[self.contentView addSubview:label];
|
|
||||||
self.titleLabel = label;
|
|
||||||
|
|
||||||
label = [[UILabel alloc] initWithFrame:CGRectZero];
|
|
||||||
label.textAlignment = NSTextAlignmentCenter;
|
|
||||||
label.textColor = [UIColor lightGrayColor];
|
|
||||||
[self.contentView addSubview:label];
|
|
||||||
self.subtitleLabel = label;
|
|
||||||
|
|
||||||
shapeLayer = [CAShapeLayer layer];
|
|
||||||
shapeLayer.backgroundColor = [UIColor clearColor].CGColor;
|
|
||||||
shapeLayer.hidden = YES;
|
|
||||||
[self.contentView.layer insertSublayer:shapeLayer below:_titleLabel.layer];
|
|
||||||
self.backgroundLayer = shapeLayer;
|
|
||||||
|
|
||||||
eventIndicator = [[FSCalendarEventIndicator alloc] initWithFrame:CGRectZero];
|
|
||||||
eventIndicator.backgroundColor = [UIColor clearColor];
|
|
||||||
eventIndicator.hidden = YES;
|
|
||||||
[self.contentView addSubview:eventIndicator];
|
|
||||||
self.eventIndicator = eventIndicator;
|
|
||||||
|
|
||||||
imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
|
|
||||||
imageView.contentMode = UIViewContentModeBottom|UIViewContentModeCenter;
|
|
||||||
[self.contentView addSubview:imageView];
|
|
||||||
self.imageView = imageView;
|
|
||||||
|
|
||||||
self.clipsToBounds = NO;
|
|
||||||
self.contentView.clipsToBounds = NO;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setBounds:(CGRect)bounds
|
- (instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||||
{
|
{
|
||||||
[super setBounds:bounds];
|
self = [super initWithCoder:aDecoder];
|
||||||
CGFloat titleHeight = self.bounds.size.height*5.0/6.0;
|
if (self) {
|
||||||
CGFloat diameter = MIN(self.bounds.size.height*5.0/6.0,self.bounds.size.width);
|
[self commonInit];
|
||||||
diameter = diameter > FSCalendarStandardCellDiameter ? (diameter - (diameter-FSCalendarStandardCellDiameter)*0.5) : diameter;
|
}
|
||||||
_backgroundLayer.frame = CGRectMake((self.bounds.size.width-diameter)/2,
|
return self;
|
||||||
(titleHeight-diameter)/2,
|
}
|
||||||
diameter,
|
|
||||||
diameter);
|
- (void)commonInit
|
||||||
_backgroundLayer.borderWidth = 1.0;
|
{
|
||||||
_backgroundLayer.borderColor = [UIColor clearColor].CGColor;
|
UILabel *label;
|
||||||
|
CAShapeLayer *shapeLayer;
|
||||||
|
UIImageView *imageView;
|
||||||
|
FSCalendarEventIndicator *eventIndicator;
|
||||||
|
|
||||||
|
label = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||||
|
label.textAlignment = NSTextAlignmentCenter;
|
||||||
|
label.textColor = [UIColor blackColor];
|
||||||
|
[self.contentView addSubview:label];
|
||||||
|
self.titleLabel = label;
|
||||||
|
|
||||||
|
label = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||||
|
label.textAlignment = NSTextAlignmentCenter;
|
||||||
|
label.textColor = [UIColor lightGrayColor];
|
||||||
|
[self.contentView addSubview:label];
|
||||||
|
self.subtitleLabel = label;
|
||||||
|
|
||||||
|
shapeLayer = [CAShapeLayer layer];
|
||||||
|
shapeLayer.backgroundColor = [UIColor clearColor].CGColor;
|
||||||
|
shapeLayer.borderWidth = 1.0;
|
||||||
|
shapeLayer.borderColor = [UIColor clearColor].CGColor;
|
||||||
|
shapeLayer.opacity = 0;
|
||||||
|
[self.contentView.layer insertSublayer:shapeLayer below:_titleLabel.layer];
|
||||||
|
self.shapeLayer = shapeLayer;
|
||||||
|
|
||||||
|
eventIndicator = [[FSCalendarEventIndicator alloc] initWithFrame:CGRectZero];
|
||||||
|
eventIndicator.backgroundColor = [UIColor clearColor];
|
||||||
|
eventIndicator.hidden = YES;
|
||||||
|
[self.contentView addSubview:eventIndicator];
|
||||||
|
self.eventIndicator = eventIndicator;
|
||||||
|
|
||||||
|
imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
|
||||||
|
imageView.contentMode = UIViewContentModeBottom|UIViewContentModeCenter;
|
||||||
|
[self.contentView addSubview:imageView];
|
||||||
|
self.imageView = imageView;
|
||||||
|
|
||||||
|
self.clipsToBounds = NO;
|
||||||
|
self.contentView.clipsToBounds = NO;
|
||||||
|
|
||||||
CGFloat eventSize = _backgroundLayer.frame.size.height/6.0;
|
|
||||||
_eventIndicator.frame = CGRectMake(0, CGRectGetMaxY(_backgroundLayer.frame)+eventSize*0.17, bounds.size.width, eventSize*0.83);
|
|
||||||
_imageView.frame = self.contentView.bounds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)layoutSubviews
|
- (void)layoutSubviews
|
||||||
{
|
{
|
||||||
[super layoutSubviews];
|
[super layoutSubviews];
|
||||||
[self configureCell];
|
|
||||||
|
if (_subtitle) {
|
||||||
|
_subtitleLabel.text = _subtitle;
|
||||||
|
if (_subtitleLabel.hidden) {
|
||||||
|
_subtitleLabel.hidden = NO;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!_subtitleLabel.hidden) {
|
||||||
|
_subtitleLabel.hidden = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_subtitle) {
|
||||||
|
CGFloat titleHeight = self.titleLabel.font.lineHeight;
|
||||||
|
CGFloat subtitleHeight = self.subtitleLabel.font.lineHeight;
|
||||||
|
|
||||||
|
CGFloat height = titleHeight + subtitleHeight;
|
||||||
|
_titleLabel.frame = CGRectMake(
|
||||||
|
self.preferredTitleOffset.x,
|
||||||
|
(self.contentView.fs_height*5.0/6.0-height)*0.5+self.preferredTitleOffset.y,
|
||||||
|
self.contentView.fs_width,
|
||||||
|
titleHeight
|
||||||
|
);
|
||||||
|
_subtitleLabel.frame = CGRectMake(
|
||||||
|
self.preferredSubtitleOffset.x,
|
||||||
|
(_titleLabel.fs_bottom-self.preferredTitleOffset.y) - (_titleLabel.fs_height-_titleLabel.font.pointSize)+self.preferredSubtitleOffset.y,
|
||||||
|
self.contentView.fs_width,
|
||||||
|
subtitleHeight
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
_titleLabel.frame = CGRectMake(
|
||||||
|
self.preferredTitleOffset.x,
|
||||||
|
self.preferredTitleOffset.y,
|
||||||
|
self.contentView.fs_width,
|
||||||
|
floor(self.contentView.fs_height*5.0/6.0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_imageView.frame = CGRectMake(self.preferredImageOffset.x, self.preferredImageOffset.y, self.contentView.fs_width, self.contentView.fs_height);
|
||||||
|
|
||||||
|
CGFloat titleHeight = self.bounds.size.height*5.0/6.0;
|
||||||
|
CGFloat diameter = MIN(self.bounds.size.height*5.0/6.0,self.bounds.size.width);
|
||||||
|
diameter = diameter > FSCalendarStandardCellDiameter ? (diameter - (diameter-FSCalendarStandardCellDiameter)*0.5) : diameter;
|
||||||
|
_shapeLayer.frame = CGRectMake((self.bounds.size.width-diameter)/2,
|
||||||
|
(titleHeight-diameter)/2,
|
||||||
|
diameter,
|
||||||
|
diameter);
|
||||||
|
|
||||||
|
CGPathRef path = [UIBezierPath bezierPathWithRoundedRect:_shapeLayer.bounds
|
||||||
|
cornerRadius:CGRectGetWidth(_shapeLayer.bounds)*0.5*self.borderRadius].CGPath;
|
||||||
|
if (!CGPathEqualToPath(_shapeLayer.path,path)) {
|
||||||
|
_shapeLayer.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGFloat eventSize = _shapeLayer.frame.size.height/6.0;
|
||||||
|
_eventIndicator.frame = CGRectMake(
|
||||||
|
self.preferredEventOffset.x,
|
||||||
|
CGRectGetMaxY(_shapeLayer.frame)+eventSize*0.17+self.preferredEventOffset.y,
|
||||||
|
self.fs_width,
|
||||||
|
eventSize*0.83
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)prepareForReuse
|
- (void)prepareForReuse
|
||||||
{
|
{
|
||||||
[super prepareForReuse];
|
[super prepareForReuse];
|
||||||
[CATransaction setDisableActions:YES];
|
if (self.window) { // Avoid interrupt of navigation transition somehow
|
||||||
_backgroundLayer.hidden = YES;
|
[CATransaction setDisableActions:YES]; // Avoid blink of shape layer.
|
||||||
|
}
|
||||||
|
self.shapeLayer.opacity = 0;
|
||||||
[self.contentView.layer removeAnimationForKey:@"opacity"];
|
[self.contentView.layer removeAnimationForKey:@"opacity"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +169,7 @@
|
||||||
|
|
||||||
- (void)performSelecting
|
- (void)performSelecting
|
||||||
{
|
{
|
||||||
_backgroundLayer.hidden = NO;
|
_shapeLayer.opacity = 1;
|
||||||
|
|
||||||
#define kAnimationDuration FSCalendarDefaultBounceAnimationDuration
|
#define kAnimationDuration FSCalendarDefaultBounceAnimationDuration
|
||||||
|
|
||||||
|
@ -126,8 +185,8 @@
|
||||||
zoomIn.duration = kAnimationDuration/4;
|
zoomIn.duration = kAnimationDuration/4;
|
||||||
group.duration = kAnimationDuration;
|
group.duration = kAnimationDuration;
|
||||||
group.animations = @[zoomOut, zoomIn];
|
group.animations = @[zoomOut, zoomIn];
|
||||||
[_backgroundLayer addAnimation:group forKey:@"bounce"];
|
[_shapeLayer addAnimation:group forKey:@"bounce"];
|
||||||
[self configureCell];
|
[self configureAppearance];
|
||||||
|
|
||||||
#undef kAnimationDuration
|
#undef kAnimationDuration
|
||||||
|
|
||||||
|
@ -135,103 +194,72 @@
|
||||||
|
|
||||||
#pragma mark - Private
|
#pragma mark - Private
|
||||||
|
|
||||||
- (void)configureCell
|
- (void)configureAppearance
|
||||||
{
|
{
|
||||||
self.contentView.hidden = self.dateIsPlaceholder && !self.calendar.showsPlaceholders;
|
|
||||||
if (self.contentView.hidden) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_titleLabel.text = [NSString stringWithFormat:@"%@",@([_calendar dayOfDate:_date])];
|
|
||||||
if (_subtitle) {
|
|
||||||
_subtitleLabel.text = _subtitle;
|
|
||||||
if (_subtitleLabel.hidden) {
|
|
||||||
_subtitleLabel.hidden = NO;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!_subtitleLabel.hidden) {
|
|
||||||
_subtitleLabel.hidden = YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_needsAdjustingViewFrame || CGSizeEqualToSize(_titleLabel.frame.size, CGSizeZero)) {
|
|
||||||
_needsAdjustingViewFrame = NO;
|
|
||||||
|
|
||||||
if (_subtitle) {
|
|
||||||
CGFloat titleHeight = [@"1" sizeWithAttributes:@{NSFontAttributeName:_titleLabel.font}].height;
|
|
||||||
CGFloat subtitleHeight = [@"1" sizeWithAttributes:@{NSFontAttributeName:_subtitleLabel.font}].height;
|
|
||||||
|
|
||||||
CGFloat height = titleHeight + subtitleHeight;
|
|
||||||
_titleLabel.frame = CGRectMake(0,
|
|
||||||
(self.contentView.fs_height*5.0/6.0-height)*0.5+_appearance.titleVerticalOffset,
|
|
||||||
self.fs_width,
|
|
||||||
titleHeight);
|
|
||||||
|
|
||||||
_subtitleLabel.frame = CGRectMake(0,
|
|
||||||
_titleLabel.fs_bottom - (_titleLabel.fs_height-_titleLabel.font.pointSize)+_appearance.subtitleVerticalOffset,
|
|
||||||
self.fs_width,
|
|
||||||
subtitleHeight);
|
|
||||||
} else {
|
|
||||||
_titleLabel.frame = CGRectMake(0, _appearance.titleVerticalOffset, self.contentView.fs_width, floor(self.contentView.fs_height*5.0/6.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
UIColor *textColor = self.colorForTitleLabel;
|
UIColor *textColor = self.colorForTitleLabel;
|
||||||
if (![textColor isEqual:_titleLabel.textColor]) {
|
if (![textColor isEqual:_titleLabel.textColor]) {
|
||||||
_titleLabel.textColor = textColor;
|
_titleLabel.textColor = textColor;
|
||||||
}
|
}
|
||||||
|
UIFont *titleFont = self.calendar.appearance.titleFont;
|
||||||
|
if (![titleFont isEqual:_titleLabel.font]) {
|
||||||
|
_titleLabel.font = titleFont;
|
||||||
|
}
|
||||||
if (_subtitle) {
|
if (_subtitle) {
|
||||||
textColor = self.colorForSubtitleLabel;
|
textColor = self.colorForSubtitleLabel;
|
||||||
if (![textColor isEqual:_subtitleLabel.textColor]) {
|
if (![textColor isEqual:_subtitleLabel.textColor]) {
|
||||||
_subtitleLabel.textColor = textColor;
|
_subtitleLabel.textColor = textColor;
|
||||||
}
|
}
|
||||||
|
titleFont = self.calendar.appearance.subtitleFont;
|
||||||
|
if (![titleFont isEqual:_subtitleLabel.font]) {
|
||||||
|
_subtitleLabel.font = titleFont;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UIColor *borderColor = self.colorForCellBorder;
|
UIColor *borderColor = self.colorForCellBorder;
|
||||||
BOOL shouldHiddenBackgroundLayer = !self.selected && !self.dateIsToday && !self.dateIsSelected && !borderColor;
|
UIColor *fillColor = self.colorForCellFill;
|
||||||
|
|
||||||
if (_backgroundLayer.hidden != shouldHiddenBackgroundLayer) {
|
BOOL shouldHideShapeLayer = !self.selected && !self.dateIsToday && !borderColor && !fillColor;
|
||||||
_backgroundLayer.hidden = shouldHiddenBackgroundLayer;
|
|
||||||
|
if (_shapeLayer.opacity == shouldHideShapeLayer) {
|
||||||
|
_shapeLayer.opacity = !shouldHideShapeLayer;
|
||||||
}
|
}
|
||||||
if (!shouldHiddenBackgroundLayer) {
|
if (!shouldHideShapeLayer) {
|
||||||
|
|
||||||
CGPathRef path = self.cellShape == FSCalendarCellShapeCircle ?
|
CGColorRef cellFillColor = self.colorForCellFill.CGColor;
|
||||||
[UIBezierPath bezierPathWithOvalInRect:_backgroundLayer.bounds].CGPath :
|
if (!CGColorEqualToColor(_shapeLayer.fillColor, cellFillColor)) {
|
||||||
[UIBezierPath bezierPathWithRect:_backgroundLayer.bounds].CGPath;
|
_shapeLayer.fillColor = cellFillColor;
|
||||||
if (!CGPathEqualToPath(_backgroundLayer.path,path)) {
|
|
||||||
_backgroundLayer.path = path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CGColorRef backgroundColor = self.colorForBackgroundLayer.CGColor;
|
CGColorRef cellBorderColor = self.colorForCellBorder.CGColor;
|
||||||
if (!CGColorEqualToColor(_backgroundLayer.fillColor, backgroundColor)) {
|
if (!CGColorEqualToColor(_shapeLayer.strokeColor, cellBorderColor)) {
|
||||||
_backgroundLayer.fillColor = backgroundColor;
|
_shapeLayer.strokeColor = cellBorderColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
CGColorRef borderColor = self.colorForCellBorder.CGColor;
|
CGPathRef path = [UIBezierPath bezierPathWithRoundedRect:_shapeLayer.bounds
|
||||||
if (!CGColorEqualToColor(_backgroundLayer.strokeColor, borderColor)) {
|
cornerRadius:CGRectGetWidth(_shapeLayer.bounds)*0.5*self.borderRadius].CGPath;
|
||||||
_backgroundLayer.strokeColor = borderColor;
|
if (!CGPathEqualToPath(_shapeLayer.path, path)) {
|
||||||
|
_shapeLayer.path = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![_image isEqual:_imageView.image]) {
|
if (![_image isEqual:_imageView.image]) {
|
||||||
[self invalidateImage];
|
_imageView.image = _image;
|
||||||
|
_imageView.hidden = !_image;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_eventIndicator.hidden == (_numberOfEvents > 0)) {
|
if (_eventIndicator.hidden == (_numberOfEvents > 0)) {
|
||||||
_eventIndicator.hidden = !_numberOfEvents;
|
_eventIndicator.hidden = !_numberOfEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
_eventIndicator.numberOfEvents = self.numberOfEvents;
|
_eventIndicator.numberOfEvents = self.numberOfEvents;
|
||||||
_eventIndicator.color = self.preferredEventColor ?: _appearance.eventColor;
|
_eventIndicator.color = self.colorsForEvents;
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isWeekend
|
|
||||||
{
|
|
||||||
return _date && ([_calendar weekdayOfDate:_date] == 1 || [_calendar weekdayOfDate:_date] == 7);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIColor *)colorForCurrentStateInDictionary:(NSDictionary *)dictionary
|
- (UIColor *)colorForCurrentStateInDictionary:(NSDictionary *)dictionary
|
||||||
{
|
{
|
||||||
if (self.isSelected || self.dateIsSelected) {
|
if (self.isSelected) {
|
||||||
if (self.dateIsToday) {
|
if (self.dateIsToday) {
|
||||||
return dictionary[@(FSCalendarCellStateSelected|FSCalendarCellStateToday)] ?: dictionary[@(FSCalendarCellStateSelected)];
|
return dictionary[@(FSCalendarCellStateSelected|FSCalendarCellStateToday)] ?: dictionary[@(FSCalendarCellStateSelected)];
|
||||||
}
|
}
|
||||||
|
@ -240,77 +268,28 @@
|
||||||
if (self.dateIsToday && [[dictionary allKeys] containsObject:@(FSCalendarCellStateToday)]) {
|
if (self.dateIsToday && [[dictionary allKeys] containsObject:@(FSCalendarCellStateToday)]) {
|
||||||
return dictionary[@(FSCalendarCellStateToday)];
|
return dictionary[@(FSCalendarCellStateToday)];
|
||||||
}
|
}
|
||||||
if (self.dateIsPlaceholder && [[dictionary allKeys] containsObject:@(FSCalendarCellStatePlaceholder)]) {
|
if (self.placeholder && [[dictionary allKeys] containsObject:@(FSCalendarCellStatePlaceholder)]) {
|
||||||
return dictionary[@(FSCalendarCellStatePlaceholder)];
|
return dictionary[@(FSCalendarCellStatePlaceholder)];
|
||||||
}
|
}
|
||||||
if (self.isWeekend && [[dictionary allKeys] containsObject:@(FSCalendarCellStateWeekend)]) {
|
if (self.weekend && [[dictionary allKeys] containsObject:@(FSCalendarCellStateWeekend)]) {
|
||||||
return dictionary[@(FSCalendarCellStateWeekend)];
|
return dictionary[@(FSCalendarCellStateWeekend)];
|
||||||
}
|
}
|
||||||
return dictionary[@(FSCalendarCellStateNormal)];
|
return dictionary[@(FSCalendarCellStateNormal)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)invalidateTitleFont
|
|
||||||
{
|
|
||||||
_titleLabel.font = self.appearance.preferredTitleFont;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateTitleTextColor
|
|
||||||
{
|
|
||||||
_titleLabel.textColor = self.colorForTitleLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateSubtitleFont
|
|
||||||
{
|
|
||||||
_subtitleLabel.font = self.appearance.preferredSubtitleFont;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateSubtitleTextColor
|
|
||||||
{
|
|
||||||
_subtitleLabel.textColor = self.colorForSubtitleLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateBorderColors
|
|
||||||
{
|
|
||||||
_backgroundLayer.strokeColor = self.colorForCellBorder.CGColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateBackgroundColors
|
|
||||||
{
|
|
||||||
_backgroundLayer.fillColor = self.colorForBackgroundLayer.CGColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateEventColors
|
|
||||||
{
|
|
||||||
_eventIndicator.color = self.preferredEventColor ?: _appearance.eventColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateCellShapes
|
|
||||||
{
|
|
||||||
CGPathRef path = self.cellShape == FSCalendarCellShapeCircle ?
|
|
||||||
[UIBezierPath bezierPathWithOvalInRect:_backgroundLayer.bounds].CGPath :
|
|
||||||
[UIBezierPath bezierPathWithRect:_backgroundLayer.bounds].CGPath;
|
|
||||||
_backgroundLayer.path = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateImage
|
|
||||||
{
|
|
||||||
_imageView.image = _image;
|
|
||||||
_imageView.hidden = !_image;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Properties
|
#pragma mark - Properties
|
||||||
|
|
||||||
- (UIColor *)colorForBackgroundLayer
|
- (UIColor *)colorForCellFill
|
||||||
{
|
{
|
||||||
if (self.dateIsSelected || self.isSelected) {
|
if (self.selected) {
|
||||||
return self.preferredSelectionColor ?: [self colorForCurrentStateInDictionary:_appearance.backgroundColors];
|
return self.preferredFillSelectionColor ?: [self colorForCurrentStateInDictionary:_appearance.backgroundColors];
|
||||||
}
|
}
|
||||||
return [self colorForCurrentStateInDictionary:_appearance.backgroundColors];
|
return self.preferredFillDefaultColor ?: [self colorForCurrentStateInDictionary:_appearance.backgroundColors];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIColor *)colorForTitleLabel
|
- (UIColor *)colorForTitleLabel
|
||||||
{
|
{
|
||||||
if (self.dateIsSelected || self.isSelected) {
|
if (self.selected) {
|
||||||
return self.preferredTitleSelectionColor ?: [self colorForCurrentStateInDictionary:_appearance.titleColors];
|
return self.preferredTitleSelectionColor ?: [self colorForCurrentStateInDictionary:_appearance.titleColors];
|
||||||
}
|
}
|
||||||
return self.preferredTitleDefaultColor ?: [self colorForCurrentStateInDictionary:_appearance.titleColors];
|
return self.preferredTitleDefaultColor ?: [self colorForCurrentStateInDictionary:_appearance.titleColors];
|
||||||
|
@ -318,7 +297,7 @@
|
||||||
|
|
||||||
- (UIColor *)colorForSubtitleLabel
|
- (UIColor *)colorForSubtitleLabel
|
||||||
{
|
{
|
||||||
if (self.dateIsSelected || self.isSelected) {
|
if (self.selected) {
|
||||||
return self.preferredSubtitleSelectionColor ?: [self colorForCurrentStateInDictionary:_appearance.subtitleColors];
|
return self.preferredSubtitleSelectionColor ?: [self colorForCurrentStateInDictionary:_appearance.subtitleColors];
|
||||||
}
|
}
|
||||||
return self.preferredSubtitleDefaultColor ?: [self colorForCurrentStateInDictionary:_appearance.subtitleColors];
|
return self.preferredSubtitleDefaultColor ?: [self colorForCurrentStateInDictionary:_appearance.subtitleColors];
|
||||||
|
@ -326,51 +305,168 @@
|
||||||
|
|
||||||
- (UIColor *)colorForCellBorder
|
- (UIColor *)colorForCellBorder
|
||||||
{
|
{
|
||||||
if (self.dateIsSelected || self.isSelected) {
|
if (self.selected) {
|
||||||
return _preferredBorderSelectionColor ?: _appearance.borderSelectionColor;
|
return _preferredBorderSelectionColor ?: _appearance.borderSelectionColor;
|
||||||
}
|
}
|
||||||
return _preferredBorderDefaultColor ?: _appearance.borderDefaultColor;
|
return _preferredBorderDefaultColor ?: _appearance.borderDefaultColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (FSCalendarCellShape)cellShape
|
- (NSArray<UIColor *> *)colorsForEvents
|
||||||
{
|
{
|
||||||
return _preferredCellShape ?: _appearance.cellShape;
|
if (self.selected) {
|
||||||
|
return _preferredEventSelectionColors ?: @[_appearance.eventSelectionColor];
|
||||||
|
}
|
||||||
|
return _preferredEventDefaultColors ?: @[_appearance.eventDefaultColor];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (CGFloat)borderRadius
|
||||||
|
{
|
||||||
|
return _preferredBorderRadius >= 0 ? _preferredBorderRadius : _appearance.borderRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define OFFSET_PROPERTY(NAME,CAPITAL,ALTERNATIVE) \
|
||||||
|
\
|
||||||
|
@synthesize NAME = _##NAME; \
|
||||||
|
\
|
||||||
|
- (void)set##CAPITAL:(CGPoint)NAME \
|
||||||
|
{ \
|
||||||
|
BOOL diff = !CGPointEqualToPoint(NAME, self.NAME); \
|
||||||
|
_##NAME = NAME; \
|
||||||
|
if (diff) { \
|
||||||
|
[self setNeedsLayout]; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
- (CGPoint)NAME \
|
||||||
|
{ \
|
||||||
|
return CGPointEqualToPoint(_##NAME, CGPointInfinity) ? ALTERNATIVE : _##NAME; \
|
||||||
|
}
|
||||||
|
|
||||||
|
OFFSET_PROPERTY(preferredTitleOffset, PreferredTitleOffset, _appearance.titleOffset);
|
||||||
|
OFFSET_PROPERTY(preferredSubtitleOffset, PreferredSubtitleOffset, _appearance.subtitleOffset);
|
||||||
|
OFFSET_PROPERTY(preferredImageOffset, PreferredImageOffset, _appearance.imageOffset);
|
||||||
|
OFFSET_PROPERTY(preferredEventOffset, PreferredEventOffset, _appearance.eventOffset);
|
||||||
|
|
||||||
|
#undef OFFSET_PROPERTY
|
||||||
|
|
||||||
- (void)setCalendar:(FSCalendar *)calendar
|
- (void)setCalendar:(FSCalendar *)calendar
|
||||||
{
|
{
|
||||||
if (![_calendar isEqual:calendar]) {
|
if (![_calendar isEqual:calendar]) {
|
||||||
_calendar = calendar;
|
_calendar = calendar;
|
||||||
}
|
|
||||||
if (![_appearance isEqual:calendar.appearance]) {
|
|
||||||
_appearance = calendar.appearance;
|
_appearance = calendar.appearance;
|
||||||
[self invalidateTitleFont];
|
[self configureAppearance];
|
||||||
[self invalidateSubtitleFont];
|
|
||||||
[self invalidateTitleTextColor];
|
|
||||||
[self invalidateSubtitleTextColor];
|
|
||||||
[self invalidateEventColors];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setSubtitle:(NSString *)subtitle
|
- (void)setSubtitle:(NSString *)subtitle
|
||||||
{
|
{
|
||||||
if (![_subtitle isEqualToString:subtitle]) {
|
if (![_subtitle isEqualToString:subtitle]) {
|
||||||
_needsAdjustingViewFrame = !(_subtitle.length && subtitle.length);
|
BOOL diff = (subtitle.length && !_subtitle.length) || (_subtitle.length && !subtitle.length);
|
||||||
_subtitle = subtitle;
|
_subtitle = subtitle;
|
||||||
if (_needsAdjustingViewFrame) {
|
if (diff) {
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setNeedsAdjustingViewFrame:(BOOL)needsAdjustingViewFrame
|
@end
|
||||||
{
|
|
||||||
if (_needsAdjustingViewFrame != needsAdjustingViewFrame) {
|
|
||||||
_needsAdjustingViewFrame = needsAdjustingViewFrame;
|
@interface FSCalendarEventIndicator ()
|
||||||
_eventIndicator.needsAdjustingViewFrame = needsAdjustingViewFrame;
|
|
||||||
}
|
@property (weak, nonatomic) UIView *contentView;
|
||||||
}
|
|
||||||
|
@property (strong, nonatomic) NSPointerArray *eventLayers;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FSCalendarEventIndicator
|
||||||
|
|
||||||
|
- (instancetype)initWithFrame:(CGRect)frame
|
||||||
|
{
|
||||||
|
self = [super initWithFrame:frame];
|
||||||
|
if (self) {
|
||||||
|
|
||||||
|
UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
|
||||||
|
[self addSubview:view];
|
||||||
|
self.contentView = view;
|
||||||
|
|
||||||
|
self.eventLayers = [NSPointerArray weakObjectsPointerArray];
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
CALayer *layer = [CALayer layer];
|
||||||
|
layer.backgroundColor = [UIColor clearColor].CGColor;
|
||||||
|
[self.contentView.layer addSublayer:layer];
|
||||||
|
[self.eventLayers addPointer:(__bridge void * _Nullable)(layer)];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)layoutSubviews
|
||||||
|
{
|
||||||
|
[super layoutSubviews];
|
||||||
|
CGFloat diameter = MIN(MIN(self.fs_width, self.fs_height),FSCalendarMaximumEventDotDiameter);
|
||||||
|
self.contentView.fs_height = self.fs_height;
|
||||||
|
self.contentView.fs_width = (self.numberOfEvents*2-1)*diameter;
|
||||||
|
self.contentView.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)layoutSublayersOfLayer:(CALayer *)layer
|
||||||
|
{
|
||||||
|
[super layoutSublayersOfLayer:layer];
|
||||||
|
if (layer == self.layer) {
|
||||||
|
|
||||||
|
CGFloat diameter = MIN(MIN(self.fs_width, self.fs_height),FSCalendarMaximumEventDotDiameter);
|
||||||
|
for (int i = 0; i < self.eventLayers.count; i++) {
|
||||||
|
CALayer *eventLayer = [self.eventLayers pointerAtIndex:i];
|
||||||
|
eventLayer.hidden = i >= self.numberOfEvents;
|
||||||
|
if (!eventLayer.hidden) {
|
||||||
|
eventLayer.frame = CGRectMake(2*i*diameter, (self.fs_height-diameter)*0.5, diameter, diameter);
|
||||||
|
if (eventLayer.cornerRadius != diameter/2) {
|
||||||
|
eventLayer.cornerRadius = diameter/2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setColor:(id)color
|
||||||
|
{
|
||||||
|
if (![_color isEqual:color]) {
|
||||||
|
_color = color;
|
||||||
|
|
||||||
|
if ([_color isKindOfClass:[UIColor class]]) {
|
||||||
|
for (NSInteger i = 0; i < self.eventLayers.count; i++) {
|
||||||
|
CALayer *layer = [self.eventLayers pointerAtIndex:i];
|
||||||
|
layer.backgroundColor = [_color CGColor];
|
||||||
|
}
|
||||||
|
} else if ([_color isKindOfClass:[NSArray class]]) {
|
||||||
|
NSArray<UIColor *> *colors = (NSArray *)_color;
|
||||||
|
for (int i = 0; i < self.eventLayers.count; i++) {
|
||||||
|
CALayer *eventLayer = [self.eventLayers pointerAtIndex:i];
|
||||||
|
eventLayer.backgroundColor = colors[MIN(i,colors.count-1)].CGColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setNumberOfEvents:(NSInteger)numberOfEvents
|
||||||
|
{
|
||||||
|
if (_numberOfEvents != numberOfEvents) {
|
||||||
|
_numberOfEvents = MIN(MAX(numberOfEvents,0),3);
|
||||||
|
[self setNeedsLayout];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@implementation FSCalendarBlankCell
|
||||||
|
|
||||||
|
- (void)configureAppearance {}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// FSCalendar
|
// FSCalendar
|
||||||
//
|
//
|
||||||
// Created by Wenchao Ding on 10/25/15.
|
// Created by Wenchao Ding on 10/25/15.
|
||||||
// Copyright (c) 2015 wenchaoios. All rights reserved.
|
// Copyright (c) 2015 Wenchao Ding. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
@ -11,3 +11,8 @@
|
||||||
@interface FSCalendarCollectionView : UICollectionView
|
@interface FSCalendarCollectionView : UICollectionView
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@interface FSCalendarSeparator : UICollectionReusableView
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
|
@ -3,11 +3,14 @@
|
||||||
// FSCalendar
|
// FSCalendar
|
||||||
//
|
//
|
||||||
// Created by Wenchao Ding on 10/25/15.
|
// Created by Wenchao Ding on 10/25/15.
|
||||||
// Copyright (c) 2015 wenchaoios. All rights reserved.
|
// Copyright (c) 2015 Wenchao Ding. All rights reserved.
|
||||||
//
|
//
|
||||||
|
// Reject -[UIScrollView(UIScrollViewInternal) _adjustContentOffsetIfNecessary]
|
||||||
|
|
||||||
|
|
||||||
#import "FSCalendarCollectionView.h"
|
#import "FSCalendarCollectionView.h"
|
||||||
#import "FSCalendarCell.h"
|
#import "FSCalendarExtensions.h"
|
||||||
|
#import "FSCalendarConstants.h"
|
||||||
|
|
||||||
@interface FSCalendarCollectionView ()
|
@interface FSCalendarCollectionView ()
|
||||||
|
|
||||||
|
@ -41,16 +44,27 @@
|
||||||
{
|
{
|
||||||
self.scrollsToTop = NO;
|
self.scrollsToTop = NO;
|
||||||
self.contentInset = UIEdgeInsetsZero;
|
self.contentInset = UIEdgeInsetsZero;
|
||||||
|
|
||||||
#ifdef __IPHONE_9_0
|
#ifdef __IPHONE_9_0
|
||||||
if ([self respondsToSelector:@selector(setSemanticContentAttribute:)]) {
|
if ([self respondsToSelector:@selector(setSemanticContentAttribute:)]) {
|
||||||
self.semanticContentAttribute = UISemanticContentAttributeForceLeftToRight;
|
self.semanticContentAttribute = UISemanticContentAttributeForceLeftToRight;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __IPHONE_10_0
|
||||||
|
SEL selector = NSSelectorFromString(@"setPrefetchingEnabled:");
|
||||||
|
if (selector && [self respondsToSelector:selector]) {
|
||||||
|
[self fs_performSelector:selector withObjects:@NO, nil];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setContentInset:(UIEdgeInsets)contentInset
|
- (void)setContentInset:(UIEdgeInsets)contentInset
|
||||||
{
|
{
|
||||||
[super setContentInset:UIEdgeInsetsZero];
|
[super setContentInset:UIEdgeInsetsZero];
|
||||||
|
if (contentInset.top) {
|
||||||
|
self.contentOffset = CGPointMake(self.contentOffset.x, self.contentOffset.y+contentInset.top);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setScrollsToTop:(BOOL)scrollsToTop
|
- (void)setScrollsToTop:(BOOL)scrollsToTop
|
||||||
|
@ -60,3 +74,24 @@
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@implementation FSCalendarSeparator
|
||||||
|
|
||||||
|
- (instancetype)initWithFrame:(CGRect)frame
|
||||||
|
{
|
||||||
|
self = [super initWithFrame:frame];
|
||||||
|
if (self) {
|
||||||
|
self.backgroundColor = FSCalendarStandardSeparatorColor;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
|
||||||
|
{
|
||||||
|
self.frame = layoutAttributes.frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
22
Verdnaturaventas/Pods/FSCalendar/FSCalendar/FSCalendarCollectionViewLayout.h
generated
Normal file
22
Verdnaturaventas/Pods/FSCalendar/FSCalendar/FSCalendarCollectionViewLayout.h
generated
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
//
|
||||||
|
// FSCalendarAnimationLayout.h
|
||||||
|
// FSCalendar
|
||||||
|
//
|
||||||
|
// Created by dingwenchao on 1/3/16.
|
||||||
|
// Copyright © 2016 Wenchao Ding. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@class FSCalendar;
|
||||||
|
|
||||||
|
@interface FSCalendarCollectionViewLayout : UICollectionViewLayout
|
||||||
|
|
||||||
|
@property (weak, nonatomic) FSCalendar *calendar;
|
||||||
|
|
||||||
|
@property (assign, nonatomic) CGFloat interitemSpacing;
|
||||||
|
@property (assign, nonatomic) UIEdgeInsets sectionInsets;
|
||||||
|
@property (assign, nonatomic) UICollectionViewScrollDirection scrollDirection;
|
||||||
|
@property (assign, nonatomic) CGSize headerReferenceSize;
|
||||||
|
|
||||||
|
@end
|
561
Verdnaturaventas/Pods/FSCalendar/FSCalendar/FSCalendarCollectionViewLayout.m
generated
Normal file
561
Verdnaturaventas/Pods/FSCalendar/FSCalendar/FSCalendarCollectionViewLayout.m
generated
Normal file
|
@ -0,0 +1,561 @@
|
||||||
|
//
|
||||||
|
// FSCalendarAnimationLayout.m
|
||||||
|
// FSCalendar
|
||||||
|
//
|
||||||
|
// Created by dingwenchao on 1/3/16.
|
||||||
|
// Copyright © 2016 Wenchao Ding. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "FSCalendarCollectionViewLayout.h"
|
||||||
|
#import "FSCalendar.h"
|
||||||
|
#import "FSCalendarDynamicHeader.h"
|
||||||
|
#import "FSCalendarCollectionView.h"
|
||||||
|
#import "FSCalendarExtensions.h"
|
||||||
|
#import "FSCalendarConstants.h"
|
||||||
|
|
||||||
|
#define kFSCalendarSeparatorInterRows @"FSCalendarSeparatorInterRows"
|
||||||
|
#define kFSCalendarSeparatorInterColumns @"FSCalendarSeparatorInterColumns"
|
||||||
|
|
||||||
|
@interface FSCalendarCollectionViewLayout ()
|
||||||
|
|
||||||
|
@property (assign, nonatomic) CGFloat *widths;
|
||||||
|
@property (assign, nonatomic) CGFloat *heights;
|
||||||
|
@property (assign, nonatomic) CGFloat *lefts;
|
||||||
|
@property (assign, nonatomic) CGFloat *tops;
|
||||||
|
|
||||||
|
@property (assign, nonatomic) CGFloat *sectionHeights;
|
||||||
|
@property (assign, nonatomic) CGFloat *sectionTops;
|
||||||
|
@property (assign, nonatomic) CGFloat *sectionBottoms;
|
||||||
|
@property (assign, nonatomic) CGFloat *sectionRowCounts;
|
||||||
|
|
||||||
|
@property (assign, nonatomic) CGSize estimatedItemSize;
|
||||||
|
|
||||||
|
@property (assign, nonatomic) CGSize contentSize;
|
||||||
|
@property (assign, nonatomic) CGSize collectionViewSize;
|
||||||
|
@property (assign, nonatomic) NSInteger numberOfSections;
|
||||||
|
|
||||||
|
@property (assign, nonatomic) FSCalendarSeparators separators;
|
||||||
|
|
||||||
|
@property (strong, nonatomic) NSMutableDictionary<NSIndexPath *, UICollectionViewLayoutAttributes *> *itemAttributes;
|
||||||
|
@property (strong, nonatomic) NSMutableDictionary<NSIndexPath *, UICollectionViewLayoutAttributes *> *headerAttributes;
|
||||||
|
@property (strong, nonatomic) NSMutableDictionary<NSIndexPath *, UICollectionViewLayoutAttributes *> *rowSeparatorAttributes;
|
||||||
|
|
||||||
|
- (void)didReceiveNotifications:(NSNotification *)notification;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FSCalendarCollectionViewLayout
|
||||||
|
|
||||||
|
- (instancetype)init
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
self.estimatedItemSize = CGSizeZero;
|
||||||
|
self.widths = NULL;
|
||||||
|
self.heights = NULL;
|
||||||
|
self.tops = NULL;
|
||||||
|
self.lefts = NULL;
|
||||||
|
|
||||||
|
self.sectionHeights = NULL;
|
||||||
|
self.sectionTops = NULL;
|
||||||
|
self.sectionBottoms = NULL;
|
||||||
|
self.sectionRowCounts = NULL;
|
||||||
|
|
||||||
|
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||||
|
self.sectionInsets = UIEdgeInsetsMake(5, 0, 5, 0);
|
||||||
|
|
||||||
|
self.itemAttributes = [NSMutableDictionary dictionary];
|
||||||
|
self.headerAttributes = [NSMutableDictionary dictionary];
|
||||||
|
self.rowSeparatorAttributes = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveNotifications:) name:UIDeviceOrientationDidChangeNotification object:nil];
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveNotifications:) name:UIScreenDidConnectNotification object:nil];
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveNotifications:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
|
||||||
|
|
||||||
|
[self registerClass:[FSCalendarSeparator class] forDecorationViewOfKind:kFSCalendarSeparatorInterRows];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
|
||||||
|
|
||||||
|
free(self.widths);
|
||||||
|
free(self.heights);
|
||||||
|
free(self.tops);
|
||||||
|
free(self.lefts);
|
||||||
|
|
||||||
|
free(self.sectionHeights);
|
||||||
|
free(self.sectionTops);
|
||||||
|
free(self.sectionRowCounts);
|
||||||
|
free(self.sectionBottoms);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)prepareLayout
|
||||||
|
{
|
||||||
|
if (CGSizeEqualToSize(self.collectionViewSize, self.collectionView.frame.size) && self.numberOfSections == self.collectionView.numberOfSections && self.separators == self.calendar.appearance.separators) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.collectionViewSize = self.collectionView.frame.size;
|
||||||
|
self.separators = self.calendar.appearance.separators;
|
||||||
|
|
||||||
|
[self.itemAttributes removeAllObjects];
|
||||||
|
[self.headerAttributes removeAllObjects];
|
||||||
|
[self.rowSeparatorAttributes removeAllObjects];
|
||||||
|
|
||||||
|
self.headerReferenceSize = ({
|
||||||
|
CGSize headerSize = CGSizeZero;
|
||||||
|
if (self.calendar.floatingMode) {
|
||||||
|
CGFloat headerHeight = self.calendar.preferredWeekdayHeight*1.5+self.calendar.preferredHeaderHeight;
|
||||||
|
headerSize = CGSizeMake(self.collectionView.fs_width, headerHeight);
|
||||||
|
}
|
||||||
|
headerSize;
|
||||||
|
});
|
||||||
|
self.estimatedItemSize = ({
|
||||||
|
CGFloat width = (self.collectionView.fs_width-self.sectionInsets.left-self.sectionInsets.right)/7.0;
|
||||||
|
CGFloat height = ({
|
||||||
|
CGFloat height = FSCalendarStandardRowHeight;
|
||||||
|
if (!self.calendar.floatingMode) {
|
||||||
|
switch (self.calendar.transitionCoordinator.representingScope) {
|
||||||
|
case FSCalendarScopeMonth: {
|
||||||
|
height = (self.collectionView.fs_height-self.sectionInsets.top-self.sectionInsets.bottom)/6.0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FSCalendarScopeWeek: {
|
||||||
|
height = (self.collectionView.fs_height-self.sectionInsets.top-self.sectionInsets.bottom);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
height = self.calendar.rowHeight;
|
||||||
|
}
|
||||||
|
height;
|
||||||
|
});
|
||||||
|
CGSize size = CGSizeMake(width, height);
|
||||||
|
size;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calculate item widths and lefts
|
||||||
|
free(self.widths);
|
||||||
|
self.widths = ({
|
||||||
|
NSInteger columnCount = 7;
|
||||||
|
size_t columnSize = sizeof(CGFloat)*columnCount;
|
||||||
|
CGFloat *widths = malloc(columnSize);
|
||||||
|
CGFloat contentWidth = self.collectionView.fs_width - self.sectionInsets.left - self.sectionInsets.right;
|
||||||
|
FSCalendarSliceCake(contentWidth, columnCount, widths);
|
||||||
|
widths;
|
||||||
|
});
|
||||||
|
|
||||||
|
free(self.lefts);
|
||||||
|
self.lefts = ({
|
||||||
|
NSInteger columnCount = 7;
|
||||||
|
size_t columnSize = sizeof(CGFloat)*columnCount;
|
||||||
|
CGFloat *lefts = malloc(columnSize);
|
||||||
|
lefts[0] = self.sectionInsets.left;
|
||||||
|
for (int i = 1; i < columnCount; i++) {
|
||||||
|
lefts[i] = lefts[i-1] + self.widths[i-1];
|
||||||
|
}
|
||||||
|
lefts;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calculate item heights and tops
|
||||||
|
free(self.heights);
|
||||||
|
self.heights = ({
|
||||||
|
NSInteger rowCount = self.calendar.transitionCoordinator.representingScope == FSCalendarScopeWeek ? 1 : 6;
|
||||||
|
size_t rowSize = sizeof(CGFloat)*rowCount;
|
||||||
|
CGFloat *heights = malloc(rowSize);
|
||||||
|
if (!self.calendar.floatingMode) {
|
||||||
|
CGFloat contentHeight = self.collectionView.fs_height - self.sectionInsets.top - self.sectionInsets.bottom;
|
||||||
|
FSCalendarSliceCake(contentHeight, rowCount, heights);
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < rowCount; i++) {
|
||||||
|
heights[i] = self.estimatedItemSize.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
heights;
|
||||||
|
});
|
||||||
|
|
||||||
|
free(self.tops);
|
||||||
|
self.tops = ({
|
||||||
|
NSInteger rowCount = self.calendar.transitionCoordinator.representingScope == FSCalendarScopeWeek ? 1 : 6;
|
||||||
|
size_t rowSize = sizeof(CGFloat)*rowCount;
|
||||||
|
CGFloat *tops = malloc(rowSize);
|
||||||
|
tops[0] = self.sectionInsets.top;
|
||||||
|
for (int i = 1; i < rowCount; i++) {
|
||||||
|
tops[i] = tops[i-1] + self.heights[i-1];
|
||||||
|
}
|
||||||
|
tops;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calculate content size
|
||||||
|
self.numberOfSections = self.collectionView.numberOfSections;
|
||||||
|
self.contentSize = ({
|
||||||
|
CGSize contentSize = CGSizeZero;
|
||||||
|
if (!self.calendar.floatingMode) {
|
||||||
|
CGFloat width = self.collectionView.fs_width;
|
||||||
|
CGFloat height = self.collectionView.fs_height;
|
||||||
|
switch (self.scrollDirection) {
|
||||||
|
case UICollectionViewScrollDirectionHorizontal: {
|
||||||
|
width *= self.numberOfSections;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UICollectionViewScrollDirectionVertical: {
|
||||||
|
height *= self.numberOfSections;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
contentSize = CGSizeMake(width, height);
|
||||||
|
} else {
|
||||||
|
free(self.sectionHeights);
|
||||||
|
self.sectionHeights = malloc(sizeof(CGFloat)*self.numberOfSections);
|
||||||
|
free(self.sectionRowCounts);
|
||||||
|
self.sectionRowCounts = malloc(sizeof(NSInteger)*self.numberOfSections);
|
||||||
|
CGFloat width = self.collectionView.fs_width;
|
||||||
|
CGFloat height = 0;
|
||||||
|
for (int i = 0; i < self.numberOfSections; i++) {
|
||||||
|
NSInteger rowCount = [self.calendar.calculator numberOfRowsInSection:i];
|
||||||
|
self.sectionRowCounts[i] = rowCount;
|
||||||
|
CGFloat sectionHeight = self.headerReferenceSize.height;
|
||||||
|
for (int j = 0; j < rowCount; j++) {
|
||||||
|
sectionHeight += self.heights[j];
|
||||||
|
}
|
||||||
|
self.sectionHeights[i] = sectionHeight;
|
||||||
|
height += sectionHeight;
|
||||||
|
}
|
||||||
|
free(self.sectionTops);
|
||||||
|
self.sectionTops = malloc(sizeof(CGFloat)*self.numberOfSections);
|
||||||
|
free(self.sectionBottoms);
|
||||||
|
self.sectionBottoms = malloc(sizeof(CGFloat)*self.numberOfSections);
|
||||||
|
self.sectionTops[0] = 0;
|
||||||
|
self.sectionBottoms[0] = self.sectionHeights[0];
|
||||||
|
for (int i = 1; i < self.numberOfSections; i++) {
|
||||||
|
self.sectionTops[i] = self.sectionTops[i-1] + self.sectionHeights[i-1];
|
||||||
|
self.sectionBottoms[i] = self.sectionTops[i] + self.sectionHeights[i];
|
||||||
|
}
|
||||||
|
contentSize = CGSizeMake(width, height);
|
||||||
|
}
|
||||||
|
contentSize;
|
||||||
|
});
|
||||||
|
|
||||||
|
[self.calendar adjustMonthPosition];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGSize)collectionViewContentSize
|
||||||
|
{
|
||||||
|
return self.contentSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
|
||||||
|
{
|
||||||
|
// Clipping
|
||||||
|
rect = CGRectIntersection(rect, CGRectMake(0, 0, self.contentSize.width, self.contentSize.height));
|
||||||
|
if (CGRectIsEmpty(rect)) return nil;
|
||||||
|
|
||||||
|
// Calculating attributes
|
||||||
|
NSMutableArray<UICollectionViewLayoutAttributes *> *layoutAttributes = [NSMutableArray array];
|
||||||
|
|
||||||
|
if (!self.calendar.floatingMode) {
|
||||||
|
|
||||||
|
switch (self.scrollDirection) {
|
||||||
|
case UICollectionViewScrollDirectionHorizontal: {
|
||||||
|
|
||||||
|
NSInteger startColumn = ({
|
||||||
|
NSInteger startSection = rect.origin.x/self.collectionView.fs_width;
|
||||||
|
CGFloat widthDelta = FSCalendarMod(CGRectGetMinX(rect), self.collectionView.fs_width)-self.sectionInsets.left;
|
||||||
|
widthDelta = MIN(MAX(0, widthDelta), self.collectionView.fs_width-self.sectionInsets.left);
|
||||||
|
NSInteger countDelta = FSCalendarFloor(widthDelta/self.estimatedItemSize.width);
|
||||||
|
NSInteger startColumn = startSection*7 + countDelta;
|
||||||
|
startColumn;
|
||||||
|
});
|
||||||
|
|
||||||
|
NSInteger endColumn = ({
|
||||||
|
NSInteger endColumn;
|
||||||
|
CGFloat section = CGRectGetMaxX(rect)/self.collectionView.fs_width;
|
||||||
|
CGFloat remainder = FSCalendarMod(section, 1);
|
||||||
|
// https://stackoverflow.com/a/10335601/2398107
|
||||||
|
if (remainder <= MAX(100*FLT_EPSILON*ABS(remainder), FLT_MIN)) {
|
||||||
|
endColumn = FSCalendarFloor(section)*7 - 1;
|
||||||
|
} else {
|
||||||
|
CGFloat widthDelta = FSCalendarMod(CGRectGetMaxX(rect), self.collectionView.fs_width)-self.sectionInsets.left;
|
||||||
|
widthDelta = MIN(MAX(0, widthDelta), self.collectionView.fs_width - self.sectionInsets.left);
|
||||||
|
NSInteger countDelta = FSCalendarCeil(widthDelta/self.estimatedItemSize.width);
|
||||||
|
endColumn = FSCalendarFloor(section)*7 + countDelta - 1;
|
||||||
|
}
|
||||||
|
endColumn;
|
||||||
|
});
|
||||||
|
|
||||||
|
NSInteger numberOfRows = self.calendar.transitionCoordinator.representingScope == FSCalendarScopeMonth ? 6 : 1;
|
||||||
|
|
||||||
|
for (NSInteger column = startColumn; column <= endColumn; column++) {
|
||||||
|
for (NSInteger row = 0; row < numberOfRows; row++) {
|
||||||
|
NSInteger section = column / 7;
|
||||||
|
NSInteger item = column % 7 + row * 7;
|
||||||
|
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:section];
|
||||||
|
UICollectionViewLayoutAttributes *itemAttributes = [self layoutAttributesForItemAtIndexPath:indexPath];
|
||||||
|
[layoutAttributes addObject:itemAttributes];
|
||||||
|
|
||||||
|
UICollectionViewLayoutAttributes *rowSeparatorAttributes = [self layoutAttributesForDecorationViewOfKind:kFSCalendarSeparatorInterRows atIndexPath:indexPath];
|
||||||
|
if (rowSeparatorAttributes) {
|
||||||
|
[layoutAttributes addObject:rowSeparatorAttributes];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UICollectionViewScrollDirectionVertical: {
|
||||||
|
|
||||||
|
NSInteger startRow = ({
|
||||||
|
NSInteger startSection = rect.origin.y/self.collectionView.fs_height;
|
||||||
|
CGFloat heightDelta = FSCalendarMod(CGRectGetMinY(rect), self.collectionView.fs_height)-self.sectionInsets.top;
|
||||||
|
heightDelta = MIN(MAX(0, heightDelta), self.collectionView.fs_height-self.sectionInsets.top);
|
||||||
|
NSInteger countDelta = FSCalendarFloor(heightDelta/self.estimatedItemSize.height);
|
||||||
|
NSInteger startRow = startSection*6 + countDelta;
|
||||||
|
startRow;
|
||||||
|
});
|
||||||
|
|
||||||
|
NSInteger endRow = ({
|
||||||
|
NSInteger endRow;
|
||||||
|
CGFloat section = CGRectGetMaxY(rect)/self.collectionView.fs_height;
|
||||||
|
CGFloat remainder = FSCalendarMod(section, 1);
|
||||||
|
// https://stackoverflow.com/a/10335601/2398107
|
||||||
|
if (remainder <= MAX(100*FLT_EPSILON*ABS(remainder), FLT_MIN)) {
|
||||||
|
endRow = FSCalendarFloor(section)*6 - 1;
|
||||||
|
} else {
|
||||||
|
CGFloat heightDelta = FSCalendarMod(CGRectGetMaxY(rect), self.collectionView.fs_height)-self.sectionInsets.top;
|
||||||
|
heightDelta = MIN(MAX(0, heightDelta), self.collectionView.fs_height-self.sectionInsets.top);
|
||||||
|
NSInteger countDelta = FSCalendarCeil(heightDelta/self.estimatedItemSize.height);
|
||||||
|
endRow = FSCalendarFloor(section)*6 + countDelta-1;
|
||||||
|
}
|
||||||
|
endRow;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (NSInteger row = startRow; row <= endRow; row++) {
|
||||||
|
for (NSInteger column = 0; column < 7; column++) {
|
||||||
|
NSInteger section = row / 6;
|
||||||
|
NSInteger item = column + (row % 6) * 7;
|
||||||
|
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:section];
|
||||||
|
UICollectionViewLayoutAttributes *itemAttributes = [self layoutAttributesForItemAtIndexPath:indexPath];
|
||||||
|
[layoutAttributes addObject:itemAttributes];
|
||||||
|
|
||||||
|
UICollectionViewLayoutAttributes *rowSeparatorAttributes = [self layoutAttributesForDecorationViewOfKind:kFSCalendarSeparatorInterRows atIndexPath:indexPath];
|
||||||
|
if (rowSeparatorAttributes) {
|
||||||
|
[layoutAttributes addObject:rowSeparatorAttributes];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
NSInteger startSection = [self searchStartSection:rect :0 :self.numberOfSections-1];
|
||||||
|
NSInteger startRowIndex = ({
|
||||||
|
CGFloat heightDelta1 = MIN(self.sectionBottoms[startSection]-CGRectGetMinY(rect)-self.sectionInsets.bottom, self.sectionRowCounts[startSection]*self.estimatedItemSize.height);
|
||||||
|
NSInteger startRowCount = FSCalendarCeil(heightDelta1/self.estimatedItemSize.height);
|
||||||
|
NSInteger startRowIndex = self.sectionRowCounts[startSection]-startRowCount;
|
||||||
|
startRowIndex;
|
||||||
|
});
|
||||||
|
|
||||||
|
NSInteger endSection = [self searchEndSection:rect :startSection :self.numberOfSections-1];
|
||||||
|
NSInteger endRowIndex = ({
|
||||||
|
CGFloat heightDelta2 = MAX(CGRectGetMaxY(rect) - self.sectionTops[endSection]- self.headerReferenceSize.height - self.sectionInsets.top, 0);
|
||||||
|
NSInteger endRowCount = FSCalendarCeil(heightDelta2/self.estimatedItemSize.height);
|
||||||
|
NSInteger endRowIndex = endRowCount - 1;
|
||||||
|
endRowIndex;
|
||||||
|
});
|
||||||
|
for (NSInteger section = startSection; section <= endSection; section++) {
|
||||||
|
NSInteger startRow = (section == startSection) ? startRowIndex : 0;
|
||||||
|
NSInteger endRow = (section == endSection) ? endRowIndex : self.sectionRowCounts[section]-1;
|
||||||
|
UICollectionViewLayoutAttributes *headerAttributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
|
||||||
|
[layoutAttributes addObject:headerAttributes];
|
||||||
|
for (NSInteger row = startRow; row <= endRow; row++) {
|
||||||
|
for (NSInteger column = 0; column < 7; column++) {
|
||||||
|
NSInteger item = row * 7 + column;
|
||||||
|
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:section];
|
||||||
|
UICollectionViewLayoutAttributes *itemAttributes = [self layoutAttributesForItemAtIndexPath:indexPath];
|
||||||
|
[layoutAttributes addObject:itemAttributes];
|
||||||
|
UICollectionViewLayoutAttributes *rowSeparatorAttributes = [self layoutAttributesForDecorationViewOfKind:kFSCalendarSeparatorInterRows atIndexPath:indexPath];
|
||||||
|
if (rowSeparatorAttributes) {
|
||||||
|
[layoutAttributes addObject:rowSeparatorAttributes];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return [NSArray arrayWithArray:layoutAttributes];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Items
|
||||||
|
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||||
|
{
|
||||||
|
FSCalendarCoordinate coordinate = [self.calendar.calculator coordinateForIndexPath:indexPath];
|
||||||
|
NSInteger column = coordinate.column;
|
||||||
|
NSInteger row = coordinate.row;
|
||||||
|
UICollectionViewLayoutAttributes *attributes = self.itemAttributes[indexPath];
|
||||||
|
if (!attributes) {
|
||||||
|
attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
|
||||||
|
CGRect frame = ({
|
||||||
|
CGFloat x, y;
|
||||||
|
switch (self.scrollDirection) {
|
||||||
|
case UICollectionViewScrollDirectionHorizontal: {
|
||||||
|
x = self.lefts[column] + indexPath.section * self.collectionView.fs_width;
|
||||||
|
y = self.tops[row];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UICollectionViewScrollDirectionVertical: {
|
||||||
|
x = self.lefts[column];
|
||||||
|
if (!self.calendar.floatingMode) {
|
||||||
|
y = self.tops[row] + indexPath.section * self.collectionView.fs_height;
|
||||||
|
} else {
|
||||||
|
y = self.sectionTops[indexPath.section] + self.headerReferenceSize.height + self.tops[row];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
CGFloat width = self.widths[column];
|
||||||
|
CGFloat height = self.heights[row];
|
||||||
|
CGRect frame = CGRectMake(x, y, width, height);
|
||||||
|
frame;
|
||||||
|
});
|
||||||
|
attributes.frame = frame;
|
||||||
|
self.itemAttributes[indexPath] = attributes;
|
||||||
|
}
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section headers
|
||||||
|
- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
|
||||||
|
{
|
||||||
|
if ([elementKind isEqualToString:UICollectionElementKindSectionHeader]) {
|
||||||
|
UICollectionViewLayoutAttributes *attributes = self.headerAttributes[indexPath];
|
||||||
|
if (!attributes) {
|
||||||
|
attributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:indexPath];
|
||||||
|
attributes.frame = CGRectMake(0, self.sectionTops[indexPath.section], self.collectionView.fs_width, self.headerReferenceSize.height);
|
||||||
|
self.headerAttributes[indexPath] = attributes;
|
||||||
|
}
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Separators
|
||||||
|
- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
|
||||||
|
{
|
||||||
|
if ([elementKind isEqualToString:kFSCalendarSeparatorInterRows] && (self.separators & FSCalendarSeparatorInterRows)) {
|
||||||
|
UICollectionViewLayoutAttributes *attributes = self.rowSeparatorAttributes[indexPath];
|
||||||
|
if (!attributes) {
|
||||||
|
FSCalendarCoordinate coordinate = [self.calendar.calculator coordinateForIndexPath:indexPath];
|
||||||
|
if (coordinate.row >= [self.calendar.calculator numberOfRowsInSection:indexPath.section]-1) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
attributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:kFSCalendarSeparatorInterRows withIndexPath:indexPath];
|
||||||
|
CGFloat x, y;
|
||||||
|
if (!self.calendar.floatingMode) {
|
||||||
|
switch (self.scrollDirection) {
|
||||||
|
case UICollectionViewScrollDirectionHorizontal: {
|
||||||
|
x = self.lefts[coordinate.column] + indexPath.section * self.collectionView.fs_width;
|
||||||
|
y = self.tops[coordinate.row]+self.heights[coordinate.row];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UICollectionViewScrollDirectionVertical: {
|
||||||
|
x = 0;
|
||||||
|
y = self.tops[coordinate.row]+self.heights[coordinate.row] + indexPath.section * self.collectionView.fs_height;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x = 0;
|
||||||
|
y = self.sectionTops[indexPath.section] + self.headerReferenceSize.height + self.tops[coordinate.row] + self.heights[coordinate.row];
|
||||||
|
}
|
||||||
|
CGFloat width = self.collectionView.fs_width;
|
||||||
|
CGFloat height = FSCalendarStandardSeparatorThickness;
|
||||||
|
attributes.frame = CGRectMake(x, y, width, height);
|
||||||
|
attributes.zIndex = NSIntegerMax;
|
||||||
|
self.rowSeparatorAttributes[indexPath] = attributes;
|
||||||
|
}
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Notifications
|
||||||
|
|
||||||
|
- (void)didReceiveNotifications:(NSNotification *)notification
|
||||||
|
{
|
||||||
|
if ([notification.name isEqualToString:UIDeviceOrientationDidChangeNotification]) {
|
||||||
|
[self invalidateLayout];
|
||||||
|
}
|
||||||
|
if ([notification.name isEqualToString:UIApplicationDidReceiveMemoryWarningNotification]) {
|
||||||
|
[self.itemAttributes removeAllObjects];
|
||||||
|
[self.headerAttributes removeAllObjects];
|
||||||
|
[self.rowSeparatorAttributes removeAllObjects];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private properties
|
||||||
|
|
||||||
|
- (void)setScrollDirection:(UICollectionViewScrollDirection)scrollDirection
|
||||||
|
{
|
||||||
|
if (_scrollDirection != scrollDirection) {
|
||||||
|
_scrollDirection = scrollDirection;
|
||||||
|
self.collectionViewSize = CGSizeAutomatic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private functions
|
||||||
|
|
||||||
|
- (NSInteger)searchStartSection:(CGRect)rect :(NSInteger)left :(NSInteger)right
|
||||||
|
{
|
||||||
|
NSInteger mid = left + (right-left)/2;
|
||||||
|
CGFloat y = rect.origin.y;
|
||||||
|
CGFloat minY = self.sectionTops[mid];
|
||||||
|
CGFloat maxY = self.sectionBottoms[mid];
|
||||||
|
if (y >= minY && y < maxY) {
|
||||||
|
return mid;
|
||||||
|
} else if (y < minY) {
|
||||||
|
return [self searchStartSection:rect :left :mid];
|
||||||
|
} else {
|
||||||
|
return [self searchStartSection:rect :mid+1 :right];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)searchEndSection:(CGRect)rect :(NSInteger)left :(NSInteger)right
|
||||||
|
{
|
||||||
|
NSInteger mid = left + (right-left)/2;
|
||||||
|
CGFloat y = CGRectGetMaxY(rect);
|
||||||
|
CGFloat minY = self.sectionTops[mid];
|
||||||
|
CGFloat maxY = self.sectionBottoms[mid];
|
||||||
|
if (y > minY && y <= maxY) {
|
||||||
|
return mid;
|
||||||
|
} else if (y <= minY) {
|
||||||
|
return [self searchEndSection:rect :left :mid];
|
||||||
|
} else {
|
||||||
|
return [self searchEndSection:rect :mid+1 :right];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
#undef kFSCalendarSeparatorInterColumns
|
||||||
|
#undef kFSCalendarSeparatorInterRows
|
||||||
|
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
//
|
|
||||||
// FSCalendarConstane.h
|
|
||||||
// FSCalendar
|
|
||||||
//
|
|
||||||
// Created by dingwenchao on 8/28/15.
|
|
||||||
// Copyright © 2016 Wenchao Ding. All rights reserved.
|
|
||||||
//
|
|
||||||
// https://github.com/WenchaoD
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
#pragma mark - Constance
|
|
||||||
|
|
||||||
UIKIT_EXTERN CGFloat const FSCalendarStandardHeaderHeight;
|
|
||||||
UIKIT_EXTERN CGFloat const FSCalendarStandardWeekdayHeight;
|
|
||||||
UIKIT_EXTERN CGFloat const FSCalendarStandardMonthlyPageHeight;
|
|
||||||
UIKIT_EXTERN CGFloat const FSCalendarStandardWeeklyPageHeight;
|
|
||||||
UIKIT_EXTERN CGFloat const FSCalendarStandardCellDiameter;
|
|
||||||
UIKIT_EXTERN CGFloat const FSCalendarAutomaticDimension;
|
|
||||||
UIKIT_EXTERN CGFloat const FSCalendarDefaultBounceAnimationDuration;
|
|
||||||
UIKIT_EXTERN CGFloat const FSCalendarStandardRowHeight;
|
|
||||||
UIKIT_EXTERN CGFloat const FSCalendarStandardTitleTextSize;
|
|
||||||
UIKIT_EXTERN CGFloat const FSCalendarStandardSubtitleTextSize;
|
|
||||||
UIKIT_EXTERN CGFloat const FSCalendarStandardWeekdayTextSize;
|
|
||||||
UIKIT_EXTERN CGFloat const FSCalendarStandardHeaderTextSize;
|
|
||||||
UIKIT_EXTERN CGFloat const FSCalendarMaximumEventDotDiameter;
|
|
||||||
|
|
||||||
UIKIT_EXTERN NSInteger const FSCalendarDefaultHourComponent;
|
|
||||||
|
|
||||||
#define FSCalendarDeviceIsIPad [[UIDevice currentDevice].model hasPrefix:@"iPad"]
|
|
||||||
|
|
||||||
#define FSCalendarStandardSelectionColor FSColorRGBA(31,119,219,1.0)
|
|
||||||
#define FSCalendarStandardTodayColor FSColorRGBA(198,51,42 ,1.0)
|
|
||||||
#define FSCalendarStandardTitleTextColor FSColorRGBA(14,69,221 ,1.0)
|
|
||||||
#define FSCalendarStandardEventDotColor FSColorRGBA(31,119,219,0.75)
|
|
||||||
|
|
||||||
#define FSColorRGBA(r,g,b,a) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a]
|
|
||||||
|
|
||||||
#if CGFLOAT_IS_DOUBLE
|
|
||||||
#define FSCalendarFloor(c) floor(c)
|
|
||||||
#else
|
|
||||||
#define FSCalendarFloor(c) floorf(c)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#pragma mark - Deprecated
|
|
||||||
|
|
||||||
#define FSCalendarDeprecated(instead) DEPRECATED_MSG_ATTRIBUTE(" Use " # instead " instead")
|
|
||||||
|
|
||||||
FSCalendarDeprecated('FSCalendarCellShape')
|
|
||||||
typedef NS_ENUM(NSInteger, FSCalendarCellStyle) {
|
|
||||||
FSCalendarCellStyleCircle = 0,
|
|
||||||
FSCalendarCellStyleRectangle = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
FSCalendarDeprecated('FSCalendarScrollDirection')
|
|
||||||
typedef NS_ENUM(NSInteger, FSCalendarFlow) {
|
|
||||||
FSCalendarFlowVertical,
|
|
||||||
FSCalendarFlowHorizontal
|
|
||||||
};
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
//
|
||||||
|
// FSCalendarConstane.h
|
||||||
|
// FSCalendar
|
||||||
|
//
|
||||||
|
// Created by dingwenchao on 8/28/15.
|
||||||
|
// Copyright © 2016 Wenchao Ding. All rights reserved.
|
||||||
|
//
|
||||||
|
// https://github.com/WenchaoD
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#pragma mark - Constants
|
||||||
|
|
||||||
|
CG_EXTERN CGFloat const FSCalendarStandardHeaderHeight;
|
||||||
|
CG_EXTERN CGFloat const FSCalendarStandardWeekdayHeight;
|
||||||
|
CG_EXTERN CGFloat const FSCalendarStandardMonthlyPageHeight;
|
||||||
|
CG_EXTERN CGFloat const FSCalendarStandardWeeklyPageHeight;
|
||||||
|
CG_EXTERN CGFloat const FSCalendarStandardCellDiameter;
|
||||||
|
CG_EXTERN CGFloat const FSCalendarStandardSeparatorThickness;
|
||||||
|
CG_EXTERN CGFloat const FSCalendarAutomaticDimension;
|
||||||
|
CG_EXTERN CGFloat const FSCalendarDefaultBounceAnimationDuration;
|
||||||
|
CG_EXTERN CGFloat const FSCalendarStandardRowHeight;
|
||||||
|
CG_EXTERN CGFloat const FSCalendarStandardTitleTextSize;
|
||||||
|
CG_EXTERN CGFloat const FSCalendarStandardSubtitleTextSize;
|
||||||
|
CG_EXTERN CGFloat const FSCalendarStandardWeekdayTextSize;
|
||||||
|
CG_EXTERN CGFloat const FSCalendarStandardHeaderTextSize;
|
||||||
|
CG_EXTERN CGFloat const FSCalendarMaximumEventDotDiameter;
|
||||||
|
CG_EXTERN CGFloat const FSCalendarStandardScopeHandleHeight;
|
||||||
|
|
||||||
|
UIKIT_EXTERN NSInteger const FSCalendarDefaultHourComponent;
|
||||||
|
|
||||||
|
UIKIT_EXTERN NSString * const FSCalendarDefaultCellReuseIdentifier;
|
||||||
|
UIKIT_EXTERN NSString * const FSCalendarBlankCellReuseIdentifier;
|
||||||
|
UIKIT_EXTERN NSString * const FSCalendarInvalidArgumentsExceptionName;
|
||||||
|
|
||||||
|
CG_EXTERN CGPoint const CGPointInfinity;
|
||||||
|
CG_EXTERN CGSize const CGSizeAutomatic;
|
||||||
|
|
||||||
|
#if TARGET_INTERFACE_BUILDER
|
||||||
|
#define FSCalendarDeviceIsIPad NO
|
||||||
|
#else
|
||||||
|
#define FSCalendarDeviceIsIPad [[UIDevice currentDevice].model hasPrefix:@"iPad"]
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FSCalendarStandardSelectionColor FSColorRGBA(31,119,219,1.0)
|
||||||
|
#define FSCalendarStandardTodayColor FSColorRGBA(198,51,42 ,1.0)
|
||||||
|
#define FSCalendarStandardTitleTextColor FSColorRGBA(14,69,221 ,1.0)
|
||||||
|
#define FSCalendarStandardEventDotColor FSColorRGBA(31,119,219,0.75)
|
||||||
|
|
||||||
|
#define FSCalendarStandardLineColor [[UIColor lightGrayColor] colorWithAlphaComponent:0.30]
|
||||||
|
#define FSCalendarStandardSeparatorColor [[UIColor lightGrayColor] colorWithAlphaComponent:0.60]
|
||||||
|
#define FSCalendarStandardScopeHandleColor [[UIColor lightGrayColor] colorWithAlphaComponent:0.50]
|
||||||
|
|
||||||
|
#define FSColorRGBA(r,g,b,a) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a]
|
||||||
|
#define FSCalendarInAppExtension [[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"]
|
||||||
|
|
||||||
|
#if CGFLOAT_IS_DOUBLE
|
||||||
|
#define FSCalendarFloor(c) floor(c)
|
||||||
|
#define FSCalendarRound(c) round(c)
|
||||||
|
#define FSCalendarCeil(c) ceil(c)
|
||||||
|
#define FSCalendarMod(c1,c2) fmod(c1,c2)
|
||||||
|
#else
|
||||||
|
#define FSCalendarFloor(c) floorf(c)
|
||||||
|
#define FSCalendarRound(c) roundf(c)
|
||||||
|
#define FSCalendarCeil(c) ceilf(c)
|
||||||
|
#define FSCalendarMod(c1,c2) fmodf(c1,c2)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FSCalendarUseWeakSelf __weak __typeof__(self) FSCalendarWeakSelf = self;
|
||||||
|
#define FSCalendarUseStrongSelf __strong __typeof__(self) self = FSCalendarWeakSelf;
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark - Deprecated
|
||||||
|
|
||||||
|
#define FSCalendarDeprecated(instead) DEPRECATED_MSG_ATTRIBUTE(" Use " # instead " instead")
|
||||||
|
|
||||||
|
FSCalendarDeprecated('borderRadius')
|
||||||
|
typedef NS_ENUM(NSUInteger, FSCalendarCellShape) {
|
||||||
|
FSCalendarCellShapeCircle = 0,
|
||||||
|
FSCalendarCellShapeRectangle = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSUInteger, FSCalendarUnit) {
|
||||||
|
FSCalendarUnitMonth = NSCalendarUnitMonth,
|
||||||
|
FSCalendarUnitWeekOfYear = NSCalendarUnitWeekOfYear,
|
||||||
|
FSCalendarUnitDay = NSCalendarUnitDay
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void FSCalendarSliceCake(CGFloat cake, NSInteger count, CGFloat *pieces) {
|
||||||
|
CGFloat total = cake;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
NSInteger remains = count - i;
|
||||||
|
CGFloat piece = FSCalendarRound(total/remains*2)*0.5;
|
||||||
|
total -= piece;
|
||||||
|
pieces[i] = piece;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,20 +8,39 @@
|
||||||
// https://github.com/WenchaoD
|
// https://github.com/WenchaoD
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "FSCalendarConstance.h"
|
#import "FSCalendarConstants.h"
|
||||||
|
|
||||||
CGFloat const FSCalendarStandardHeaderHeight = 40;
|
CGFloat const FSCalendarStandardHeaderHeight = 40;
|
||||||
CGFloat const FSCalendarStandardWeekdayHeight = 25;
|
CGFloat const FSCalendarStandardWeekdayHeight = 25;
|
||||||
CGFloat const FSCalendarStandardMonthlyPageHeight = 300.0;
|
CGFloat const FSCalendarStandardMonthlyPageHeight = 300.0;
|
||||||
CGFloat const FSCalendarStandardWeeklyPageHeight = 108+1/3.0;
|
CGFloat const FSCalendarStandardWeeklyPageHeight = 108+1/3.0;
|
||||||
CGFloat const FSCalendarStandardCellDiameter = 100/3.0;
|
CGFloat const FSCalendarStandardCellDiameter = 100/3.0;
|
||||||
|
CGFloat const FSCalendarStandardSeparatorThickness = 0.5;
|
||||||
CGFloat const FSCalendarAutomaticDimension = -1;
|
CGFloat const FSCalendarAutomaticDimension = -1;
|
||||||
CGFloat const FSCalendarDefaultBounceAnimationDuration = 0.15;
|
CGFloat const FSCalendarDefaultBounceAnimationDuration = 0.15;
|
||||||
CGFloat const FSCalendarStandardRowHeight = 38+1.0/3;
|
CGFloat const FSCalendarStandardRowHeight = 38;
|
||||||
CGFloat const FSCalendarStandardTitleTextSize = 13.5;
|
CGFloat const FSCalendarStandardTitleTextSize = 13.5;
|
||||||
CGFloat const FSCalendarStandardSubtitleTextSize = 10;
|
CGFloat const FSCalendarStandardSubtitleTextSize = 10;
|
||||||
CGFloat const FSCalendarStandardWeekdayTextSize = 14;
|
CGFloat const FSCalendarStandardWeekdayTextSize = 14;
|
||||||
CGFloat const FSCalendarStandardHeaderTextSize = 16.5;
|
CGFloat const FSCalendarStandardHeaderTextSize = 16.5;
|
||||||
CGFloat const FSCalendarMaximumEventDotDiameter = 4.8;
|
CGFloat const FSCalendarMaximumEventDotDiameter = 4.8;
|
||||||
|
CGFloat const FSCalendarStandardScopeHandleHeight = 26;
|
||||||
|
|
||||||
|
NSInteger const FSCalendarDefaultHourComponent = 0;
|
||||||
|
|
||||||
|
NSString * const FSCalendarDefaultCellReuseIdentifier = @"_FSCalendarDefaultCellReuseIdentifier";
|
||||||
|
NSString * const FSCalendarBlankCellReuseIdentifier = @"_FSCalendarBlankCellReuseIdentifier";
|
||||||
|
NSString * const FSCalendarInvalidArgumentsExceptionName = @"Invalid argument exception";
|
||||||
|
|
||||||
|
CGPoint const CGPointInfinity = {
|
||||||
|
.x = CGFLOAT_MAX,
|
||||||
|
.y = CGFLOAT_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
CGSize const CGSizeAutomatic = {
|
||||||
|
.width = FSCalendarAutomaticDimension,
|
||||||
|
.height = FSCalendarAutomaticDimension
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
NSInteger const FSCalendarDefaultHourComponent = 0;
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
//
|
||||||
|
// FSCalendarDelegationFactory.h
|
||||||
|
// FSCalendar
|
||||||
|
//
|
||||||
|
// Created by dingwenchao on 19/12/2016.
|
||||||
|
// Copyright © 2016 wenchaoios. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "FSCalendarDelegationProxy.h"
|
||||||
|
|
||||||
|
@interface FSCalendarDelegationFactory : NSObject
|
||||||
|
|
||||||
|
+ (FSCalendarDelegationProxy *)dataSourceProxy;
|
||||||
|
+ (FSCalendarDelegationProxy *)delegateProxy;
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,40 @@
|
||||||
|
//
|
||||||
|
// FSCalendarDelegationFactory.m
|
||||||
|
// FSCalendar
|
||||||
|
//
|
||||||
|
// Created by dingwenchao on 19/12/2016.
|
||||||
|
// Copyright © 2016 wenchaoios. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "FSCalendarDelegationFactory.h"
|
||||||
|
|
||||||
|
#define FSCalendarSelectorEntry(SEL1,SEL2) NSStringFromSelector(@selector(SEL1)):NSStringFromSelector(@selector(SEL2))
|
||||||
|
|
||||||
|
@implementation FSCalendarDelegationFactory
|
||||||
|
|
||||||
|
+ (FSCalendarDelegationProxy *)dataSourceProxy
|
||||||
|
{
|
||||||
|
FSCalendarDelegationProxy *delegation = [[FSCalendarDelegationProxy alloc] init];
|
||||||
|
delegation.protocol = @protocol(FSCalendarDataSource);
|
||||||
|
delegation.deprecations = @{FSCalendarSelectorEntry(calendar:numberOfEventsForDate:, calendar:hasEventForDate:)};
|
||||||
|
return delegation;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (FSCalendarDelegationProxy *)delegateProxy
|
||||||
|
{
|
||||||
|
FSCalendarDelegationProxy *delegation = [[FSCalendarDelegationProxy alloc] init];
|
||||||
|
delegation.protocol = @protocol(FSCalendarDelegateAppearance);
|
||||||
|
delegation.deprecations = @{
|
||||||
|
FSCalendarSelectorEntry(calendarCurrentPageDidChange:, calendarCurrentMonthDidChange:),
|
||||||
|
FSCalendarSelectorEntry(calendar:shouldSelectDate:atMonthPosition:, calendar:shouldSelectDate:),
|
||||||
|
FSCalendarSelectorEntry(calendar:didSelectDate:atMonthPosition:, calendar:didSelectDate:),
|
||||||
|
FSCalendarSelectorEntry(calendar:shouldDeselectDate:atMonthPosition:, calendar:shouldDeselectDate:),
|
||||||
|
FSCalendarSelectorEntry(calendar:didDeselectDate:atMonthPosition:, calendar:didDeselectDate:)
|
||||||
|
};
|
||||||
|
return delegation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#undef FSCalendarSelectorEntry
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
//
|
||||||
|
// FSCalendarDelegationProxy.h
|
||||||
|
// FSCalendar
|
||||||
|
//
|
||||||
|
// Created by dingwenchao on 11/12/2016.
|
||||||
|
// Copyright © 2016 Wenchao Ding. All rights reserved.
|
||||||
|
//
|
||||||
|
// https://github.com/WenchaoD
|
||||||
|
//
|
||||||
|
// 1. Smart proxy delegation http://petersteinberger.com/blog/2013/smart-proxy-delegation/
|
||||||
|
// 2. Manage deprecated delegation functions
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "FSCalendar.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface FSCalendarDelegationProxy : NSProxy
|
||||||
|
|
||||||
|
@property (weak , nonatomic) id delegation;
|
||||||
|
@property (strong, nonatomic) Protocol *protocol;
|
||||||
|
@property (strong, nonatomic) NSDictionary<NSString *,NSString *> *deprecations;
|
||||||
|
|
||||||
|
- (instancetype)init;
|
||||||
|
- (SEL)deprecatedSelectorOfSelector:(SEL)selector;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
//
|
||||||
|
// FSCalendarDelegationProxy.m
|
||||||
|
// FSCalendar
|
||||||
|
//
|
||||||
|
// Created by dingwenchao on 11/12/2016.
|
||||||
|
// Copyright © 2016 Wenchao Ding. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "FSCalendarDelegationProxy.h"
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
|
||||||
|
@implementation FSCalendarDelegationProxy
|
||||||
|
|
||||||
|
- (instancetype)init
|
||||||
|
{
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)respondsToSelector:(SEL)selector
|
||||||
|
{
|
||||||
|
BOOL responds = [self.delegation respondsToSelector:selector];
|
||||||
|
if (!responds) responds = [self.delegation respondsToSelector:[self deprecatedSelectorOfSelector:selector]];
|
||||||
|
if (!responds) responds = [super respondsToSelector:selector];
|
||||||
|
return responds;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)conformsToProtocol:(Protocol *)protocol
|
||||||
|
{
|
||||||
|
return [self.delegation conformsToProtocol:protocol];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)forwardInvocation:(NSInvocation *)invocation
|
||||||
|
{
|
||||||
|
SEL selector = invocation.selector;
|
||||||
|
if (![self.delegation respondsToSelector:selector]) {
|
||||||
|
selector = [self deprecatedSelectorOfSelector:selector];
|
||||||
|
invocation.selector = selector;
|
||||||
|
}
|
||||||
|
if ([self.delegation respondsToSelector:selector]) {
|
||||||
|
[invocation invokeWithTarget:self.delegation];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
|
||||||
|
{
|
||||||
|
if ([self.delegation respondsToSelector:sel]) {
|
||||||
|
return [(NSObject *)self.delegation methodSignatureForSelector:sel];
|
||||||
|
}
|
||||||
|
SEL selector = [self deprecatedSelectorOfSelector:sel];
|
||||||
|
if ([self.delegation respondsToSelector:selector]) {
|
||||||
|
return [(NSObject *)self.delegation methodSignatureForSelector:selector];
|
||||||
|
}
|
||||||
|
#if TARGET_INTERFACE_BUILDER
|
||||||
|
return [NSObject methodSignatureForSelector:@selector(init)];
|
||||||
|
#endif
|
||||||
|
struct objc_method_description desc = protocol_getMethodDescription(self.protocol, sel, NO, YES);
|
||||||
|
const char *types = desc.types;
|
||||||
|
return types?[NSMethodSignature signatureWithObjCTypes:types]:[NSObject methodSignatureForSelector:@selector(init)];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (SEL)deprecatedSelectorOfSelector:(SEL)selector
|
||||||
|
{
|
||||||
|
NSString *selectorString = NSStringFromSelector(selector);
|
||||||
|
selectorString = self.deprecations[selectorString];
|
||||||
|
return NSSelectorFromString(selectorString);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -13,47 +13,43 @@
|
||||||
|
|
||||||
#import "FSCalendar.h"
|
#import "FSCalendar.h"
|
||||||
#import "FSCalendarCell.h"
|
#import "FSCalendarCell.h"
|
||||||
#import "FSCalendarHeader.h"
|
#import "FSCalendarHeaderView.h"
|
||||||
#import "FSCalendarStickyHeader.h"
|
#import "FSCalendarStickyHeader.h"
|
||||||
|
#import "FSCalendarCollectionView.h"
|
||||||
|
#import "FSCalendarCollectionViewLayout.h"
|
||||||
|
#import "FSCalendarScopeHandle.h"
|
||||||
|
#import "FSCalendarCalculator.h"
|
||||||
|
#import "FSCalendarTransitionCoordinator.h"
|
||||||
|
#import "FSCalendarDelegationProxy.h"
|
||||||
|
|
||||||
@interface FSCalendar (Dynamic)
|
@interface FSCalendar (Dynamic)
|
||||||
|
|
||||||
@property (readonly, nonatomic) CAShapeLayer *maskLayer;
|
@property (readonly, nonatomic) FSCalendarCollectionView *collectionView;
|
||||||
@property (readonly, nonatomic) FSCalendarHeader *header;
|
@property (readonly, nonatomic) FSCalendarScopeHandle *scopeHandle;
|
||||||
@property (readonly, nonatomic) UICollectionView *collectionView;
|
@property (readonly, nonatomic) FSCalendarCollectionViewLayout *collectionViewLayout;
|
||||||
@property (readonly, nonatomic) UICollectionViewFlowLayout *collectionViewLayout;
|
@property (readonly, nonatomic) FSCalendarTransitionCoordinator *transitionCoordinator;
|
||||||
@property (readonly, nonatomic) NSArray *weekdays;
|
@property (readonly, nonatomic) FSCalendarCalculator *calculator;
|
||||||
@property (readonly, nonatomic) BOOL ibEditing;
|
|
||||||
@property (readonly, nonatomic) BOOL floatingMode;
|
@property (readonly, nonatomic) BOOL floatingMode;
|
||||||
@property (readonly, nonatomic) NSArray *visibleStickyHeaders;
|
@property (readonly, nonatomic) NSArray *visibleStickyHeaders;
|
||||||
@property (readonly, nonatomic) CGFloat preferredHeaderHeight;
|
@property (readonly, nonatomic) CGFloat preferredHeaderHeight;
|
||||||
@property (readonly, nonatomic) CGFloat preferredWeekdayHeight;
|
@property (readonly, nonatomic) CGFloat preferredWeekdayHeight;
|
||||||
@property (readonly, nonatomic) CGFloat preferredRowHeight;
|
|
||||||
@property (readonly, nonatomic) UIView *bottomBorder;
|
@property (readonly, nonatomic) UIView *bottomBorder;
|
||||||
|
|
||||||
@property (readonly, nonatomic) NSCalendar *calendar;
|
@property (readonly, nonatomic) NSCalendar *gregorian;
|
||||||
@property (readonly, nonatomic) NSDateComponents *components;
|
@property (readonly, nonatomic) NSDateComponents *components;
|
||||||
@property (readonly, nonatomic) NSDateFormatter *formatter;
|
@property (readonly, nonatomic) NSDateFormatter *formatter;
|
||||||
|
|
||||||
@property (readonly, nonatomic) UIView *contentView;
|
@property (readonly, nonatomic) UIView *contentView;
|
||||||
@property (readonly, nonatomic) UIView *daysContainer;
|
@property (readonly, nonatomic) UIView *daysContainer;
|
||||||
|
|
||||||
@property (assign, nonatomic) BOOL needsAdjustingMonthPosition;
|
|
||||||
@property (assign, nonatomic) BOOL needsAdjustingViewFrame;
|
@property (assign, nonatomic) BOOL needsAdjustingViewFrame;
|
||||||
|
|
||||||
- (void)invalidateWeekdayFont;
|
|
||||||
- (void)invalidateWeekdayTextColor;
|
|
||||||
|
|
||||||
- (void)invalidateHeaders;
|
- (void)invalidateHeaders;
|
||||||
- (void)invalidateWeekdaySymbols;
|
- (void)adjustMonthPosition;
|
||||||
- (void)invalidateAppearanceForCell:(FSCalendarCell *)cell;
|
- (void)configureAppearance;
|
||||||
|
|
||||||
- (NSDate *)dateForIndexPath:(NSIndexPath *)indexPath;
|
- (BOOL)isPageInRange:(NSDate *)page;
|
||||||
- (NSDate *)dateForIndexPath:(NSIndexPath *)indexPath scope:(FSCalendarScope)scope;
|
- (BOOL)isDateInRange:(NSDate *)date;
|
||||||
- (NSIndexPath *)indexPathForDate:(NSDate *)date;
|
|
||||||
- (NSIndexPath *)indexPathForDate:(NSDate *)date scope:(FSCalendarScope)scope;
|
|
||||||
|
|
||||||
- (NSInteger)numberOfHeadPlaceholdersForMonth:(NSDate *)month;
|
|
||||||
|
|
||||||
- (CGSize)sizeThatFits:(CGSize)size scope:(FSCalendarScope)scope;
|
- (CGSize)sizeThatFits:(CGSize)size scope:(FSCalendarScope)scope;
|
||||||
|
|
||||||
|
@ -68,21 +64,21 @@
|
||||||
@property (readonly, nonatomic) NSDictionary *subtitleColors;
|
@property (readonly, nonatomic) NSDictionary *subtitleColors;
|
||||||
@property (readonly, nonatomic) NSDictionary *borderColors;
|
@property (readonly, nonatomic) NSDictionary *borderColors;
|
||||||
|
|
||||||
@property (readonly, nonatomic) UIFont *preferredTitleFont;
|
@end
|
||||||
@property (readonly, nonatomic) UIFont *preferredSubtitleFont;
|
|
||||||
@property (readonly, nonatomic) UIFont *preferredWeekdayFont;
|
|
||||||
@property (readonly, nonatomic) UIFont *preferredHeaderTitleFont;
|
|
||||||
|
|
||||||
- (void)adjustTitleIfNecessary;
|
@interface FSCalendarWeekdayView (Dynamic)
|
||||||
- (void)invalidateFonts;
|
|
||||||
|
@property (readwrite, nonatomic) FSCalendar *calendar;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@interface FSCalendarCollectionViewLayout (Dynamic)
|
||||||
|
|
||||||
@interface FSCalendarHeader (Dynamic)
|
@property (readonly, nonatomic) CGSize estimatedItemSize;
|
||||||
|
|
||||||
@property (readonly, nonatomic) UICollectionView *collectionView;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@interface FSCalendarDelegationProxy()<FSCalendarDataSource,FSCalendarDelegate,FSCalendarDelegateAppearance>
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
//
|
|
||||||
// FSCalendarEventView.h
|
|
||||||
// FSCalendar
|
|
||||||
//
|
|
||||||
// Created by dingwenchao on 2/3/16.
|
|
||||||
// Copyright © 2016 wenchaoios. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
|
|
||||||
@interface FSCalendarEventIndicator : UIView
|
|
||||||
|
|
||||||
@property (assign, nonatomic) NSInteger numberOfEvents;
|
|
||||||
@property (strong, nonatomic) id color;
|
|
||||||
@property (assign, nonatomic) BOOL needsAdjustingViewFrame;
|
|
||||||
|
|
||||||
@end
|
|
|
@ -1,143 +0,0 @@
|
||||||
//
|
|
||||||
// FSCalendarEventView.m
|
|
||||||
// FSCalendar
|
|
||||||
//
|
|
||||||
// Created by dingwenchao on 2/3/16.
|
|
||||||
// Copyright © 2016 wenchaoios. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "FSCalendarEventIndicator.h"
|
|
||||||
#import "FSCalendarConstance.h"
|
|
||||||
#import "UIView+FSExtension.h"
|
|
||||||
#import "CALayer+FSExtension.h"
|
|
||||||
|
|
||||||
@interface FSCalendarEventIndicator ()
|
|
||||||
|
|
||||||
@property (weak, nonatomic) UIView *contentView;
|
|
||||||
|
|
||||||
@property (strong, nonatomic) NSMutableArray *eventLayers;
|
|
||||||
@property (assign, nonatomic) BOOL needsInvalidatingColor;
|
|
||||||
|
|
||||||
- (UIImage *)dotImageWithColor:(UIColor *)color diameter:(CGFloat)diameter;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation FSCalendarEventIndicator
|
|
||||||
|
|
||||||
- (instancetype)initWithFrame:(CGRect)frame
|
|
||||||
{
|
|
||||||
self = [super initWithFrame:frame];
|
|
||||||
if (self) {
|
|
||||||
|
|
||||||
UIView *view = [[UIView alloc] initWithFrame:CGRectZero];
|
|
||||||
[self addSubview:view];
|
|
||||||
self.contentView = view;
|
|
||||||
|
|
||||||
self.eventLayers = [NSMutableArray arrayWithCapacity:3];
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
CALayer *layer = [CALayer layer];
|
|
||||||
layer.masksToBounds = YES;
|
|
||||||
layer.backgroundColor = FSCalendarStandardEventDotColor.CGColor;
|
|
||||||
[self.eventLayers addObject:layer];
|
|
||||||
[self.contentView.layer addSublayer:layer];
|
|
||||||
}
|
|
||||||
|
|
||||||
_needsInvalidatingColor = YES;
|
|
||||||
_needsAdjustingViewFrame = YES;
|
|
||||||
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)layoutSubviews
|
|
||||||
{
|
|
||||||
[super layoutSubviews];
|
|
||||||
if (_needsAdjustingViewFrame) {
|
|
||||||
CGFloat diameter = MIN(MIN(self.fs_width, self.fs_height),FSCalendarMaximumEventDotDiameter);
|
|
||||||
self.contentView.fs_height = self.fs_height;
|
|
||||||
self.contentView.fs_width = (self.numberOfEvents*2-1)*diameter;
|
|
||||||
self.contentView.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)layoutSublayersOfLayer:(CALayer *)layer
|
|
||||||
{
|
|
||||||
[super layoutSublayersOfLayer:layer];
|
|
||||||
if (layer == self.layer) {
|
|
||||||
if (_needsAdjustingViewFrame) {
|
|
||||||
_needsAdjustingViewFrame = NO;
|
|
||||||
CGFloat diameter = MIN(MIN(self.fs_width, self.fs_height),FSCalendarMaximumEventDotDiameter);
|
|
||||||
for (int i = 0; i < self.eventLayers.count; i++) {
|
|
||||||
CALayer *layer = self.eventLayers[i];
|
|
||||||
layer.hidden = i >= self.numberOfEvents;
|
|
||||||
if (!layer.hidden) {
|
|
||||||
layer.frame = CGRectMake(2*i*diameter, (self.fs_height-diameter)*0.5, diameter, diameter);
|
|
||||||
layer.cornerRadius = diameter * 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_needsInvalidatingColor) {
|
|
||||||
_needsInvalidatingColor = NO;
|
|
||||||
CGFloat diameter = MIN(MIN(self.fs_width, self.fs_height),FSCalendarMaximumEventDotDiameter);
|
|
||||||
if ([_color isKindOfClass:[UIColor class]]) {
|
|
||||||
UIImage *dotImage = [self dotImageWithColor:_color diameter:diameter];
|
|
||||||
[self.eventLayers makeObjectsPerformSelector:@selector(setContents:) withObject:(id)dotImage.CGImage];
|
|
||||||
} else if ([_color isKindOfClass:[NSArray class]]) {
|
|
||||||
NSArray *colors = (NSArray *)_color;
|
|
||||||
if (colors.count) {
|
|
||||||
UIColor *lastColor = colors.firstObject;
|
|
||||||
for (int i = 0; i < self.numberOfEvents; i++) {
|
|
||||||
if (i < colors.count) {
|
|
||||||
lastColor = colors[i];
|
|
||||||
}
|
|
||||||
CALayer *layer = self.eventLayers[i];
|
|
||||||
UIImage *dotImage = [self dotImageWithColor:lastColor diameter:diameter];
|
|
||||||
layer.contents = (id)dotImage.CGImage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setColor:(id)color
|
|
||||||
{
|
|
||||||
if (![_color isEqual:color]) {
|
|
||||||
_color = color;
|
|
||||||
_needsInvalidatingColor = YES;
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setNumberOfEvents:(NSInteger)numberOfEvents
|
|
||||||
{
|
|
||||||
if (_numberOfEvents != numberOfEvents) {
|
|
||||||
_numberOfEvents = MIN(MAX(numberOfEvents,0),3);
|
|
||||||
_needsAdjustingViewFrame = YES;
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setNeedsAdjustingViewFrame:(BOOL)needsAdjustingViewFrame
|
|
||||||
{
|
|
||||||
if (_needsAdjustingViewFrame != needsAdjustingViewFrame) {
|
|
||||||
_needsAdjustingViewFrame = needsAdjustingViewFrame;
|
|
||||||
if (needsAdjustingViewFrame) {
|
|
||||||
[self setNeedsLayout];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIImage *)dotImageWithColor:(UIColor *)color diameter:(CGFloat)diameter
|
|
||||||
{
|
|
||||||
CGRect bounds = CGRectMake(0, 0, diameter, diameter);
|
|
||||||
UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0);
|
|
||||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
|
||||||
CGContextSetFillColorWithColor(context, color.CGColor);
|
|
||||||
CGContextFillEllipseInRect(context, bounds);
|
|
||||||
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
|
|
||||||
UIGraphicsEndImageContext();
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
//
|
||||||
|
// FSCalendarExtensions.h
|
||||||
|
// FSCalendar
|
||||||
|
//
|
||||||
|
// Created by dingwenchao on 10/8/16.
|
||||||
|
// Copyright © 2016 Wenchao Ding. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface UIView (FSCalendarExtensions)
|
||||||
|
|
||||||
|
@property (nonatomic) CGFloat fs_width;
|
||||||
|
@property (nonatomic) CGFloat fs_height;
|
||||||
|
|
||||||
|
@property (nonatomic) CGFloat fs_top;
|
||||||
|
@property (nonatomic) CGFloat fs_left;
|
||||||
|
@property (nonatomic) CGFloat fs_bottom;
|
||||||
|
@property (nonatomic) CGFloat fs_right;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@interface CALayer (FSCalendarExtensions)
|
||||||
|
|
||||||
|
@property (nonatomic) CGFloat fs_width;
|
||||||
|
@property (nonatomic) CGFloat fs_height;
|
||||||
|
|
||||||
|
@property (nonatomic) CGFloat fs_top;
|
||||||
|
@property (nonatomic) CGFloat fs_left;
|
||||||
|
@property (nonatomic) CGFloat fs_bottom;
|
||||||
|
@property (nonatomic) CGFloat fs_right;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@interface NSCalendar (FSCalendarExtensions)
|
||||||
|
|
||||||
|
- (nullable NSDate *)fs_firstDayOfMonth:(NSDate *)month;
|
||||||
|
- (nullable NSDate *)fs_lastDayOfMonth:(NSDate *)month;
|
||||||
|
- (nullable NSDate *)fs_firstDayOfWeek:(NSDate *)week;
|
||||||
|
- (nullable NSDate *)fs_lastDayOfWeek:(NSDate *)week;
|
||||||
|
- (nullable NSDate *)fs_middleDayOfWeek:(NSDate *)week;
|
||||||
|
- (NSInteger)fs_numberOfDaysInMonth:(NSDate *)month;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface NSMapTable (FSCalendarExtensions)
|
||||||
|
|
||||||
|
- (void)setObject:(nullable id)obj forKeyedSubscript:(id<NSCopying>)key;
|
||||||
|
- (id)objectForKeyedSubscript:(id<NSCopying>)key;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface NSCache (FSCalendarExtensions)
|
||||||
|
|
||||||
|
- (void)setObject:(nullable id)obj forKeyedSubscript:(id<NSCopying>)key;
|
||||||
|
- (id)objectForKeyedSubscript:(id<NSCopying>)key;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@interface NSObject (FSCalendarExtensions)
|
||||||
|
|
||||||
|
#define IVAR_DEF(SET,GET,TYPE) \
|
||||||
|
- (void)fs_set##SET##Variable:(TYPE)value forKey:(NSString *)key; \
|
||||||
|
- (TYPE)fs_##GET##VariableForKey:(NSString *)key;
|
||||||
|
IVAR_DEF(Bool, bool, BOOL)
|
||||||
|
IVAR_DEF(Float, float, CGFloat)
|
||||||
|
IVAR_DEF(Integer, integer, NSInteger)
|
||||||
|
IVAR_DEF(UnsignedInteger, unsignedInteger, NSUInteger)
|
||||||
|
#undef IVAR_DEF
|
||||||
|
|
||||||
|
- (void)fs_setVariable:(id)variable forKey:(NSString *)key;
|
||||||
|
- (id)fs_variableForKey:(NSString *)key;
|
||||||
|
|
||||||
|
- (nullable id)fs_performSelector:(SEL)selector withObjects:(nullable id)firstObject, ... NS_REQUIRES_NIL_TERMINATION;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,440 @@
|
||||||
|
//
|
||||||
|
// FSCalendarExtensions.m
|
||||||
|
// FSCalendar
|
||||||
|
//
|
||||||
|
// Created by dingwenchao on 10/8/16.
|
||||||
|
// Copyright © 2016 Wenchao Ding. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "FSCalendarExtensions.h"
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
|
||||||
|
@implementation UIView (FSCalendarExtensions)
|
||||||
|
|
||||||
|
- (CGFloat)fs_width
|
||||||
|
{
|
||||||
|
return CGRectGetWidth(self.frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setFs_width:(CGFloat)fs_width
|
||||||
|
{
|
||||||
|
self.frame = CGRectMake(self.fs_left, self.fs_top, fs_width, self.fs_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)fs_height
|
||||||
|
{
|
||||||
|
return CGRectGetHeight(self.frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setFs_height:(CGFloat)fs_height
|
||||||
|
{
|
||||||
|
self.frame = CGRectMake(self.fs_left, self.fs_top, self.fs_width, fs_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)fs_top
|
||||||
|
{
|
||||||
|
return CGRectGetMinY(self.frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setFs_top:(CGFloat)fs_top
|
||||||
|
{
|
||||||
|
self.frame = CGRectMake(self.fs_left, fs_top, self.fs_width, self.fs_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)fs_bottom
|
||||||
|
{
|
||||||
|
return CGRectGetMaxY(self.frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setFs_bottom:(CGFloat)fs_bottom
|
||||||
|
{
|
||||||
|
self.fs_top = fs_bottom - self.fs_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)fs_left
|
||||||
|
{
|
||||||
|
return CGRectGetMinX(self.frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setFs_left:(CGFloat)fs_left
|
||||||
|
{
|
||||||
|
self.frame = CGRectMake(fs_left, self.fs_top, self.fs_width, self.fs_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)fs_right
|
||||||
|
{
|
||||||
|
return CGRectGetMaxX(self.frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setFs_right:(CGFloat)fs_right
|
||||||
|
{
|
||||||
|
self.fs_left = self.fs_right - self.fs_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@implementation CALayer (FSCalendarExtensions)
|
||||||
|
|
||||||
|
- (CGFloat)fs_width
|
||||||
|
{
|
||||||
|
return CGRectGetWidth(self.frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setFs_width:(CGFloat)fs_width
|
||||||
|
{
|
||||||
|
self.frame = CGRectMake(self.fs_left, self.fs_top, fs_width, self.fs_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)fs_height
|
||||||
|
{
|
||||||
|
return CGRectGetHeight(self.frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setFs_height:(CGFloat)fs_height
|
||||||
|
{
|
||||||
|
self.frame = CGRectMake(self.fs_left, self.fs_top, self.fs_width, fs_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)fs_top
|
||||||
|
{
|
||||||
|
return CGRectGetMinY(self.frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setFs_top:(CGFloat)fs_top
|
||||||
|
{
|
||||||
|
self.frame = CGRectMake(self.fs_left, fs_top, self.fs_width, self.fs_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)fs_bottom
|
||||||
|
{
|
||||||
|
return CGRectGetMaxY(self.frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setFs_bottom:(CGFloat)fs_bottom
|
||||||
|
{
|
||||||
|
self.fs_top = fs_bottom - self.fs_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)fs_left
|
||||||
|
{
|
||||||
|
return CGRectGetMinX(self.frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setFs_left:(CGFloat)fs_left
|
||||||
|
{
|
||||||
|
self.frame = CGRectMake(fs_left, self.fs_top, self.fs_width, self.fs_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)fs_right
|
||||||
|
{
|
||||||
|
return CGRectGetMaxX(self.frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setFs_right:(CGFloat)fs_right
|
||||||
|
{
|
||||||
|
self.fs_left = self.fs_right - self.fs_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface NSCalendar (FSCalendarExtensionsPrivate)
|
||||||
|
|
||||||
|
@property (readonly, nonatomic) NSDateComponents *fs_privateComponents;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation NSCalendar (FSCalendarExtensions)
|
||||||
|
|
||||||
|
- (nullable NSDate *)fs_firstDayOfMonth:(NSDate *)month
|
||||||
|
{
|
||||||
|
if (!month) return nil;
|
||||||
|
NSDateComponents *components = [self components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour fromDate:month];
|
||||||
|
components.day = 1;
|
||||||
|
return [self dateFromComponents:components];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable NSDate *)fs_lastDayOfMonth:(NSDate *)month
|
||||||
|
{
|
||||||
|
if (!month) return nil;
|
||||||
|
NSDateComponents *components = [self components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour fromDate:month];
|
||||||
|
components.month++;
|
||||||
|
components.day = 0;
|
||||||
|
return [self dateFromComponents:components];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable NSDate *)fs_firstDayOfWeek:(NSDate *)week
|
||||||
|
{
|
||||||
|
if (!week) return nil;
|
||||||
|
NSDateComponents *weekdayComponents = [self components:NSCalendarUnitWeekday fromDate:week];
|
||||||
|
NSDateComponents *components = self.fs_privateComponents;
|
||||||
|
components.day = - (weekdayComponents.weekday - self.firstWeekday);
|
||||||
|
components.day = (components.day-7) % 7;
|
||||||
|
NSDate *firstDayOfWeek = [self dateByAddingComponents:components toDate:week options:0];
|
||||||
|
firstDayOfWeek = [self dateBySettingHour:0 minute:0 second:0 ofDate:firstDayOfWeek options:0];
|
||||||
|
components.day = NSIntegerMax;
|
||||||
|
return firstDayOfWeek;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable NSDate *)fs_lastDayOfWeek:(NSDate *)week
|
||||||
|
{
|
||||||
|
if (!week) return nil;
|
||||||
|
NSDateComponents *weekdayComponents = [self components:NSCalendarUnitWeekday fromDate:week];
|
||||||
|
NSDateComponents *components = self.fs_privateComponents;
|
||||||
|
components.day = - (weekdayComponents.weekday - self.firstWeekday);
|
||||||
|
components.day = (components.day-7) % 7 + 6;
|
||||||
|
NSDate *lastDayOfWeek = [self dateByAddingComponents:components toDate:week options:0];
|
||||||
|
lastDayOfWeek = [self dateBySettingHour:0 minute:0 second:0 ofDate:lastDayOfWeek options:0];
|
||||||
|
components.day = NSIntegerMax;
|
||||||
|
return lastDayOfWeek;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable NSDate *)fs_middleDayOfWeek:(NSDate *)week
|
||||||
|
{
|
||||||
|
if (!week) return nil;
|
||||||
|
NSDateComponents *weekdayComponents = [self components:NSCalendarUnitWeekday fromDate:week];
|
||||||
|
NSDateComponents *componentsToSubtract = self.fs_privateComponents;
|
||||||
|
componentsToSubtract.day = - (weekdayComponents.weekday - self.firstWeekday) + 3;
|
||||||
|
NSDate *middleDayOfWeek = [self dateByAddingComponents:componentsToSubtract toDate:week options:0];
|
||||||
|
NSDateComponents *components = [self components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour fromDate:middleDayOfWeek];
|
||||||
|
middleDayOfWeek = [self dateFromComponents:components];
|
||||||
|
componentsToSubtract.day = NSIntegerMax;
|
||||||
|
return middleDayOfWeek;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSInteger)fs_numberOfDaysInMonth:(NSDate *)month
|
||||||
|
{
|
||||||
|
if (!month) return 0;
|
||||||
|
NSRange days = [self rangeOfUnit:NSCalendarUnitDay
|
||||||
|
inUnit:NSCalendarUnitMonth
|
||||||
|
forDate:month];
|
||||||
|
return days.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDateComponents *)fs_privateComponents
|
||||||
|
{
|
||||||
|
NSDateComponents *components = objc_getAssociatedObject(self, _cmd);
|
||||||
|
if (!components) {
|
||||||
|
components = [[NSDateComponents alloc] init];
|
||||||
|
objc_setAssociatedObject(self, _cmd, components, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||||
|
}
|
||||||
|
return components;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation NSMapTable (FSCalendarExtensions)
|
||||||
|
|
||||||
|
- (void)setObject:(nullable id)obj forKeyedSubscript:(id<NSCopying>)key
|
||||||
|
{
|
||||||
|
if (!key) return;
|
||||||
|
|
||||||
|
if (obj) {
|
||||||
|
[self setObject:obj forKey:key];
|
||||||
|
} else {
|
||||||
|
[self removeObjectForKey:key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)objectForKeyedSubscript:(id<NSCopying>)key
|
||||||
|
{
|
||||||
|
return [self objectForKey:key];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation NSCache (FSCalendarExtensions)
|
||||||
|
|
||||||
|
- (void)setObject:(nullable id)obj forKeyedSubscript:(id<NSCopying>)key
|
||||||
|
{
|
||||||
|
if (!key) return;
|
||||||
|
|
||||||
|
if (obj) {
|
||||||
|
[self setObject:obj forKey:key];
|
||||||
|
} else {
|
||||||
|
[self removeObjectForKey:key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)objectForKeyedSubscript:(id<NSCopying>)key
|
||||||
|
{
|
||||||
|
return [self objectForKey:key];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation NSObject (FSCalendarExtensions)
|
||||||
|
|
||||||
|
#define IVAR_IMP(SET,GET,TYPE) \
|
||||||
|
- (void)fs_set##SET##Variable:(TYPE)value forKey:(NSString *)key \
|
||||||
|
{ \
|
||||||
|
Ivar ivar = class_getInstanceVariable([self class], key.UTF8String); \
|
||||||
|
((void (*)(id, Ivar, TYPE))object_setIvar)(self, ivar, value); \
|
||||||
|
} \
|
||||||
|
- (TYPE)fs_##GET##VariableForKey:(NSString *)key \
|
||||||
|
{ \
|
||||||
|
Ivar ivar = class_getInstanceVariable([self class], key.UTF8String); \
|
||||||
|
ptrdiff_t offset = ivar_getOffset(ivar); \
|
||||||
|
unsigned char *bytes = (unsigned char *)(__bridge void *)self; \
|
||||||
|
TYPE value = *((TYPE *)(bytes+offset)); \
|
||||||
|
return value; \
|
||||||
|
}
|
||||||
|
IVAR_IMP(Bool,bool,BOOL)
|
||||||
|
IVAR_IMP(Float,float,CGFloat)
|
||||||
|
IVAR_IMP(Integer,integer,NSInteger)
|
||||||
|
IVAR_IMP(UnsignedInteger,unsignedInteger,NSUInteger)
|
||||||
|
#undef IVAR_IMP
|
||||||
|
|
||||||
|
- (void)fs_setVariable:(id)variable forKey:(NSString *)key
|
||||||
|
{
|
||||||
|
Ivar ivar = class_getInstanceVariable(self.class, key.UTF8String);
|
||||||
|
object_setIvar(self, ivar, variable);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)fs_variableForKey:(NSString *)key
|
||||||
|
{
|
||||||
|
Ivar ivar = class_getInstanceVariable(self.class, key.UTF8String);
|
||||||
|
id variable = object_getIvar(self, ivar);
|
||||||
|
return variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)fs_performSelector:(SEL)selector withObjects:(nullable id)firstObject, ...
|
||||||
|
{
|
||||||
|
if (!selector) return nil;
|
||||||
|
NSMethodSignature *signature = [self methodSignatureForSelector:selector];
|
||||||
|
if (!signature) return nil;
|
||||||
|
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
||||||
|
if (!invocation) return nil;
|
||||||
|
invocation.target = self;
|
||||||
|
invocation.selector = selector;
|
||||||
|
|
||||||
|
// Parameters
|
||||||
|
if (firstObject) {
|
||||||
|
int index = 2;
|
||||||
|
va_list args;
|
||||||
|
va_start(args, firstObject);
|
||||||
|
if (firstObject) {
|
||||||
|
id obj = firstObject;
|
||||||
|
do {
|
||||||
|
const char *argType = [signature getArgumentTypeAtIndex:index];
|
||||||
|
if(!strcmp(argType, @encode(id))){
|
||||||
|
// object
|
||||||
|
[invocation setArgument:&obj atIndex:index++];
|
||||||
|
} else {
|
||||||
|
NSString *argTypeString = [NSString stringWithUTF8String:argType];
|
||||||
|
if ([argTypeString hasPrefix:@"{"] && [argTypeString hasSuffix:@"}"]) {
|
||||||
|
// struct
|
||||||
|
#define PARAM_STRUCT_TYPES(_type,_getter,_default) \
|
||||||
|
if (!strcmp(argType, @encode(_type))) { \
|
||||||
|
_type value = [obj respondsToSelector:@selector(_getter)]?[obj _getter]:_default; \
|
||||||
|
[invocation setArgument:&value atIndex:index]; \
|
||||||
|
}
|
||||||
|
PARAM_STRUCT_TYPES(CGPoint, CGPointValue, CGPointZero)
|
||||||
|
PARAM_STRUCT_TYPES(CGSize, CGSizeValue, CGSizeZero)
|
||||||
|
PARAM_STRUCT_TYPES(CGRect, CGRectValue, CGRectZero)
|
||||||
|
PARAM_STRUCT_TYPES(CGAffineTransform, CGAffineTransformValue, CGAffineTransformIdentity)
|
||||||
|
PARAM_STRUCT_TYPES(CATransform3D, CATransform3DValue, CATransform3DIdentity)
|
||||||
|
PARAM_STRUCT_TYPES(CGVector, CGVectorValue, CGVectorMake(0, 0))
|
||||||
|
PARAM_STRUCT_TYPES(UIEdgeInsets, UIEdgeInsetsValue, UIEdgeInsetsZero)
|
||||||
|
PARAM_STRUCT_TYPES(UIOffset, UIOffsetValue, UIOffsetZero)
|
||||||
|
PARAM_STRUCT_TYPES(NSRange, rangeValue, NSMakeRange(NSNotFound, 0))
|
||||||
|
|
||||||
|
#undef PARAM_STRUCT_TYPES
|
||||||
|
index++;
|
||||||
|
} else {
|
||||||
|
// basic type
|
||||||
|
#define PARAM_BASIC_TYPES(_type,_getter) \
|
||||||
|
if (!strcmp(argType, @encode(_type))) { \
|
||||||
|
_type value = [obj respondsToSelector:@selector(_getter)]?[obj _getter]:0; \
|
||||||
|
[invocation setArgument:&value atIndex:index]; \
|
||||||
|
}
|
||||||
|
PARAM_BASIC_TYPES(BOOL, boolValue)
|
||||||
|
PARAM_BASIC_TYPES(int, intValue)
|
||||||
|
PARAM_BASIC_TYPES(unsigned int, unsignedIntValue)
|
||||||
|
PARAM_BASIC_TYPES(char, charValue)
|
||||||
|
PARAM_BASIC_TYPES(unsigned char, unsignedCharValue)
|
||||||
|
PARAM_BASIC_TYPES(long, longValue)
|
||||||
|
PARAM_BASIC_TYPES(unsigned long, unsignedLongValue)
|
||||||
|
PARAM_BASIC_TYPES(long long, longLongValue)
|
||||||
|
PARAM_BASIC_TYPES(unsigned long long, unsignedLongLongValue)
|
||||||
|
PARAM_BASIC_TYPES(float, floatValue)
|
||||||
|
PARAM_BASIC_TYPES(double, doubleValue)
|
||||||
|
|
||||||
|
#undef PARAM_BASIC_TYPES
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while((obj = va_arg(args, id)));
|
||||||
|
|
||||||
|
}
|
||||||
|
va_end(args);
|
||||||
|
[invocation retainArguments];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute
|
||||||
|
[invocation invoke];
|
||||||
|
|
||||||
|
// Return
|
||||||
|
const char *returnType = signature.methodReturnType;
|
||||||
|
NSUInteger length = [signature methodReturnLength];
|
||||||
|
id returnValue;
|
||||||
|
if (!strcmp(returnType, @encode(void))){
|
||||||
|
// void
|
||||||
|
returnValue = nil;
|
||||||
|
} else if(!strcmp(returnType, @encode(id))){
|
||||||
|
// id
|
||||||
|
void *value;
|
||||||
|
[invocation getReturnValue:&value];
|
||||||
|
returnValue = (__bridge id)(value);
|
||||||
|
return returnValue;
|
||||||
|
} else {
|
||||||
|
NSString *returnTypeString = [NSString stringWithUTF8String:returnType];
|
||||||
|
if ([returnTypeString hasPrefix:@"{"] && [returnTypeString hasSuffix:@"}"]) {
|
||||||
|
// struct
|
||||||
|
#define RETURN_STRUCT_TYPES(_type) \
|
||||||
|
if (!strcmp(returnType, @encode(_type))) { \
|
||||||
|
_type value; \
|
||||||
|
[invocation getReturnValue:&value]; \
|
||||||
|
returnValue = [NSValue value:&value withObjCType:@encode(_type)]; \
|
||||||
|
}
|
||||||
|
RETURN_STRUCT_TYPES(CGPoint)
|
||||||
|
RETURN_STRUCT_TYPES(CGSize)
|
||||||
|
RETURN_STRUCT_TYPES(CGRect)
|
||||||
|
RETURN_STRUCT_TYPES(CGAffineTransform)
|
||||||
|
RETURN_STRUCT_TYPES(CATransform3D)
|
||||||
|
RETURN_STRUCT_TYPES(CGVector)
|
||||||
|
RETURN_STRUCT_TYPES(UIEdgeInsets)
|
||||||
|
RETURN_STRUCT_TYPES(UIOffset)
|
||||||
|
RETURN_STRUCT_TYPES(NSRange)
|
||||||
|
|
||||||
|
#undef RETURN_STRUCT_TYPES
|
||||||
|
} else {
|
||||||
|
// basic
|
||||||
|
void *buffer = (void *)malloc(length);
|
||||||
|
[invocation getReturnValue:buffer];
|
||||||
|
#define RETURN_BASIC_TYPES(_type) \
|
||||||
|
if (!strcmp(returnType, @encode(_type))) { \
|
||||||
|
returnValue = @(*((_type*)buffer)); \
|
||||||
|
}
|
||||||
|
RETURN_BASIC_TYPES(BOOL)
|
||||||
|
RETURN_BASIC_TYPES(int)
|
||||||
|
RETURN_BASIC_TYPES(unsigned int)
|
||||||
|
RETURN_BASIC_TYPES(char)
|
||||||
|
RETURN_BASIC_TYPES(unsigned char)
|
||||||
|
RETURN_BASIC_TYPES(long)
|
||||||
|
RETURN_BASIC_TYPES(unsigned long)
|
||||||
|
RETURN_BASIC_TYPES(long long)
|
||||||
|
RETURN_BASIC_TYPES(unsigned long long)
|
||||||
|
RETURN_BASIC_TYPES(float)
|
||||||
|
RETURN_BASIC_TYPES(double)
|
||||||
|
|
||||||
|
#undef RETURN_BASIC_TYPES
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
//
|
|
||||||
// FSCalendarAnimationLayout.h
|
|
||||||
// FSCalendar
|
|
||||||
//
|
|
||||||
// Created by dingwenchao on 1/3/16.
|
|
||||||
// Copyright © 2016 wenchaoios. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
|
|
||||||
@class FSCalendar;
|
|
||||||
|
|
||||||
typedef NS_ENUM(NSUInteger, FSCalendarScope);
|
|
||||||
|
|
||||||
@interface FSCalendarFlowLayout : UICollectionViewFlowLayout <UICollectionViewDelegateFlowLayout>
|
|
||||||
|
|
||||||
@property (weak, nonatomic) FSCalendar *calendar;
|
|
||||||
|
|
||||||
|
|
||||||
@end
|
|
|
@ -1,86 +0,0 @@
|
||||||
//
|
|
||||||
// FSCalendarAnimationLayout.m
|
|
||||||
// FSCalendar
|
|
||||||
//
|
|
||||||
// Created by dingwenchao on 1/3/16.
|
|
||||||
// Copyright © 2016 wenchaoios. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "FSCalendarFlowLayout.h"
|
|
||||||
#import "FSCalendarDynamicHeader.h"
|
|
||||||
#import "FSCalendar.h"
|
|
||||||
#import "UIView+FSExtension.h"
|
|
||||||
#import <objc/runtime.h>
|
|
||||||
|
|
||||||
@implementation FSCalendarFlowLayout
|
|
||||||
|
|
||||||
- (instancetype)init
|
|
||||||
{
|
|
||||||
self = [super init];
|
|
||||||
if (self) {
|
|
||||||
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
|
||||||
self.minimumInteritemSpacing = 0;
|
|
||||||
self.minimumLineSpacing = 0;
|
|
||||||
self.itemSize = CGSizeMake(1, 1);
|
|
||||||
self.sectionInset = UIEdgeInsetsZero;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)prepareLayout
|
|
||||||
{
|
|
||||||
[super prepareLayout];
|
|
||||||
|
|
||||||
|
|
||||||
CGFloat rowHeight = self.calendar.preferredRowHeight;
|
|
||||||
|
|
||||||
if (!self.calendar.floatingMode) {
|
|
||||||
|
|
||||||
self.headerReferenceSize = CGSizeZero;
|
|
||||||
|
|
||||||
CGFloat padding = self.calendar.preferredWeekdayHeight*0.1;
|
|
||||||
if (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) {
|
|
||||||
padding = FSCalendarFloor(padding);
|
|
||||||
rowHeight = FSCalendarFloor(rowHeight*2)*0.5; // Round to nearest multiple of 0.5. e.g. (16.8->16.5),(16.2->16.0)
|
|
||||||
}
|
|
||||||
self.sectionInset = UIEdgeInsetsMake(padding, 0, padding, 0);
|
|
||||||
switch (self.calendar.scope) {
|
|
||||||
|
|
||||||
case FSCalendarScopeMonth: {
|
|
||||||
|
|
||||||
CGSize itemSize = CGSizeMake(
|
|
||||||
self.collectionView.fs_width/7.0-(self.scrollDirection == UICollectionViewScrollDirectionVertical)*0.1,
|
|
||||||
rowHeight
|
|
||||||
);
|
|
||||||
self.itemSize = itemSize;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case FSCalendarScopeWeek: {
|
|
||||||
|
|
||||||
CGSize itemSize = CGSizeMake(self.collectionView.fs_width/7.0, rowHeight);
|
|
||||||
self.itemSize = itemSize;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
CGFloat headerHeight = self.calendar.preferredWeekdayHeight*1.5+self.calendar.preferredHeaderHeight;
|
|
||||||
self.headerReferenceSize = CGSizeMake(self.collectionView.fs_width, headerHeight);
|
|
||||||
|
|
||||||
CGSize itemSize = CGSizeMake(
|
|
||||||
self.collectionView.fs_width/7-(self.scrollDirection == UICollectionViewScrollDirectionVertical)*0.1,
|
|
||||||
rowHeight
|
|
||||||
);
|
|
||||||
self.itemSize = itemSize;
|
|
||||||
|
|
||||||
self.sectionInset = UIEdgeInsetsZero;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
|
@ -7,22 +7,25 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#import "FSCalendarCollectionView.h"
|
|
||||||
|
|
||||||
@class FSCalendar,FSCalendarAppearance;
|
|
||||||
|
|
||||||
@interface FSCalendarHeader : UIView
|
@class FSCalendar, FSCalendarAppearance, FSCalendarHeaderLayout, FSCalendarCollectionView;
|
||||||
|
|
||||||
|
@interface FSCalendarHeaderView : UIView
|
||||||
|
|
||||||
@property (weak, nonatomic) FSCalendarCollectionView *collectionView;
|
@property (weak, nonatomic) FSCalendarCollectionView *collectionView;
|
||||||
|
@property (weak, nonatomic) FSCalendarHeaderLayout *collectionViewLayout;
|
||||||
@property (weak, nonatomic) FSCalendar *calendar;
|
@property (weak, nonatomic) FSCalendar *calendar;
|
||||||
@property (weak, nonatomic) FSCalendarAppearance *appearance;
|
|
||||||
|
|
||||||
@property (assign, nonatomic) CGFloat scrollOffset;
|
@property (assign, nonatomic) CGFloat scrollOffset;
|
||||||
@property (assign, nonatomic) UICollectionViewScrollDirection scrollDirection;
|
@property (assign, nonatomic) UICollectionViewScrollDirection scrollDirection;
|
||||||
@property (assign, nonatomic) BOOL scrollEnabled;
|
@property (assign, nonatomic) BOOL scrollEnabled;
|
||||||
@property (assign, nonatomic) BOOL needsAdjustingViewFrame;
|
@property (assign, nonatomic) BOOL needsAdjustingViewFrame;
|
||||||
|
@property (assign, nonatomic) BOOL needsAdjustingMonthPosition;
|
||||||
|
|
||||||
|
- (void)setScrollOffset:(CGFloat)scrollOffset animated:(BOOL)animated;
|
||||||
- (void)reloadData;
|
- (void)reloadData;
|
||||||
|
- (void)configureAppearance;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -30,17 +33,17 @@
|
||||||
@interface FSCalendarHeaderCell : UICollectionViewCell
|
@interface FSCalendarHeaderCell : UICollectionViewCell
|
||||||
|
|
||||||
@property (weak, nonatomic) UILabel *titleLabel;
|
@property (weak, nonatomic) UILabel *titleLabel;
|
||||||
@property (weak, nonatomic) FSCalendarHeader *header;
|
@property (weak, nonatomic) FSCalendarHeaderView *header;
|
||||||
|
|
||||||
- (void)invalidateHeaderFont;
|
|
||||||
- (void)invalidateHeaderTextColor;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@interface FSCalendarHeaderLayout : UICollectionViewFlowLayout
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@interface FSCalendarHeaderTouchDeliver : UIView
|
@interface FSCalendarHeaderTouchDeliver : UIView
|
||||||
|
|
||||||
@property (weak, nonatomic) FSCalendar *calendar;
|
@property (weak, nonatomic) FSCalendar *calendar;
|
||||||
@property (weak, nonatomic) FSCalendarHeader *header;
|
@property (weak, nonatomic) FSCalendarHeaderView *header;
|
||||||
|
|
||||||
@end
|
@end
|
|
@ -7,20 +7,19 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "FSCalendar.h"
|
#import "FSCalendar.h"
|
||||||
#import "UIView+FSExtension.h"
|
#import "FSCalendarExtensions.h"
|
||||||
#import "FSCalendarHeader.h"
|
#import "FSCalendarHeaderView.h"
|
||||||
#import "FSCalendarCollectionView.h"
|
#import "FSCalendarCollectionView.h"
|
||||||
#import "FSCalendarDynamicHeader.h"
|
#import "FSCalendarDynamicHeader.h"
|
||||||
|
|
||||||
@interface FSCalendarHeader ()<UICollectionViewDataSource,UICollectionViewDelegate>
|
@interface FSCalendarHeaderView ()<UICollectionViewDataSource,UICollectionViewDelegate>
|
||||||
|
|
||||||
@property (weak, nonatomic) UICollectionViewFlowLayout *collectionViewLayout;
|
- (void)scrollToOffset:(CGFloat)scrollOffset animated:(BOOL)animated;
|
||||||
|
- (void)configureCell:(FSCalendarHeaderCell *)cell atIndexPath:(NSIndexPath *)indexPath;
|
||||||
@property (assign, nonatomic) BOOL needsAdjustingMonthPosition;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation FSCalendarHeader
|
@implementation FSCalendarHeaderView
|
||||||
|
|
||||||
#pragma mark - Life cycle
|
#pragma mark - Life cycle
|
||||||
|
|
||||||
|
@ -44,17 +43,12 @@
|
||||||
|
|
||||||
- (void)initialize
|
- (void)initialize
|
||||||
{
|
{
|
||||||
|
_needsAdjustingViewFrame = YES;
|
||||||
|
_needsAdjustingMonthPosition = YES;
|
||||||
_scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
_scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||||
_scrollEnabled = YES;
|
_scrollEnabled = YES;
|
||||||
_needsAdjustingMonthPosition = YES;
|
|
||||||
_needsAdjustingViewFrame = YES;
|
|
||||||
|
|
||||||
UICollectionViewFlowLayout *collectionViewLayout = [[UICollectionViewFlowLayout alloc] init];
|
FSCalendarHeaderLayout *collectionViewLayout = [[FSCalendarHeaderLayout alloc] init];
|
||||||
collectionViewLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
|
||||||
collectionViewLayout.minimumInteritemSpacing = 0;
|
|
||||||
collectionViewLayout.minimumLineSpacing = 0;
|
|
||||||
collectionViewLayout.sectionInset = UIEdgeInsetsZero;
|
|
||||||
collectionViewLayout.itemSize = CGSizeMake(1, 1);
|
|
||||||
self.collectionViewLayout = collectionViewLayout;
|
self.collectionViewLayout = collectionViewLayout;
|
||||||
|
|
||||||
FSCalendarCollectionView *collectionView = [[FSCalendarCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:collectionViewLayout];
|
FSCalendarCollectionView *collectionView = [[FSCalendarCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:collectionViewLayout];
|
||||||
|
@ -77,21 +71,14 @@
|
||||||
if (_needsAdjustingViewFrame) {
|
if (_needsAdjustingViewFrame) {
|
||||||
_needsAdjustingViewFrame = NO;
|
_needsAdjustingViewFrame = NO;
|
||||||
_collectionViewLayout.itemSize = CGSizeMake(1, 1);
|
_collectionViewLayout.itemSize = CGSizeMake(1, 1);
|
||||||
|
[_collectionViewLayout invalidateLayout];
|
||||||
_collectionView.frame = CGRectMake(0, self.fs_height*0.1, self.fs_width, self.fs_height*0.9);
|
_collectionView.frame = CGRectMake(0, self.fs_height*0.1, self.fs_width, self.fs_height*0.9);
|
||||||
_collectionViewLayout.itemSize = CGSizeMake(
|
|
||||||
_collectionView.fs_width*((_scrollDirection==UICollectionViewScrollDirectionHorizontal)?0.5:1),
|
|
||||||
_collectionView.fs_height
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_needsAdjustingMonthPosition) {
|
if (_needsAdjustingMonthPosition) {
|
||||||
_needsAdjustingMonthPosition = NO;
|
_needsAdjustingMonthPosition = NO;
|
||||||
if (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) {
|
[self scrollToOffset:_scrollOffset animated:NO];
|
||||||
[_collectionView setContentOffset:CGPointMake((_scrollOffset+0.5)*_collectionViewLayout.itemSize.width, 0) animated:NO];
|
}
|
||||||
} else {
|
|
||||||
[_collectionView setContentOffset:CGPointMake(0, _scrollOffset * _collectionViewLayout.itemSize.height) animated:NO];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,81 +97,18 @@
|
||||||
|
|
||||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
|
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
|
||||||
{
|
{
|
||||||
switch (self.calendar.scope) {
|
NSInteger numberOfSections = self.calendar.collectionView.numberOfSections;
|
||||||
case FSCalendarScopeMonth: {
|
if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
|
||||||
switch (_scrollDirection) {
|
return numberOfSections;
|
||||||
case UICollectionViewScrollDirectionVertical: {
|
|
||||||
NSDate *minimumPage = [_calendar beginingOfMonthOfDate:_calendar.minimumDate];
|
|
||||||
NSInteger count = [_calendar monthsFromDate:minimumPage toDate:_calendar.maximumDate] + 1;
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
case UICollectionViewScrollDirectionHorizontal: {
|
|
||||||
// 这里需要默认多出两项,否则当contentOffset为负时,切换到其他页面时会自动归零
|
|
||||||
// 2 more pages to prevent scrollView from auto bouncing while push/present to other UIViewController
|
|
||||||
NSDate *minimumPage = [_calendar beginingOfMonthOfDate:_calendar.minimumDate];
|
|
||||||
NSInteger count = [_calendar monthsFromDate:minimumPage toDate:_calendar.maximumDate] + 1;
|
|
||||||
return count + 2;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case FSCalendarScopeWeek: {
|
|
||||||
NSDate *minimumPage = [_calendar beginingOfMonthOfDate:_calendar.minimumDate];
|
|
||||||
NSInteger count = [_calendar weeksFromDate:minimumPage toDate:_calendar.maximumDate] + 1;
|
|
||||||
return count + 2;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return numberOfSections + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
|
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||||
{
|
{
|
||||||
FSCalendarHeaderCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
|
FSCalendarHeaderCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
|
||||||
cell.header = self;
|
cell.header = self;
|
||||||
cell.titleLabel.font = _appearance.preferredHeaderTitleFont;
|
[self configureCell:cell atIndexPath:indexPath];
|
||||||
cell.titleLabel.textColor = _appearance.headerTitleColor;
|
|
||||||
_calendar.formatter.dateFormat = _appearance.headerDateFormat;
|
|
||||||
BOOL usesUpperCase = (_appearance.caseOptions & 15) == FSCalendarCaseOptionsHeaderUsesUpperCase;
|
|
||||||
NSString *text = nil;
|
|
||||||
switch (self.calendar.scope) {
|
|
||||||
case FSCalendarScopeMonth: {
|
|
||||||
if (_scrollDirection == UICollectionViewScrollDirectionHorizontal) {
|
|
||||||
// 多出的两项需要制空
|
|
||||||
if ((indexPath.item == 0 || indexPath.item == [collectionView numberOfItemsInSection:0] - 1)) {
|
|
||||||
text = nil;
|
|
||||||
} else {
|
|
||||||
NSDate *date = [_calendar dateByAddingMonths:indexPath.item-1 toDate:_calendar.minimumDate];
|
|
||||||
text = [_calendar.formatter stringFromDate:date];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
NSDate *date = [_calendar dateByAddingMonths:indexPath.item toDate:_calendar.minimumDate];
|
|
||||||
text = [_calendar.formatter stringFromDate:date];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case FSCalendarScopeWeek: {
|
|
||||||
if ((indexPath.item == 0 || indexPath.item == [collectionView numberOfItemsInSection:0] - 1)) {
|
|
||||||
text = nil;
|
|
||||||
} else {
|
|
||||||
NSDate *firstPage = [_calendar middleOfWeekFromDate:_calendar.minimumDate];
|
|
||||||
NSDate *date = [_calendar dateByAddingWeeks:indexPath.item-1 toDate:firstPage];
|
|
||||||
text = [_calendar.formatter stringFromDate:date];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
text = usesUpperCase ? text.uppercaseString : text;
|
|
||||||
cell.titleLabel.text = text;
|
|
||||||
[cell setNeedsLayout];
|
|
||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,23 +124,34 @@
|
||||||
|
|
||||||
#pragma mark - Properties
|
#pragma mark - Properties
|
||||||
|
|
||||||
|
|
||||||
- (void)setCalendar:(FSCalendar *)calendar
|
- (void)setCalendar:(FSCalendar *)calendar
|
||||||
{
|
{
|
||||||
if (![_calendar isEqual:calendar]) {
|
_calendar = calendar;
|
||||||
_calendar = calendar;
|
[self configureAppearance];
|
||||||
_appearance = calendar.appearance;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)setScrollOffset:(CGFloat)scrollOffset
|
- (void)setScrollOffset:(CGFloat)scrollOffset
|
||||||
|
{
|
||||||
|
[self setScrollOffset:scrollOffset animated:NO];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setScrollOffset:(CGFloat)scrollOffset animated:(BOOL)animated
|
||||||
{
|
{
|
||||||
if (_scrollOffset != scrollOffset) {
|
if (_scrollOffset != scrollOffset) {
|
||||||
_scrollOffset = scrollOffset;
|
_scrollOffset = scrollOffset;
|
||||||
}
|
}
|
||||||
_needsAdjustingMonthPosition = YES;
|
[self scrollToOffset:scrollOffset animated:NO];
|
||||||
[self setNeedsLayout];
|
}
|
||||||
|
|
||||||
|
- (void)scrollToOffset:(CGFloat)scrollOffset animated:(BOOL)animated
|
||||||
|
{
|
||||||
|
if (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) {
|
||||||
|
CGFloat step = self.collectionView.fs_width*((self.scrollDirection==UICollectionViewScrollDirectionHorizontal)?0.5:1);
|
||||||
|
[_collectionView setContentOffset:CGPointMake((scrollOffset+0.5)*step, 0) animated:animated];
|
||||||
|
} else {
|
||||||
|
CGFloat step = self.collectionView.fs_height;
|
||||||
|
[_collectionView setContentOffset:CGPointMake(0, scrollOffset*step) animated:animated];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setScrollDirection:(UICollectionViewScrollDirection)scrollDirection
|
- (void)setScrollDirection:(UICollectionViewScrollDirection)scrollDirection
|
||||||
|
@ -225,7 +160,6 @@
|
||||||
_scrollDirection = scrollDirection;
|
_scrollDirection = scrollDirection;
|
||||||
_collectionViewLayout.scrollDirection = scrollDirection;
|
_collectionViewLayout.scrollDirection = scrollDirection;
|
||||||
_needsAdjustingMonthPosition = YES;
|
_needsAdjustingMonthPosition = YES;
|
||||||
_needsAdjustingViewFrame = YES;
|
|
||||||
[self setNeedsLayout];
|
[self setNeedsLayout];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,6 +179,56 @@
|
||||||
[_collectionView reloadData];
|
[_collectionView reloadData];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)configureCell:(FSCalendarHeaderCell *)cell atIndexPath:(NSIndexPath *)indexPath
|
||||||
|
{
|
||||||
|
FSCalendarAppearance *appearance = self.calendar.appearance;
|
||||||
|
cell.titleLabel.font = appearance.headerTitleFont;
|
||||||
|
cell.titleLabel.textColor = appearance.headerTitleColor;
|
||||||
|
_calendar.formatter.dateFormat = appearance.headerDateFormat;
|
||||||
|
BOOL usesUpperCase = (appearance.caseOptions & 15) == FSCalendarCaseOptionsHeaderUsesUpperCase;
|
||||||
|
NSString *text = nil;
|
||||||
|
switch (self.calendar.transitionCoordinator.representingScope) {
|
||||||
|
case FSCalendarScopeMonth: {
|
||||||
|
if (_scrollDirection == UICollectionViewScrollDirectionHorizontal) {
|
||||||
|
// 多出的两项需要制空
|
||||||
|
if ((indexPath.item == 0 || indexPath.item == [self.collectionView numberOfItemsInSection:0] - 1)) {
|
||||||
|
text = nil;
|
||||||
|
} else {
|
||||||
|
NSDate *date = [self.calendar.gregorian dateByAddingUnit:NSCalendarUnitMonth value:indexPath.item-1 toDate:self.calendar.minimumDate options:0];
|
||||||
|
text = [_calendar.formatter stringFromDate:date];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NSDate *date = [self.calendar.gregorian dateByAddingUnit:NSCalendarUnitMonth value:indexPath.item toDate:self.calendar.minimumDate options:0];
|
||||||
|
text = [_calendar.formatter stringFromDate:date];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FSCalendarScopeWeek: {
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
text = usesUpperCase ? text.uppercaseString : text;
|
||||||
|
cell.titleLabel.text = text;
|
||||||
|
[cell setNeedsLayout];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)configureAppearance
|
||||||
|
{
|
||||||
|
[self.collectionView.visibleCells enumerateObjectsUsingBlock:^(__kindof FSCalendarHeaderCell * _Nonnull cell, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||||
|
[self configureCell:cell atIndexPath:[self.collectionView indexPathForCell:cell]];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@ -280,31 +264,49 @@
|
||||||
CGFloat position = [self.contentView convertPoint:CGPointMake(CGRectGetMidX(self.contentView.bounds), CGRectGetMidY(self.contentView.bounds)) toView:self.header].x;
|
CGFloat position = [self.contentView convertPoint:CGPointMake(CGRectGetMidX(self.contentView.bounds), CGRectGetMidY(self.contentView.bounds)) toView:self.header].x;
|
||||||
CGFloat center = CGRectGetMidX(self.header.bounds);
|
CGFloat center = CGRectGetMidX(self.header.bounds);
|
||||||
if (self.header.scrollEnabled) {
|
if (self.header.scrollEnabled) {
|
||||||
self.contentView.alpha = 1.0 - (1.0-self.header.appearance.headerMinimumDissolvedAlpha)*ABS(center-position)/self.fs_width;
|
self.contentView.alpha = 1.0 - (1.0-self.header.calendar.appearance.headerMinimumDissolvedAlpha)*ABS(center-position)/self.fs_width;
|
||||||
} else {
|
} else {
|
||||||
self.contentView.alpha = (position > 0 && position < self.header.fs_width*0.75);
|
self.contentView.alpha = (position > self.header.fs_width*0.25 && position < self.header.fs_width*0.75);
|
||||||
}
|
}
|
||||||
} else if (self.header.scrollDirection == UICollectionViewScrollDirectionVertical) {
|
} else if (self.header.scrollDirection == UICollectionViewScrollDirectionVertical) {
|
||||||
CGFloat position = [self.contentView convertPoint:CGPointMake(CGRectGetMidX(self.contentView.bounds), CGRectGetMidY(self.contentView.bounds)) toView:self.header].y;
|
CGFloat position = [self.contentView convertPoint:CGPointMake(CGRectGetMidX(self.contentView.bounds), CGRectGetMidY(self.contentView.bounds)) toView:self.header].y;
|
||||||
CGFloat center = CGRectGetMidY(self.header.bounds);
|
CGFloat center = CGRectGetMidY(self.header.bounds);
|
||||||
self.contentView.alpha = 1.0 - (1.0-self.header.appearance.headerMinimumDissolvedAlpha)*ABS(center-position)/self.fs_height;
|
self.contentView.alpha = 1.0 - (1.0-self.header.calendar.appearance.headerMinimumDissolvedAlpha)*ABS(center-position)/self.fs_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)invalidateHeaderFont
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@implementation FSCalendarHeaderLayout
|
||||||
|
|
||||||
|
- (instancetype)init
|
||||||
{
|
{
|
||||||
_titleLabel.font = self.header.appearance.preferredHeaderTitleFont;
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||||
|
self.minimumInteritemSpacing = 0;
|
||||||
|
self.minimumLineSpacing = 0;
|
||||||
|
self.sectionInset = UIEdgeInsetsZero;
|
||||||
|
self.itemSize = CGSizeMake(1, 1);
|
||||||
|
}
|
||||||
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)invalidateHeaderTextColor
|
- (void)prepareLayout
|
||||||
{
|
{
|
||||||
_titleLabel.textColor = self.header.appearance.headerTitleColor;
|
[super prepareLayout];
|
||||||
|
|
||||||
|
self.itemSize = CGSizeMake(
|
||||||
|
self.collectionView.fs_width*((self.scrollDirection==UICollectionViewScrollDirectionHorizontal)?0.5:1),
|
||||||
|
self.collectionView.fs_height
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
@implementation FSCalendarHeaderTouchDeliver
|
@implementation FSCalendarHeaderTouchDeliver
|
||||||
|
|
||||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
|
@ -0,0 +1,20 @@
|
||||||
|
//
|
||||||
|
// FSCalendarScopeHandle.h
|
||||||
|
// FSCalendar
|
||||||
|
//
|
||||||
|
// Created by dingwenchao on 4/29/16.
|
||||||
|
// Copyright © 2016 Wenchao Ding. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@class FSCalendar;
|
||||||
|
|
||||||
|
@interface FSCalendarScopeHandle : UIView <UIGestureRecognizerDelegate>
|
||||||
|
|
||||||
|
@property (weak, nonatomic) UIPanGestureRecognizer *panGesture;
|
||||||
|
@property (weak, nonatomic) FSCalendar *calendar;
|
||||||
|
|
||||||
|
- (void)handlePan:(id)sender;
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,78 @@
|
||||||
|
//
|
||||||
|
// FSCalendarScopeHandle.m
|
||||||
|
// FSCalendar
|
||||||
|
//
|
||||||
|
// Created by dingwenchao on 4/29/16.
|
||||||
|
// Copyright © 2016 Wenchao Ding. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "FSCalendarScopeHandle.h"
|
||||||
|
#import "FSCalendar.h"
|
||||||
|
#import "FSCalendarTransitionCoordinator.h"
|
||||||
|
#import "FSCalendarDynamicHeader.h"
|
||||||
|
#import "FSCalendarExtensions.h"
|
||||||
|
|
||||||
|
@interface FSCalendarScopeHandle () <UIGestureRecognizerDelegate>
|
||||||
|
|
||||||
|
@property (weak, nonatomic) UIView *topBorder;
|
||||||
|
@property (weak, nonatomic) UIView *handleIndicator;
|
||||||
|
|
||||||
|
@property (weak, nonatomic) FSCalendarAppearance *appearance;
|
||||||
|
|
||||||
|
@property (assign, nonatomic) CGFloat lastTranslation;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FSCalendarScopeHandle
|
||||||
|
|
||||||
|
- (instancetype)initWithFrame:(CGRect)frame
|
||||||
|
{
|
||||||
|
self = [super initWithFrame:frame];
|
||||||
|
if (self) {
|
||||||
|
|
||||||
|
UIView *view;
|
||||||
|
|
||||||
|
view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 1)];
|
||||||
|
view.backgroundColor = FSCalendarStandardLineColor;
|
||||||
|
[self addSubview:view];
|
||||||
|
self.topBorder = view;
|
||||||
|
|
||||||
|
view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 30, 6)];
|
||||||
|
view.layer.shouldRasterize = YES;
|
||||||
|
view.layer.masksToBounds = YES;
|
||||||
|
view.layer.cornerRadius = 3;
|
||||||
|
view.layer.backgroundColor = FSCalendarStandardScopeHandleColor.CGColor;
|
||||||
|
[self addSubview:view];
|
||||||
|
self.handleIndicator = view;
|
||||||
|
|
||||||
|
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
|
||||||
|
panGesture.minimumNumberOfTouches = 1;
|
||||||
|
panGesture.maximumNumberOfTouches = 2;
|
||||||
|
[self addGestureRecognizer:panGesture];
|
||||||
|
self.panGesture = panGesture;
|
||||||
|
|
||||||
|
self.exclusiveTouch = YES;
|
||||||
|
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)layoutSubviews
|
||||||
|
{
|
||||||
|
[super layoutSubviews];
|
||||||
|
self.topBorder.frame = CGRectMake(0, 0, self.fs_width, 1);
|
||||||
|
self.handleIndicator.center = CGPointMake(self.fs_width/2, self.fs_height/2-0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)handlePan:(id)sender
|
||||||
|
{
|
||||||
|
[self.calendar.transitionCoordinator handleScopeGesture:sender];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setCalendar:(FSCalendar *)calendar
|
||||||
|
{
|
||||||
|
_calendar = calendar;
|
||||||
|
self.panGesture.delegate = self.calendar.transitionCoordinator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -3,7 +3,7 @@
|
||||||
// FSCalendar
|
// FSCalendar
|
||||||
//
|
//
|
||||||
// Created by dingwenchao on 9/17/15.
|
// Created by dingwenchao on 9/17/15.
|
||||||
// Copyright (c) 2015 wenchaoios. All rights reserved.
|
// Copyright (c) 2015 Wenchao Ding. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
@ -13,18 +13,11 @@
|
||||||
@interface FSCalendarStickyHeader : UICollectionReusableView
|
@interface FSCalendarStickyHeader : UICollectionReusableView
|
||||||
|
|
||||||
@property (weak, nonatomic) FSCalendar *calendar;
|
@property (weak, nonatomic) FSCalendar *calendar;
|
||||||
@property (weak, nonatomic) FSCalendarAppearance *appearance;
|
|
||||||
|
|
||||||
@property (weak, nonatomic) UILabel *titleLabel;
|
@property (weak, nonatomic) UILabel *titleLabel;
|
||||||
|
|
||||||
@property (strong, nonatomic) NSArray *weekdayLabels;
|
|
||||||
@property (strong, nonatomic) NSDate *month;
|
@property (strong, nonatomic) NSDate *month;
|
||||||
|
|
||||||
- (void)invalidateHeaderFont;
|
- (void)configureAppearance;
|
||||||
- (void)invalidateHeaderTextColor;
|
|
||||||
- (void)invalidateWeekdayFont;
|
|
||||||
- (void)invalidateWeekdayTextColor;
|
|
||||||
|
|
||||||
- (void)invalidateWeekdaySymbols;
|
@end
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
|
@ -3,21 +3,21 @@
|
||||||
// FSCalendar
|
// FSCalendar
|
||||||
//
|
//
|
||||||
// Created by dingwenchao on 9/17/15.
|
// Created by dingwenchao on 9/17/15.
|
||||||
// Copyright (c) 2015 wenchaoios. All rights reserved.
|
// Copyright (c) 2015 Wenchao Ding. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "FSCalendarStickyHeader.h"
|
#import "FSCalendarStickyHeader.h"
|
||||||
#import "FSCalendar.h"
|
#import "FSCalendar.h"
|
||||||
#import "UIView+FSExtension.h"
|
#import "FSCalendarWeekdayView.h"
|
||||||
#import "FSCalendarConstance.h"
|
#import "FSCalendarExtensions.h"
|
||||||
|
#import "FSCalendarConstants.h"
|
||||||
#import "FSCalendarDynamicHeader.h"
|
#import "FSCalendarDynamicHeader.h"
|
||||||
|
|
||||||
@interface FSCalendarStickyHeader ()
|
@interface FSCalendarStickyHeader ()
|
||||||
|
|
||||||
@property (weak, nonatomic) UIView *contentView;
|
@property (weak , nonatomic) UIView *contentView;
|
||||||
@property (weak, nonatomic) UIView *separator;
|
@property (weak , nonatomic) UIView *bottomBorder;
|
||||||
|
@property (weak , nonatomic) FSCalendarWeekdayView *weekdayView;
|
||||||
@property (assign, nonatomic) BOOL needsAdjustingViewFrame;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@ -28,8 +28,6 @@
|
||||||
self = [super initWithFrame:frame];
|
self = [super initWithFrame:frame];
|
||||||
if (self) {
|
if (self) {
|
||||||
|
|
||||||
_needsAdjustingViewFrame = YES;
|
|
||||||
|
|
||||||
UIView *view;
|
UIView *view;
|
||||||
UILabel *label;
|
UILabel *label;
|
||||||
|
|
||||||
|
@ -45,18 +43,13 @@
|
||||||
self.titleLabel = label;
|
self.titleLabel = label;
|
||||||
|
|
||||||
view = [[UIView alloc] initWithFrame:CGRectZero];
|
view = [[UIView alloc] initWithFrame:CGRectZero];
|
||||||
view.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.25];
|
view.backgroundColor = FSCalendarStandardLineColor;
|
||||||
[_contentView addSubview:view];
|
[_contentView addSubview:view];
|
||||||
self.separator = view;
|
self.bottomBorder = view;
|
||||||
|
|
||||||
NSMutableArray *weekdayLabels = [NSMutableArray arrayWithCapacity:7];
|
FSCalendarWeekdayView *weekdayView = [[FSCalendarWeekdayView alloc] init];
|
||||||
for (int i = 0; i < 7; i++) {
|
[self.contentView addSubview:weekdayView];
|
||||||
label = [[UILabel alloc] initWithFrame:CGRectZero];
|
self.weekdayView = weekdayView;
|
||||||
label.textAlignment = NSTextAlignmentCenter;
|
|
||||||
[_contentView addSubview:label];
|
|
||||||
[weekdayLabels addObject:label];
|
|
||||||
}
|
|
||||||
self.weekdayLabels = weekdayLabels.copy;
|
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
@ -65,28 +58,19 @@
|
||||||
{
|
{
|
||||||
[super layoutSubviews];
|
[super layoutSubviews];
|
||||||
|
|
||||||
if (_needsAdjustingViewFrame) {
|
_contentView.frame = self.bounds;
|
||||||
|
|
||||||
_needsAdjustingViewFrame = NO;
|
CGFloat weekdayHeight = _calendar.preferredWeekdayHeight;
|
||||||
_contentView.frame = self.bounds;
|
CGFloat weekdayMargin = weekdayHeight * 0.1;
|
||||||
|
CGFloat titleWidth = _contentView.fs_width;
|
||||||
CGFloat weekdayWidth = self.fs_width / 7.0;
|
|
||||||
CGFloat weekdayHeight = _calendar.preferredWeekdayHeight;
|
self.weekdayView.frame = CGRectMake(0, _contentView.fs_height-weekdayHeight-weekdayMargin, self.contentView.fs_width, weekdayHeight);
|
||||||
CGFloat weekdayMargin = weekdayHeight * 0.1;
|
|
||||||
CGFloat titleWidth = _contentView.fs_width;
|
CGFloat titleHeight = [@"1" sizeWithAttributes:@{NSFontAttributeName:self.calendar.appearance.headerTitleFont}].height*1.5 + weekdayMargin*3;
|
||||||
|
|
||||||
[_weekdayLabels enumerateObjectsUsingBlock:^(UILabel *label, NSUInteger index, BOOL *stop) { \
|
_bottomBorder.frame = CGRectMake(0, _contentView.fs_height-weekdayHeight-weekdayMargin*2, _contentView.fs_width, 1.0);
|
||||||
label.frame = CGRectMake(index*weekdayWidth, _contentView.fs_height-weekdayHeight-weekdayMargin, weekdayWidth, weekdayHeight);
|
_titleLabel.frame = CGRectMake(0, _bottomBorder.fs_bottom-titleHeight-weekdayMargin, titleWidth,titleHeight);
|
||||||
}];
|
|
||||||
|
|
||||||
CGFloat titleHeight = [@"1" sizeWithAttributes:@{NSFontAttributeName:_appearance.preferredHeaderTitleFont}].height*1.5 + weekdayMargin*3;
|
|
||||||
|
|
||||||
_separator.frame = CGRectMake(0, _contentView.fs_height-weekdayHeight-weekdayMargin*2, _contentView.fs_width, 1.0);
|
|
||||||
_titleLabel.frame = CGRectMake(0, _separator.fs_bottom-titleHeight-weekdayMargin, titleWidth,titleHeight);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[self reloadData];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Properties
|
#pragma mark - Properties
|
||||||
|
@ -95,63 +79,30 @@
|
||||||
{
|
{
|
||||||
if (![_calendar isEqual:calendar]) {
|
if (![_calendar isEqual:calendar]) {
|
||||||
_calendar = calendar;
|
_calendar = calendar;
|
||||||
|
_weekdayView.calendar = calendar;
|
||||||
|
[self configureAppearance];
|
||||||
}
|
}
|
||||||
if (![_appearance isEqual:calendar.appearance]) {
|
|
||||||
_appearance = calendar.appearance;
|
|
||||||
[self invalidateHeaderFont];
|
|
||||||
[self invalidateHeaderTextColor];
|
|
||||||
[self invalidateWeekdayFont];
|
|
||||||
[self invalidateWeekdayTextColor];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Public methods
|
|
||||||
|
|
||||||
- (void)reloadData
|
|
||||||
{
|
|
||||||
[self invalidateWeekdaySymbols];
|
|
||||||
_calendar.formatter.dateFormat = _appearance.headerDateFormat;
|
|
||||||
BOOL usesUpperCase = (_appearance.caseOptions & 15) == FSCalendarCaseOptionsHeaderUsesUpperCase;
|
|
||||||
NSString *text = [_calendar.formatter stringFromDate:_month];
|
|
||||||
text = usesUpperCase ? text.uppercaseString : text;
|
|
||||||
_titleLabel.text = text;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Private methods
|
#pragma mark - Private methods
|
||||||
|
|
||||||
- (void)invalidateHeaderFont
|
- (void)configureAppearance
|
||||||
{
|
{
|
||||||
_titleLabel.font = _appearance.headerTitleFont;
|
_titleLabel.font = self.calendar.appearance.headerTitleFont;
|
||||||
|
_titleLabel.textColor = self.calendar.appearance.headerTitleColor;
|
||||||
|
[self.weekdayView configureAppearance];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)invalidateHeaderTextColor
|
- (void)setMonth:(NSDate *)month
|
||||||
{
|
{
|
||||||
_titleLabel.textColor = _appearance.headerTitleColor;
|
_month = month;
|
||||||
|
_calendar.formatter.dateFormat = self.calendar.appearance.headerDateFormat;
|
||||||
|
BOOL usesUpperCase = (self.calendar.appearance.caseOptions & 15) == FSCalendarCaseOptionsHeaderUsesUpperCase;
|
||||||
|
NSString *text = [_calendar.formatter stringFromDate:_month];
|
||||||
|
text = usesUpperCase ? text.uppercaseString : text;
|
||||||
|
self.titleLabel.text = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)invalidateWeekdayFont
|
|
||||||
{
|
|
||||||
[_weekdayLabels makeObjectsPerformSelector:@selector(setFont:) withObject:_appearance.weekdayFont];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateWeekdayTextColor
|
|
||||||
{
|
|
||||||
[_weekdayLabels makeObjectsPerformSelector:@selector(setTextColor:) withObject:_appearance.weekdayTextColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidateWeekdaySymbols
|
|
||||||
{
|
|
||||||
BOOL useVeryShortWeekdaySymbols = (_appearance.caseOptions & (15<<4) ) == FSCalendarCaseOptionsWeekdayUsesSingleUpperCase;
|
|
||||||
NSArray *weekdaySymbols = useVeryShortWeekdaySymbols ? _calendar.calendar.veryShortStandaloneWeekdaySymbols : _calendar.calendar.shortStandaloneWeekdaySymbols;
|
|
||||||
BOOL useDefaultWeekdayCase = (_appearance.caseOptions & (15<<4) ) == FSCalendarCaseOptionsWeekdayUsesDefaultCase;
|
|
||||||
[_weekdayLabels enumerateObjectsUsingBlock:^(UILabel *label, NSUInteger index, BOOL *stop) {
|
|
||||||
index += _calendar.firstWeekday-1;
|
|
||||||
index %= 7;
|
|
||||||
label.text = useDefaultWeekdayCase ? weekdaySymbols[index] : [weekdaySymbols[index] uppercaseString];
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
59
Verdnaturaventas/Pods/FSCalendar/FSCalendar/FSCalendarTransitionCoordinator.h
generated
Normal file
59
Verdnaturaventas/Pods/FSCalendar/FSCalendar/FSCalendarTransitionCoordinator.h
generated
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
//
|
||||||
|
// FSCalendarTransitionCoordinator.h
|
||||||
|
// FSCalendar
|
||||||
|
//
|
||||||
|
// Created by dingwenchao on 3/13/16.
|
||||||
|
// Copyright © 2016 Wenchao Ding. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "FSCalendar.h"
|
||||||
|
#import "FSCalendarCollectionView.h"
|
||||||
|
#import "FSCalendarCollectionViewLayout.h"
|
||||||
|
#import "FSCalendarScopeHandle.h"
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSUInteger, FSCalendarTransition) {
|
||||||
|
FSCalendarTransitionNone,
|
||||||
|
FSCalendarTransitionMonthToWeek,
|
||||||
|
FSCalendarTransitionWeekToMonth
|
||||||
|
};
|
||||||
|
typedef NS_ENUM(NSUInteger, FSCalendarTransitionState) {
|
||||||
|
FSCalendarTransitionStateIdle,
|
||||||
|
FSCalendarTransitionStateChanging,
|
||||||
|
FSCalendarTransitionStateFinishing,
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface FSCalendarTransitionCoordinator : NSObject <UIGestureRecognizerDelegate>
|
||||||
|
|
||||||
|
@property (weak, nonatomic) FSCalendar *calendar;
|
||||||
|
@property (weak, nonatomic) FSCalendarCollectionView *collectionView;
|
||||||
|
@property (weak, nonatomic) FSCalendarCollectionViewLayout *collectionViewLayout;
|
||||||
|
|
||||||
|
@property (assign, nonatomic) FSCalendarTransition transition;
|
||||||
|
@property (assign, nonatomic) FSCalendarTransitionState state;
|
||||||
|
|
||||||
|
@property (assign, nonatomic) CGSize cachedMonthSize;
|
||||||
|
|
||||||
|
@property (readonly, nonatomic) FSCalendarScope representingScope;
|
||||||
|
|
||||||
|
- (instancetype)initWithCalendar:(FSCalendar *)calendar;
|
||||||
|
|
||||||
|
- (void)performScopeTransitionFromScope:(FSCalendarScope)fromScope toScope:(FSCalendarScope)toScope animated:(BOOL)animated;
|
||||||
|
- (void)performBoundingRectTransitionFromMonth:(NSDate *)fromMonth toMonth:(NSDate *)toMonth duration:(CGFloat)duration;
|
||||||
|
|
||||||
|
- (void)handleScopeGesture:(id)sender;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@interface FSCalendarTransitionAttributes : NSObject
|
||||||
|
|
||||||
|
@property (assign, nonatomic) CGRect sourceBounds;
|
||||||
|
@property (assign, nonatomic) CGRect targetBounds;
|
||||||
|
@property (strong, nonatomic) NSDate *sourcePage;
|
||||||
|
@property (strong, nonatomic) NSDate *targetPage;
|
||||||
|
@property (assign, nonatomic) NSInteger focusedRowNumber;
|
||||||
|
@property (assign, nonatomic) NSDate *focusedDate;
|
||||||
|
@property (strong, nonatomic) NSDate *firstDayOfMonth;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
752
Verdnaturaventas/Pods/FSCalendar/FSCalendar/FSCalendarTransitionCoordinator.m
generated
Normal file
752
Verdnaturaventas/Pods/FSCalendar/FSCalendar/FSCalendarTransitionCoordinator.m
generated
Normal file
|
@ -0,0 +1,752 @@
|
||||||
|
//
|
||||||
|
// FSCalendarTransitionCoordinator.m
|
||||||
|
// FSCalendar
|
||||||
|
//
|
||||||
|
// Created by Wenchao Ding on 3/13/16.
|
||||||
|
// Copyright © 2016 Wenchao Ding. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "FSCalendarTransitionCoordinator.h"
|
||||||
|
#import "FSCalendarExtensions.h"
|
||||||
|
#import "FSCalendarDynamicHeader.h"
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
|
||||||
|
@interface FSCalendarTransitionCoordinator ()
|
||||||
|
|
||||||
|
@property (readonly, nonatomic) FSCalendarTransitionAttributes *transitionAttributes;
|
||||||
|
@property (strong , nonatomic) FSCalendarTransitionAttributes *pendingAttributes;
|
||||||
|
@property (assign , nonatomic) CGFloat lastTranslation;
|
||||||
|
|
||||||
|
- (void)performTransitionCompletionAnimated:(BOOL)animated;
|
||||||
|
- (void)performTransitionCompletion:(FSCalendarTransition)transition animated:(BOOL)animated;
|
||||||
|
|
||||||
|
- (void)performAlphaAnimationFrom:(CGFloat)fromAlpha to:(CGFloat)toAlpha duration:(CGFloat)duration exception:(NSInteger)exception completion:(void(^)())completion;
|
||||||
|
- (void)performForwardTransition:(FSCalendarTransition)transition fromProgress:(CGFloat)progress;
|
||||||
|
- (void)performBackwardTransition:(FSCalendarTransition)transition fromProgress:(CGFloat)progress;
|
||||||
|
- (void)performAlphaAnimationWithProgress:(CGFloat)progress;
|
||||||
|
- (void)performPathAnimationWithProgress:(CGFloat)progress;
|
||||||
|
|
||||||
|
- (void)scopeTransitionDidBegin:(UIPanGestureRecognizer *)panGesture;
|
||||||
|
- (void)scopeTransitionDidUpdate:(UIPanGestureRecognizer *)panGesture;
|
||||||
|
- (void)scopeTransitionDidEnd:(UIPanGestureRecognizer *)panGesture;
|
||||||
|
|
||||||
|
- (CGRect)boundingRectForScope:(FSCalendarScope)scope page:(NSDate *)page;
|
||||||
|
|
||||||
|
- (void)boundingRectWillChange:(CGRect)targetBounds animated:(BOOL)animated;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FSCalendarTransitionCoordinator
|
||||||
|
|
||||||
|
- (instancetype)initWithCalendar:(FSCalendar *)calendar
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
self.calendar = calendar;
|
||||||
|
self.collectionView = self.calendar.collectionView;
|
||||||
|
self.collectionViewLayout = self.calendar.collectionViewLayout;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Target actions
|
||||||
|
|
||||||
|
- (void)handleScopeGesture:(UIPanGestureRecognizer *)sender
|
||||||
|
{
|
||||||
|
switch (sender.state) {
|
||||||
|
case UIGestureRecognizerStateBegan: {
|
||||||
|
[self scopeTransitionDidBegin:sender];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UIGestureRecognizerStateChanged: {
|
||||||
|
[self scopeTransitionDidUpdate:sender];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UIGestureRecognizerStateEnded: {
|
||||||
|
[self scopeTransitionDidEnd:sender];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UIGestureRecognizerStateCancelled: {
|
||||||
|
[self scopeTransitionDidEnd:sender];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UIGestureRecognizerStateFailed: {
|
||||||
|
[self scopeTransitionDidEnd:sender];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - <UIGestureRecognizerDelegate>
|
||||||
|
|
||||||
|
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
|
||||||
|
{
|
||||||
|
if (self.state != FSCalendarTransitionStateIdle) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||||
|
|
||||||
|
if (gestureRecognizer == self.calendar.scopeGesture && self.calendar.collectionViewLayout.scrollDirection == UICollectionViewScrollDirectionVertical) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
if (gestureRecognizer == self.calendar.scopeHandle.panGesture) {
|
||||||
|
CGFloat velocity = [(UIPanGestureRecognizer *)gestureRecognizer velocityInView:gestureRecognizer.view].y;
|
||||||
|
return self.calendar.scope == FSCalendarScopeWeek ? velocity >= 0 : velocity <= 0;
|
||||||
|
}
|
||||||
|
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && [[gestureRecognizer valueForKey:@"_targets"] containsObject:self.calendar]) {
|
||||||
|
CGPoint velocity = [(UIPanGestureRecognizer *)gestureRecognizer velocityInView:gestureRecognizer.view];
|
||||||
|
BOOL shouldStart = self.calendar.scope == FSCalendarScopeWeek ? velocity.y >= 0 : velocity.y <= 0;
|
||||||
|
if (!shouldStart) return NO;
|
||||||
|
shouldStart = (ABS(velocity.x)<=ABS(velocity.y));
|
||||||
|
if (shouldStart) {
|
||||||
|
self.calendar.collectionView.panGestureRecognizer.enabled = NO;
|
||||||
|
self.calendar.collectionView.panGestureRecognizer.enabled = YES;
|
||||||
|
}
|
||||||
|
return shouldStart;
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
|
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
|
||||||
|
{
|
||||||
|
return otherGestureRecognizer == self.collectionView.panGestureRecognizer && self.collectionView.decelerating;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)scopeTransitionDidBegin:(UIPanGestureRecognizer *)panGesture
|
||||||
|
{
|
||||||
|
if (self.state != FSCalendarTransitionStateIdle) return;
|
||||||
|
|
||||||
|
CGPoint velocity = [panGesture velocityInView:panGesture.view];
|
||||||
|
switch (self.calendar.scope) {
|
||||||
|
case FSCalendarScopeMonth: {
|
||||||
|
if (velocity.y < 0) {
|
||||||
|
self.state = FSCalendarTransitionStateChanging;
|
||||||
|
self.transition = FSCalendarTransitionMonthToWeek;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FSCalendarScopeWeek: {
|
||||||
|
if (velocity.y > 0) {
|
||||||
|
self.state = FSCalendarTransitionStateChanging;
|
||||||
|
self.transition = FSCalendarTransitionWeekToMonth;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (self.state != FSCalendarTransitionStateChanging) return;
|
||||||
|
|
||||||
|
self.pendingAttributes = self.transitionAttributes;
|
||||||
|
self.lastTranslation = [panGesture translationInView:panGesture.view].y;
|
||||||
|
|
||||||
|
if (self.transition == FSCalendarTransitionWeekToMonth) {
|
||||||
|
[self.calendar fs_setVariable:self.pendingAttributes.targetPage forKey:@"_currentPage"];
|
||||||
|
[self prelayoutForWeekToMonthTransition];
|
||||||
|
self.collectionView.fs_top = -self.pendingAttributes.focusedRowNumber*self.calendar.collectionViewLayout.estimatedItemSize.height;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)scopeTransitionDidUpdate:(UIPanGestureRecognizer *)panGesture
|
||||||
|
{
|
||||||
|
if (self.state != FSCalendarTransitionStateChanging) return;
|
||||||
|
|
||||||
|
CGFloat translation = [panGesture translationInView:panGesture.view].y;
|
||||||
|
|
||||||
|
[CATransaction begin];
|
||||||
|
[CATransaction setDisableActions:YES];
|
||||||
|
switch (self.transition) {
|
||||||
|
case FSCalendarTransitionMonthToWeek: {
|
||||||
|
CGFloat progress = ({
|
||||||
|
CGFloat minTranslation = CGRectGetHeight(self.pendingAttributes.targetBounds) - CGRectGetHeight(self.pendingAttributes.sourceBounds);
|
||||||
|
translation = MAX(minTranslation, translation);
|
||||||
|
translation = MIN(0, translation);
|
||||||
|
CGFloat progress = translation/minTranslation;
|
||||||
|
progress;
|
||||||
|
});
|
||||||
|
[self performAlphaAnimationWithProgress:progress];
|
||||||
|
[self performPathAnimationWithProgress:progress];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FSCalendarTransitionWeekToMonth: {
|
||||||
|
CGFloat progress = ({
|
||||||
|
CGFloat maxTranslation = CGRectGetHeight(self.pendingAttributes.targetBounds) - CGRectGetHeight(self.pendingAttributes.sourceBounds);
|
||||||
|
translation = MIN(maxTranslation, translation);
|
||||||
|
translation = MAX(0, translation);
|
||||||
|
CGFloat progress = translation/maxTranslation;
|
||||||
|
progress;
|
||||||
|
});
|
||||||
|
[self performAlphaAnimationWithProgress:progress];
|
||||||
|
[self performPathAnimationWithProgress:progress];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
[CATransaction commit];
|
||||||
|
self.lastTranslation = translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)scopeTransitionDidEnd:(UIPanGestureRecognizer *)panGesture
|
||||||
|
{
|
||||||
|
if (self.state != FSCalendarTransitionStateChanging) return;
|
||||||
|
|
||||||
|
self.state = FSCalendarTransitionStateFinishing;
|
||||||
|
|
||||||
|
CGFloat translation = [panGesture translationInView:panGesture.view].y;
|
||||||
|
CGFloat velocity = [panGesture velocityInView:panGesture.view].y;
|
||||||
|
|
||||||
|
switch (self.transition) {
|
||||||
|
case FSCalendarTransitionMonthToWeek: {
|
||||||
|
CGFloat progress = ({
|
||||||
|
CGFloat minTranslation = CGRectGetHeight(self.pendingAttributes.targetBounds) - CGRectGetHeight(self.pendingAttributes.sourceBounds);
|
||||||
|
translation = MAX(minTranslation, translation);
|
||||||
|
translation = MIN(0, translation);
|
||||||
|
CGFloat progress = translation/minTranslation;
|
||||||
|
progress;
|
||||||
|
});
|
||||||
|
if (velocity >= 0) {
|
||||||
|
[self performBackwardTransition:self.transition fromProgress:progress];
|
||||||
|
} else {
|
||||||
|
[self performForwardTransition:self.transition fromProgress:progress];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FSCalendarTransitionWeekToMonth: {
|
||||||
|
CGFloat progress = ({
|
||||||
|
CGFloat maxTranslation = CGRectGetHeight(self.pendingAttributes.targetBounds) - CGRectGetHeight(self.pendingAttributes.sourceBounds);
|
||||||
|
translation = MAX(0, translation);
|
||||||
|
translation = MIN(maxTranslation, translation);
|
||||||
|
CGFloat progress = translation/maxTranslation;
|
||||||
|
progress;
|
||||||
|
});
|
||||||
|
if (velocity >= 0) {
|
||||||
|
[self performForwardTransition:self.transition fromProgress:progress];
|
||||||
|
} else {
|
||||||
|
[self performBackwardTransition:self.transition fromProgress:progress];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Public methods
|
||||||
|
|
||||||
|
- (void)performScopeTransitionFromScope:(FSCalendarScope)fromScope toScope:(FSCalendarScope)toScope animated:(BOOL)animated
|
||||||
|
{
|
||||||
|
if (fromScope == toScope) return;
|
||||||
|
|
||||||
|
self.transition = ({
|
||||||
|
FSCalendarTransition transition = FSCalendarTransitionNone;
|
||||||
|
if (fromScope == FSCalendarScopeMonth && toScope == FSCalendarScopeWeek) {
|
||||||
|
transition = FSCalendarTransitionMonthToWeek;
|
||||||
|
} else if (fromScope == FSCalendarScopeWeek && toScope == FSCalendarScopeMonth) {
|
||||||
|
transition = FSCalendarTransitionWeekToMonth;
|
||||||
|
}
|
||||||
|
transition;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start transition
|
||||||
|
self.state = FSCalendarTransitionStateFinishing;
|
||||||
|
FSCalendarTransitionAttributes *attr = self.transitionAttributes;
|
||||||
|
self.pendingAttributes = attr;
|
||||||
|
|
||||||
|
switch (self.transition) {
|
||||||
|
|
||||||
|
case FSCalendarTransitionMonthToWeek: {
|
||||||
|
|
||||||
|
[self.calendar fs_setVariable:attr.targetPage forKey:@"_currentPage"];
|
||||||
|
self.calendar.contentView.clipsToBounds = YES;
|
||||||
|
|
||||||
|
if (animated) {
|
||||||
|
CGFloat duration = 0.3;
|
||||||
|
|
||||||
|
[self performAlphaAnimationFrom:1 to:0 duration:0.22 exception:attr.focusedRowNumber completion:^{
|
||||||
|
[self performTransitionCompletionAnimated:animated];
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (self.calendar.delegate && ([self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)] || [self.calendar.delegate respondsToSelector:@selector(calendarCurrentScopeWillChange:animated:)])) {
|
||||||
|
[UIView beginAnimations:nil context:nil];
|
||||||
|
[UIView setAnimationsEnabled:YES];
|
||||||
|
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
|
||||||
|
[UIView setAnimationDuration:duration];
|
||||||
|
self.collectionView.fs_top = -attr.focusedRowNumber*self.calendar.collectionViewLayout.estimatedItemSize.height;
|
||||||
|
[self boundingRectWillChange:attr.targetBounds animated:animated];
|
||||||
|
[UIView commitAnimations];
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
[self performTransitionCompletionAnimated:animated];
|
||||||
|
[self boundingRectWillChange:attr.targetBounds animated:animated];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case FSCalendarTransitionWeekToMonth: {
|
||||||
|
|
||||||
|
[self.calendar fs_setVariable:attr.targetPage forKey:@"_currentPage"];
|
||||||
|
|
||||||
|
[self prelayoutForWeekToMonthTransition];
|
||||||
|
|
||||||
|
if (animated) {
|
||||||
|
|
||||||
|
CGFloat duration = 0.3;
|
||||||
|
|
||||||
|
[self performAlphaAnimationFrom:0 to:1 duration:duration exception:attr.focusedRowNumber completion:^{
|
||||||
|
[self performTransitionCompletionAnimated:animated];
|
||||||
|
}];
|
||||||
|
|
||||||
|
[CATransaction begin];
|
||||||
|
[CATransaction setDisableActions:NO];
|
||||||
|
if (self.calendar.delegate && ([self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)] || [self.calendar.delegate respondsToSelector:@selector(calendarCurrentScopeWillChange:animated:)])) {
|
||||||
|
self.collectionView.fs_top = -attr.focusedRowNumber*self.calendar.collectionViewLayout.estimatedItemSize.height;
|
||||||
|
[UIView beginAnimations:nil context:nil];
|
||||||
|
[UIView setAnimationsEnabled:YES];
|
||||||
|
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
|
||||||
|
[UIView setAnimationDuration:duration];
|
||||||
|
self.collectionView.fs_top = 0;
|
||||||
|
[self boundingRectWillChange:attr.targetBounds animated:animated];
|
||||||
|
[UIView commitAnimations];
|
||||||
|
}
|
||||||
|
[CATransaction commit];
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
[self performTransitionCompletionAnimated:animated];
|
||||||
|
[self boundingRectWillChange:attr.targetBounds animated:animated];
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)performBoundingRectTransitionFromMonth:(NSDate *)fromMonth toMonth:(NSDate *)toMonth duration:(CGFloat)duration
|
||||||
|
{
|
||||||
|
if (self.calendar.scope != FSCalendarScopeMonth) return;
|
||||||
|
NSInteger lastRowCount = [self.calendar.calculator numberOfRowsInMonth:fromMonth];
|
||||||
|
NSInteger currentRowCount = [self.calendar.calculator numberOfRowsInMonth:toMonth];
|
||||||
|
if (lastRowCount != currentRowCount) {
|
||||||
|
CGFloat animationDuration = duration;
|
||||||
|
CGRect bounds = (CGRect){CGPointZero,[self.calendar sizeThatFits:self.calendar.frame.size scope:FSCalendarScopeMonth]};
|
||||||
|
self.state = FSCalendarTransitionStateChanging;
|
||||||
|
void (^completion)(BOOL) = ^(BOOL finished) {
|
||||||
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(MAX(0, duration-animationDuration) * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||||
|
self.calendar.needsAdjustingViewFrame = YES;
|
||||||
|
[self.calendar setNeedsLayout];
|
||||||
|
self.state = FSCalendarTransitionStateIdle;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
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
|
||||||
|
{
|
||||||
|
[self performTransitionCompletion:self.transition animated:animated];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)performTransitionCompletion:(FSCalendarTransition)transition animated:(BOOL)animated
|
||||||
|
{
|
||||||
|
switch (transition) {
|
||||||
|
case FSCalendarTransitionMonthToWeek: {
|
||||||
|
[self.calendar.visibleCells enumerateObjectsUsingBlock:^(UICollectionViewCell *obj, NSUInteger idx, BOOL * stop) {
|
||||||
|
obj.contentView.layer.opacity = 1;
|
||||||
|
}];
|
||||||
|
self.collectionViewLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||||
|
self.calendar.calendarHeaderView.scrollDirection = self.collectionViewLayout.scrollDirection;
|
||||||
|
self.calendar.needsAdjustingViewFrame = YES;
|
||||||
|
[self.collectionView reloadData];
|
||||||
|
[self.calendar.calendarHeaderView reloadData];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FSCalendarTransitionWeekToMonth: {
|
||||||
|
self.calendar.needsAdjustingViewFrame = YES;
|
||||||
|
[self.calendar.visibleCells enumerateObjectsUsingBlock:^(UICollectionViewCell *obj, NSUInteger idx, BOOL * stop) {
|
||||||
|
[CATransaction begin];
|
||||||
|
[CATransaction setDisableActions:YES];
|
||||||
|
obj.contentView.layer.opacity = 1;
|
||||||
|
[CATransaction commit];
|
||||||
|
[obj.contentView.layer removeAnimationForKey:@"opacity"];
|
||||||
|
}];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
self.state = FSCalendarTransitionStateIdle;
|
||||||
|
self.transition = FSCalendarTransitionNone;
|
||||||
|
self.calendar.contentView.clipsToBounds = NO;
|
||||||
|
self.pendingAttributes = nil;
|
||||||
|
[self.calendar setNeedsLayout];
|
||||||
|
[self.calendar layoutIfNeeded];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (FSCalendarTransitionAttributes *)transitionAttributes
|
||||||
|
{
|
||||||
|
FSCalendarTransitionAttributes *attributes = [[FSCalendarTransitionAttributes alloc] init];
|
||||||
|
attributes.sourceBounds = self.calendar.bounds;
|
||||||
|
attributes.sourcePage = self.calendar.currentPage;
|
||||||
|
switch (self.transition) {
|
||||||
|
|
||||||
|
case FSCalendarTransitionMonthToWeek: {
|
||||||
|
|
||||||
|
NSDate *focusedDate = ({
|
||||||
|
NSArray<NSDate *> *candidates = ({
|
||||||
|
NSMutableArray *dates = self.calendar.selectedDates.reverseObjectEnumerator.allObjects.mutableCopy;
|
||||||
|
if (self.calendar.today) {
|
||||||
|
[dates addObject:self.calendar.today];
|
||||||
|
}
|
||||||
|
if (self.calendar.currentPage) {
|
||||||
|
[dates addObject:self.calendar.currentPage];
|
||||||
|
}
|
||||||
|
dates.copy;
|
||||||
|
});
|
||||||
|
NSArray<NSDate *> *visibleCandidates = [candidates filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSDate * _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
|
||||||
|
NSIndexPath *indexPath = [self.calendar.calculator indexPathForDate:evaluatedObject scope:FSCalendarScopeMonth];
|
||||||
|
NSInteger currentSection = [self.calendar.calculator indexPathForDate:self.calendar.currentPage scope:FSCalendarScopeMonth].section;
|
||||||
|
return indexPath.section == currentSection;
|
||||||
|
}]];
|
||||||
|
NSDate *date = visibleCandidates.firstObject;
|
||||||
|
date;
|
||||||
|
});
|
||||||
|
NSInteger focusedRow = [self.calendar.calculator coordinateForIndexPath:[self.calendar.calculator indexPathForDate:focusedDate scope:FSCalendarScopeMonth]].row;
|
||||||
|
|
||||||
|
NSDate *currentPage = self.calendar.currentPage;
|
||||||
|
NSIndexPath *indexPath = [self.calendar.calculator indexPathForDate:currentPage scope:FSCalendarScopeMonth];
|
||||||
|
NSDate *monthHead = [self.calendar.calculator monthHeadForSection:indexPath.section];
|
||||||
|
NSDate *targetPage = [self.calendar.gregorian dateByAddingUnit:NSCalendarUnitDay value:focusedRow*7 toDate:monthHead options:0];
|
||||||
|
|
||||||
|
attributes.focusedRowNumber = focusedRow;
|
||||||
|
attributes.focusedDate = focusedDate;
|
||||||
|
attributes.targetPage = targetPage;
|
||||||
|
attributes.targetBounds = [self boundingRectForScope:FSCalendarScopeWeek page:attributes.targetPage];
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FSCalendarTransitionWeekToMonth: {
|
||||||
|
|
||||||
|
NSInteger focusedRow = 0;
|
||||||
|
NSDate *currentPage = self.calendar.currentPage;
|
||||||
|
|
||||||
|
NSDate *focusedDate = ({
|
||||||
|
NSArray<NSDate *> *candidates = ({
|
||||||
|
NSMutableArray *dates = self.calendar.selectedDates.reverseObjectEnumerator.allObjects.mutableCopy;
|
||||||
|
if (self.calendar.today) {
|
||||||
|
[dates addObject:self.calendar.today];
|
||||||
|
}
|
||||||
|
if (self.calendar.currentPage) {
|
||||||
|
[dates addObject:[self.calendar.gregorian fs_lastDayOfWeek:currentPage]];
|
||||||
|
}
|
||||||
|
dates.copy;
|
||||||
|
});
|
||||||
|
NSArray<NSDate *> *visibleCandidates = [candidates filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSDate * _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
|
||||||
|
NSIndexPath *indexPath = [self.calendar.calculator indexPathForDate:evaluatedObject scope:FSCalendarScopeWeek];
|
||||||
|
NSInteger currentSection = [self.calendar.calculator indexPathForDate:self.calendar.currentPage scope:FSCalendarScopeWeek].section;
|
||||||
|
return indexPath.section == currentSection;
|
||||||
|
}]];
|
||||||
|
NSDate *date = visibleCandidates.firstObject;
|
||||||
|
date;
|
||||||
|
});
|
||||||
|
|
||||||
|
NSDate *firstDayOfMonth = [self.calendar.gregorian fs_firstDayOfMonth:focusedDate];
|
||||||
|
attributes.focusedDate = focusedDate;
|
||||||
|
firstDayOfMonth = firstDayOfMonth ?: [self.calendar.gregorian fs_firstDayOfMonth:currentPage];
|
||||||
|
NSInteger numberOfPlaceholdersForPrev = [self.calendar.calculator numberOfHeadPlaceholdersForMonth:firstDayOfMonth];
|
||||||
|
NSDate *firstDateOfPage = [self.calendar.gregorian dateByAddingUnit:NSCalendarUnitDay value:-numberOfPlaceholdersForPrev toDate:firstDayOfMonth options:0];
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
NSDate *currentRow = [self.calendar.gregorian dateByAddingUnit:NSCalendarUnitWeekOfYear value:i toDate:firstDateOfPage options:0];
|
||||||
|
if ([self.calendar.gregorian isDate:currentRow inSameDayAsDate:currentPage]) {
|
||||||
|
focusedRow = i;
|
||||||
|
currentPage = firstDayOfMonth;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attributes.focusedRowNumber = focusedRow;
|
||||||
|
attributes.targetPage = currentPage;
|
||||||
|
attributes.firstDayOfMonth = firstDayOfMonth;
|
||||||
|
|
||||||
|
attributes.targetBounds = [self boundingRectForScope:FSCalendarScopeMonth page:attributes.targetPage];
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private properties
|
||||||
|
|
||||||
|
- (FSCalendarScope)representingScope
|
||||||
|
{
|
||||||
|
switch (self.state) {
|
||||||
|
case FSCalendarTransitionStateIdle: {
|
||||||
|
return self.calendar.scope;
|
||||||
|
}
|
||||||
|
case FSCalendarTransitionStateChanging:
|
||||||
|
case FSCalendarTransitionStateFinishing: {
|
||||||
|
return FSCalendarScopeMonth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private methods
|
||||||
|
|
||||||
|
- (CGRect)boundingRectForScope:(FSCalendarScope)scope page:(NSDate *)page
|
||||||
|
{
|
||||||
|
CGSize contentSize;
|
||||||
|
switch (scope) {
|
||||||
|
case FSCalendarScopeMonth: {
|
||||||
|
if (self.calendar.placeholderType == FSCalendarPlaceholderTypeFillSixRows) {
|
||||||
|
contentSize = self.cachedMonthSize;
|
||||||
|
} else {
|
||||||
|
CGFloat padding = self.calendar.collectionViewLayout.sectionInsets.top + self.calendar.collectionViewLayout.sectionInsets.bottom;
|
||||||
|
contentSize = CGSizeMake(self.calendar.fs_width,
|
||||||
|
self.calendar.preferredHeaderHeight+
|
||||||
|
self.calendar.preferredWeekdayHeight+
|
||||||
|
([self.calendar.calculator numberOfRowsInMonth:page]*self.calendar.collectionViewLayout.estimatedItemSize.height)+
|
||||||
|
self.calendar.scopeHandle.fs_height+padding);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FSCalendarScopeWeek: {
|
||||||
|
contentSize = [self.calendar sizeThatFits:self.calendar.frame.size scope:scope];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (CGRect){CGPointZero,contentSize};
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)boundingRectWillChange:(CGRect)targetBounds animated:(BOOL)animated
|
||||||
|
{
|
||||||
|
self.calendar.scopeHandle.fs_bottom = CGRectGetMaxY(targetBounds);
|
||||||
|
self.calendar.contentView.fs_height = CGRectGetHeight(targetBounds)-self.calendar.scopeHandle.fs_height;
|
||||||
|
self.calendar.daysContainer.fs_height = CGRectGetHeight(targetBounds)-self.calendar.preferredHeaderHeight-self.calendar.preferredWeekdayHeight-self.calendar.scopeHandle.fs_height;
|
||||||
|
[[self.calendar valueForKey:@"delegateProxy"] calendar:self.calendar boundingRectWillChange:targetBounds animated:animated];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)performForwardTransition:(FSCalendarTransition)transition fromProgress:(CGFloat)progress
|
||||||
|
{
|
||||||
|
FSCalendarTransitionAttributes *attr = self.pendingAttributes;
|
||||||
|
switch (transition) {
|
||||||
|
case FSCalendarTransitionMonthToWeek: {
|
||||||
|
|
||||||
|
[self.calendar willChangeValueForKey:@"scope"];
|
||||||
|
[self.calendar fs_setUnsignedIntegerVariable:FSCalendarScopeWeek forKey:@"_scope"];
|
||||||
|
[self.calendar didChangeValueForKey:@"scope"];
|
||||||
|
|
||||||
|
[self.calendar fs_setVariable:attr.targetPage forKey:@"_currentPage"];
|
||||||
|
|
||||||
|
self.calendar.contentView.clipsToBounds = YES;
|
||||||
|
|
||||||
|
CGFloat currentAlpha = MAX(1-progress*1.1,0);
|
||||||
|
CGFloat duration = 0.3;
|
||||||
|
[self performAlphaAnimationFrom:currentAlpha to:0 duration:0.22 exception:attr.focusedRowNumber completion:^{
|
||||||
|
[self performTransitionCompletionAnimated:YES];
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (self.calendar.delegate && ([self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)] || [self.calendar.delegate respondsToSelector:@selector(calendarCurrentScopeWillChange:animated:)])) {
|
||||||
|
[UIView beginAnimations:@"delegateTranslation" context:"translation"];
|
||||||
|
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
|
||||||
|
[UIView setAnimationDuration:duration];
|
||||||
|
self.collectionView.fs_top = -attr.focusedRowNumber*self.calendar.collectionViewLayout.estimatedItemSize.height;
|
||||||
|
[self boundingRectWillChange:attr.targetBounds animated:YES];
|
||||||
|
[UIView commitAnimations];
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FSCalendarTransitionWeekToMonth: {
|
||||||
|
|
||||||
|
[self.calendar willChangeValueForKey:@"scope"];
|
||||||
|
[self.calendar fs_setUnsignedIntegerVariable:FSCalendarScopeMonth forKey:@"_scope"];
|
||||||
|
[self.calendar didChangeValueForKey:@"scope"];
|
||||||
|
|
||||||
|
[self performAlphaAnimationFrom:progress to:1 duration:0.4 exception:attr.focusedRowNumber completion:^{
|
||||||
|
[self performTransitionCompletionAnimated:YES];
|
||||||
|
}];
|
||||||
|
|
||||||
|
CGFloat duration = 0.3;
|
||||||
|
[CATransaction begin];
|
||||||
|
[CATransaction setDisableActions:NO];
|
||||||
|
|
||||||
|
if (self.calendar.delegate && ([self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)] || [self.calendar.delegate respondsToSelector:@selector(calendarCurrentScopeWillChange:animated:)])) {
|
||||||
|
[UIView beginAnimations:@"delegateTranslation" context:"translation"];
|
||||||
|
[UIView setAnimationsEnabled:YES];
|
||||||
|
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
|
||||||
|
[UIView setAnimationDuration:duration];
|
||||||
|
self.collectionView.fs_top = 0;
|
||||||
|
[self boundingRectWillChange:attr.targetBounds animated:YES];
|
||||||
|
[UIView commitAnimations];
|
||||||
|
}
|
||||||
|
[CATransaction commit];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)performBackwardTransition:(FSCalendarTransition)transition fromProgress:(CGFloat)progress
|
||||||
|
{
|
||||||
|
switch (transition) {
|
||||||
|
case FSCalendarTransitionMonthToWeek: {
|
||||||
|
|
||||||
|
[self.calendar willChangeValueForKey:@"scope"];
|
||||||
|
[self.calendar fs_setUnsignedIntegerVariable:FSCalendarScopeMonth forKey:@"_scope"];
|
||||||
|
[self.calendar didChangeValueForKey:@"scope"];
|
||||||
|
|
||||||
|
[self performAlphaAnimationFrom:MAX(1-progress*1.1,0) to:1 duration:0.3 exception:self.pendingAttributes.focusedRowNumber completion:^{
|
||||||
|
[self.calendar.visibleCells enumerateObjectsUsingBlock:^(__kindof UICollectionViewCell * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||||
|
obj.contentView.layer.opacity = 1;
|
||||||
|
[obj.contentView.layer removeAnimationForKey:@"opacity"];
|
||||||
|
}];
|
||||||
|
self.pendingAttributes = nil;
|
||||||
|
self.state = FSCalendarTransitionStateIdle;
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (self.calendar.delegate && ([self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)] || [self.calendar.delegate respondsToSelector:@selector(calendarCurrentScopeWillChange:animated:)])) {
|
||||||
|
[UIView beginAnimations:@"delegateTranslation" context:"translation"];
|
||||||
|
[UIView setAnimationsEnabled:YES];
|
||||||
|
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
|
||||||
|
[UIView setAnimationDuration:0.3];
|
||||||
|
self.collectionView.fs_top = 0;
|
||||||
|
[self boundingRectWillChange:self.pendingAttributes.sourceBounds animated:YES];
|
||||||
|
[UIView commitAnimations];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FSCalendarTransitionWeekToMonth: {
|
||||||
|
|
||||||
|
[self.calendar willChangeValueForKey:@"scope"];
|
||||||
|
[self.calendar fs_setUnsignedIntegerVariable:FSCalendarScopeWeek forKey:@"_scope"];
|
||||||
|
[self.calendar didChangeValueForKey:@"scope"];
|
||||||
|
|
||||||
|
[self performAlphaAnimationFrom:progress to:0 duration:0.3 exception:self.pendingAttributes.focusedRowNumber completion:^{
|
||||||
|
[self.calendar fs_setVariable:self.pendingAttributes.sourcePage forKey:@"_currentPage"];
|
||||||
|
[self performTransitionCompletion:FSCalendarTransitionMonthToWeek animated:YES];
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (self.calendar.delegate && ([self.calendar.delegate respondsToSelector:@selector(calendar:boundingRectWillChange:animated:)] || [self.calendar.delegate respondsToSelector:@selector(calendarCurrentScopeWillChange:animated:)])) {
|
||||||
|
[UIView beginAnimations:@"delegateTranslation" context:"translation"];
|
||||||
|
[UIView setAnimationsEnabled:YES];
|
||||||
|
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
|
||||||
|
[UIView setAnimationDuration:0.3];
|
||||||
|
self.collectionView.fs_top = (-self.pendingAttributes.focusedRowNumber*self.calendar.collectionViewLayout.estimatedItemSize.height);
|
||||||
|
[self boundingRectWillChange:self.pendingAttributes.sourceBounds animated:YES];
|
||||||
|
[UIView commitAnimations];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)performAlphaAnimationFrom:(CGFloat)fromAlpha to:(CGFloat)toAlpha duration:(CGFloat)duration exception:(NSInteger)exception completion:(void(^)())completion;
|
||||||
|
{
|
||||||
|
[self.calendar.visibleCells enumerateObjectsUsingBlock:^(FSCalendarCell *cell, NSUInteger idx, BOOL *stop) {
|
||||||
|
if (CGRectContainsPoint(self.collectionView.bounds, cell.center)) {
|
||||||
|
BOOL shouldPerformAlpha = NO;
|
||||||
|
NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];
|
||||||
|
NSInteger row = [self.calendar.calculator coordinateForIndexPath:indexPath].row;
|
||||||
|
shouldPerformAlpha = row != exception;
|
||||||
|
if (shouldPerformAlpha) {
|
||||||
|
CABasicAnimation *opacity = [CABasicAnimation animationWithKeyPath:@"opacity"];
|
||||||
|
opacity.duration = duration;
|
||||||
|
opacity.fromValue = @(fromAlpha);
|
||||||
|
opacity.toValue = @(toAlpha);
|
||||||
|
opacity.removedOnCompletion = NO;
|
||||||
|
opacity.fillMode = kCAFillModeForwards;
|
||||||
|
[cell.contentView.layer addAnimation:opacity forKey:@"opacity"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
if (completion) {
|
||||||
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||||
|
completion();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)performAlphaAnimationWithProgress:(CGFloat)progress
|
||||||
|
{
|
||||||
|
CGFloat opacity = self.transition == FSCalendarTransitionMonthToWeek ? MAX((1-progress*1.1),0) : progress;
|
||||||
|
[self.calendar.visibleCells enumerateObjectsUsingBlock:^(FSCalendarCell *cell, NSUInteger idx, BOOL *stop) {
|
||||||
|
if (CGRectContainsPoint(self.collectionView.bounds, cell.center)) {
|
||||||
|
BOOL shouldPerformAlpha = NO;
|
||||||
|
NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];
|
||||||
|
NSInteger row = [self.calendar.calculator coordinateForIndexPath:indexPath].row;
|
||||||
|
shouldPerformAlpha = row != self.pendingAttributes.focusedRowNumber;
|
||||||
|
if (shouldPerformAlpha) {
|
||||||
|
cell.contentView.layer.opacity = opacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)performPathAnimationWithProgress:(CGFloat)progress
|
||||||
|
{
|
||||||
|
CGFloat targetHeight = CGRectGetHeight(self.pendingAttributes.targetBounds);
|
||||||
|
CGFloat sourceHeight = CGRectGetHeight(self.pendingAttributes.sourceBounds);
|
||||||
|
CGFloat currentHeight = sourceHeight - (sourceHeight-targetHeight)*progress - self.calendar.scopeHandle.fs_height;
|
||||||
|
CGRect currentBounds = CGRectMake(0, 0, CGRectGetWidth(self.pendingAttributes.targetBounds), currentHeight+self.calendar.scopeHandle.fs_height);
|
||||||
|
self.collectionView.fs_top = (-self.pendingAttributes.focusedRowNumber*self.calendar.collectionViewLayout.estimatedItemSize.height)*(self.transition == FSCalendarTransitionMonthToWeek?progress:(1-progress));
|
||||||
|
[self boundingRectWillChange:currentBounds animated:NO];
|
||||||
|
if (self.transition == FSCalendarTransitionWeekToMonth) {
|
||||||
|
self.calendar.contentView.fs_height = targetHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (void)prelayoutForWeekToMonthTransition
|
||||||
|
{
|
||||||
|
self.calendar.contentView.clipsToBounds = YES;
|
||||||
|
self.calendar.contentView.fs_height = CGRectGetHeight(self.pendingAttributes.targetBounds)-self.calendar.scopeHandle.fs_height;
|
||||||
|
self.collectionViewLayout.scrollDirection = (UICollectionViewScrollDirection)self.calendar.scrollDirection;
|
||||||
|
self.calendar.calendarHeaderView.scrollDirection = self.collectionViewLayout.scrollDirection;
|
||||||
|
self.calendar.needsAdjustingViewFrame = YES;
|
||||||
|
[self.calendar setNeedsLayout];
|
||||||
|
[self.collectionView reloadData];
|
||||||
|
[self.calendar.calendarHeaderView reloadData];
|
||||||
|
[self.calendar layoutIfNeeded];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FSCalendarTransitionAttributes
|
||||||
|
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
//
|
||||||
|
// FSCalendarWeekdayView.h
|
||||||
|
// FSCalendar
|
||||||
|
//
|
||||||
|
// Created by dingwenchao on 03/11/2016.
|
||||||
|
// Copyright © 2016 dingwenchao. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@class FSCalendar;
|
||||||
|
|
||||||
|
@interface FSCalendarWeekdayView : UIView
|
||||||
|
|
||||||
|
/**
|
||||||
|
An array of UILabel objects displaying the weekday symbols.
|
||||||
|
*/
|
||||||
|
@property (readonly, nonatomic) NSArray<UILabel *> *weekdayLabels;
|
||||||
|
|
||||||
|
- (void)configureAppearance;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,109 @@
|
||||||
|
//
|
||||||
|
// FSCalendarWeekdayView.m
|
||||||
|
// FSCalendar
|
||||||
|
//
|
||||||
|
// Created by dingwenchao on 03/11/2016.
|
||||||
|
// Copyright © 2016 Wenchao Ding. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "FSCalendarWeekdayView.h"
|
||||||
|
#import "FSCalendar.h"
|
||||||
|
#import "FSCalendarDynamicHeader.h"
|
||||||
|
#import "FSCalendarExtensions.h"
|
||||||
|
|
||||||
|
@interface FSCalendarWeekdayView()
|
||||||
|
|
||||||
|
@property (strong, nonatomic) NSPointerArray *weekdayPointers;
|
||||||
|
@property (weak , nonatomic) UIView *contentView;
|
||||||
|
@property (weak , nonatomic) FSCalendar *calendar;
|
||||||
|
|
||||||
|
- (void)commonInit;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FSCalendarWeekdayView
|
||||||
|
|
||||||
|
- (instancetype)initWithFrame:(CGRect)frame
|
||||||
|
{
|
||||||
|
self = [super initWithFrame:frame];
|
||||||
|
if (self) {
|
||||||
|
[self commonInit];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithCoder:(NSCoder *)coder
|
||||||
|
{
|
||||||
|
self = [super initWithCoder:coder];
|
||||||
|
if (self) {
|
||||||
|
[self commonInit];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)commonInit
|
||||||
|
{
|
||||||
|
UIView *contentView = [[UIView alloc] initWithFrame:CGRectZero];
|
||||||
|
[self addSubview:contentView];
|
||||||
|
_contentView = contentView;
|
||||||
|
|
||||||
|
_weekdayPointers = [NSPointerArray weakObjectsPointerArray];
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
UILabel *weekdayLabel = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||||
|
weekdayLabel.textAlignment = NSTextAlignmentCenter;
|
||||||
|
[self.contentView addSubview:weekdayLabel];
|
||||||
|
[_weekdayPointers addPointer:(__bridge void * _Nullable)(weekdayLabel)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)layoutSubviews
|
||||||
|
{
|
||||||
|
[super layoutSubviews];
|
||||||
|
|
||||||
|
self.contentView.frame = self.bounds;
|
||||||
|
|
||||||
|
// Position Calculation
|
||||||
|
NSInteger count = self.weekdayPointers.count;
|
||||||
|
size_t size = sizeof(CGFloat)*count;
|
||||||
|
CGFloat *widths = malloc(size);
|
||||||
|
CGFloat contentWidth = self.contentView.fs_width;
|
||||||
|
FSCalendarSliceCake(contentWidth, count, widths);
|
||||||
|
|
||||||
|
CGFloat x = 0;
|
||||||
|
for (NSInteger i = 0; i < count; i++) {
|
||||||
|
CGFloat width = widths[i];
|
||||||
|
UILabel *label = [self.weekdayPointers pointerAtIndex:i];
|
||||||
|
label.frame = CGRectMake(x, 0, width, self.contentView.fs_height);
|
||||||
|
x += width;
|
||||||
|
}
|
||||||
|
free(widths);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setCalendar:(FSCalendar *)calendar
|
||||||
|
{
|
||||||
|
_calendar = calendar;
|
||||||
|
[self configureAppearance];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray<UILabel *> *)weekdayLabels
|
||||||
|
{
|
||||||
|
return self.weekdayPointers.allObjects;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)configureAppearance
|
||||||
|
{
|
||||||
|
BOOL useVeryShortWeekdaySymbols = (self.calendar.appearance.caseOptions & (15<<4) ) == FSCalendarCaseOptionsWeekdayUsesSingleUpperCase;
|
||||||
|
NSArray *weekdaySymbols = useVeryShortWeekdaySymbols ? self.calendar.gregorian.veryShortStandaloneWeekdaySymbols : self.calendar.gregorian.shortStandaloneWeekdaySymbols;
|
||||||
|
BOOL useDefaultWeekdayCase = (self.calendar.appearance.caseOptions & (15<<4) ) == FSCalendarCaseOptionsWeekdayUsesDefaultCase;
|
||||||
|
|
||||||
|
for (NSInteger i = 0; i < self.weekdayPointers.count; i++) {
|
||||||
|
NSInteger index = (i + self.calendar.firstWeekday-1) % 7;
|
||||||
|
UILabel *label = [self.weekdayPointers pointerAtIndex:i];
|
||||||
|
label.font = self.calendar.appearance.weekdayFont;
|
||||||
|
label.textColor = self.calendar.appearance.weekdayTextColor;
|
||||||
|
label.text = useDefaultWeekdayCase ? weekdaySymbols[index] : [weekdaySymbols[index] uppercaseString];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -1,79 +0,0 @@
|
||||||
//
|
|
||||||
// NSDate+FSExtension.h
|
|
||||||
// Pods
|
|
||||||
//
|
|
||||||
// Created by Wenchao Ding on 29/1/15.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This category is deprecated in this framework as it premised that the calendar should be gregorian. But feel free to use it for gregorian-only.
|
|
||||||
*/
|
|
||||||
@interface NSDate (FSExtension)
|
|
||||||
|
|
||||||
@property (readonly, nonatomic) NSInteger fs_year;
|
|
||||||
@property (readonly, nonatomic) NSInteger fs_month;
|
|
||||||
@property (readonly, nonatomic) NSInteger fs_day;
|
|
||||||
@property (readonly, nonatomic) NSInteger fs_weekday;
|
|
||||||
@property (readonly, nonatomic) NSInteger fs_weekOfYear;
|
|
||||||
@property (readonly, nonatomic) NSInteger fs_hour;
|
|
||||||
@property (readonly, nonatomic) NSInteger fs_minute;
|
|
||||||
@property (readonly, nonatomic) NSInteger fs_second;
|
|
||||||
|
|
||||||
@property (readonly, nonatomic) NSDate *fs_dateByIgnoringTimeComponents;
|
|
||||||
@property (readonly, nonatomic) NSDate *fs_firstDayOfMonth;
|
|
||||||
@property (readonly, nonatomic) NSDate *fs_lastDayOfMonth;
|
|
||||||
@property (readonly, nonatomic) NSDate *fs_firstDayOfWeek;
|
|
||||||
@property (readonly, nonatomic) NSDate *fs_middleOfWeek;
|
|
||||||
@property (readonly, nonatomic) NSDate *fs_tomorrow;
|
|
||||||
@property (readonly, nonatomic) NSDate *fs_yesterday;
|
|
||||||
@property (readonly, nonatomic) NSInteger fs_numberOfDaysInMonth;
|
|
||||||
|
|
||||||
+ (instancetype)fs_dateFromString:(NSString *)string format:(NSString *)format;
|
|
||||||
+ (instancetype)fs_dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day;
|
|
||||||
|
|
||||||
- (NSDate *)fs_dateByAddingYears:(NSInteger)years;
|
|
||||||
- (NSDate *)fs_dateBySubtractingYears:(NSInteger)years;
|
|
||||||
- (NSDate *)fs_dateByAddingMonths:(NSInteger)months;
|
|
||||||
- (NSDate *)fs_dateBySubtractingMonths:(NSInteger)months;
|
|
||||||
- (NSDate *)fs_dateByAddingWeeks:(NSInteger)weeks;
|
|
||||||
- (NSDate *)fs_dateBySubtractingWeeks:(NSInteger)weeks;
|
|
||||||
- (NSDate *)fs_dateByAddingDays:(NSInteger)days;
|
|
||||||
- (NSDate *)fs_dateBySubtractingDays:(NSInteger)days;
|
|
||||||
- (NSInteger)fs_yearsFrom:(NSDate *)date;
|
|
||||||
- (NSInteger)fs_monthsFrom:(NSDate *)date;
|
|
||||||
- (NSInteger)fs_weeksFrom:(NSDate *)date;
|
|
||||||
- (NSInteger)fs_daysFrom:(NSDate *)date;
|
|
||||||
|
|
||||||
- (BOOL)fs_isEqualToDateForMonth:(NSDate *)date;
|
|
||||||
- (BOOL)fs_isEqualToDateForWeek:(NSDate *)date;
|
|
||||||
- (BOOL)fs_isEqualToDateForDay:(NSDate *)date;
|
|
||||||
|
|
||||||
- (NSString *)fs_stringWithFormat:(NSString *)format;
|
|
||||||
- (NSString *)fs_string;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
@interface NSCalendar (FSExtension)
|
|
||||||
|
|
||||||
+ (instancetype)fs_sharedCalendar;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface NSDateFormatter (FSExtension)
|
|
||||||
|
|
||||||
+ (instancetype)fs_sharedDateFormatter;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface NSDateComponents (FSExtension)
|
|
||||||
|
|
||||||
+ (instancetype)fs_sharedDateComponents;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,344 +0,0 @@
|
||||||
//
|
|
||||||
// NSDate+FSExtension.m
|
|
||||||
// Pods
|
|
||||||
//
|
|
||||||
// Created by Wenchao Ding on 29/1/15.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "NSDate+FSExtension.h"
|
|
||||||
|
|
||||||
@implementation NSDate (FSExtension)
|
|
||||||
|
|
||||||
- (NSInteger)fs_year
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *component = [calendar components:NSCalendarUnitYear fromDate:self];
|
|
||||||
return component.year;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)fs_month
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *component = [calendar components:NSCalendarUnitMonth
|
|
||||||
fromDate:self];
|
|
||||||
return component.month;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)fs_day
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *component = [calendar components:NSCalendarUnitDay
|
|
||||||
fromDate:self];
|
|
||||||
return component.day;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)fs_weekday
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *component = [calendar components:NSCalendarUnitWeekday fromDate:self];
|
|
||||||
return component.weekday;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)fs_weekOfYear
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *component = [calendar components:NSCalendarUnitWeekOfYear fromDate:self];
|
|
||||||
return component.weekOfYear;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)fs_hour
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *component = [calendar components:NSCalendarUnitHour
|
|
||||||
fromDate:self];
|
|
||||||
return component.hour;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)fs_minute
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *component = [calendar components:NSCalendarUnitMinute
|
|
||||||
fromDate:self];
|
|
||||||
return component.minute;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)fs_second
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *component = [calendar components:NSCalendarUnitSecond
|
|
||||||
fromDate:self];
|
|
||||||
return component.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDate *)fs_dateByIgnoringTimeComponents
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:self];
|
|
||||||
return [calendar dateFromComponents:components];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDate *)fs_firstDayOfMonth
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth| NSCalendarUnitDay fromDate:self];
|
|
||||||
components.day = 1;
|
|
||||||
return [calendar dateFromComponents:components];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDate *)fs_lastDayOfMonth
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:self];
|
|
||||||
components.month++;
|
|
||||||
components.day = 0;
|
|
||||||
return [calendar dateFromComponents:components];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDate *)fs_firstDayOfWeek
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *weekdayComponents = [calendar components:NSCalendarUnitWeekday fromDate:self];
|
|
||||||
NSDateComponents *componentsToSubtract = [NSDateComponents fs_sharedDateComponents];
|
|
||||||
componentsToSubtract.day = - (weekdayComponents.weekday - calendar.firstWeekday);
|
|
||||||
NSDate *beginningOfWeek = [calendar dateByAddingComponents:componentsToSubtract toDate:self options:0];
|
|
||||||
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:beginningOfWeek];
|
|
||||||
beginningOfWeek = [calendar dateFromComponents:components];
|
|
||||||
componentsToSubtract.day = NSIntegerMax;
|
|
||||||
return beginningOfWeek;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDate *)fs_middleOfWeek
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *weekdayComponents = [calendar components:NSCalendarUnitWeekday fromDate:self];
|
|
||||||
NSDateComponents *componentsToSubtract = [NSDateComponents fs_sharedDateComponents];
|
|
||||||
componentsToSubtract.day = - (weekdayComponents.weekday - calendar.firstWeekday) + 3;
|
|
||||||
NSDate *middleOfWeek = [calendar dateByAddingComponents:componentsToSubtract toDate:self options:0];
|
|
||||||
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:middleOfWeek];
|
|
||||||
middleOfWeek = [calendar dateFromComponents:components];
|
|
||||||
componentsToSubtract.day = NSIntegerMax;
|
|
||||||
return middleOfWeek;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDate *)fs_tomorrow
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:self];
|
|
||||||
components.day++;
|
|
||||||
return [calendar dateFromComponents:components];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDate *)fs_yesterday
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:self];
|
|
||||||
components.day--;
|
|
||||||
return [calendar dateFromComponents:components];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)fs_numberOfDaysInMonth
|
|
||||||
{
|
|
||||||
NSCalendar *c = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSRange days = [c rangeOfUnit:NSCalendarUnitDay
|
|
||||||
inUnit:NSCalendarUnitMonth
|
|
||||||
forDate:self];
|
|
||||||
return days.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (instancetype)fs_dateFromString:(NSString *)string format:(NSString *)format
|
|
||||||
{
|
|
||||||
NSDateFormatter *formatter = [NSDateFormatter fs_sharedDateFormatter];
|
|
||||||
formatter.dateFormat = format;
|
|
||||||
return [formatter dateFromString:string];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (instancetype)fs_dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *components = [NSDateComponents fs_sharedDateComponents];
|
|
||||||
components.year = year;
|
|
||||||
components.month = month;
|
|
||||||
components.day = day;
|
|
||||||
NSDate *date = [calendar dateFromComponents:components];
|
|
||||||
components.year = NSIntegerMax;
|
|
||||||
components.month = NSIntegerMax;
|
|
||||||
components.day = NSIntegerMax;
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDate *)fs_dateByAddingYears:(NSInteger)years
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *components = [NSDateComponents fs_sharedDateComponents];
|
|
||||||
components.year = years;
|
|
||||||
NSDate *date = [calendar dateByAddingComponents:components toDate:self options:0];
|
|
||||||
components.year = NSIntegerMax;
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDate *)fs_dateBySubtractingYears:(NSInteger)years
|
|
||||||
{
|
|
||||||
return [self fs_dateByAddingYears:-years];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDate *)fs_dateByAddingMonths:(NSInteger)months
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *components = [NSDateComponents fs_sharedDateComponents];
|
|
||||||
components.month = months;
|
|
||||||
NSDate *date = [calendar dateByAddingComponents:components toDate:self options:0];
|
|
||||||
components.month = NSIntegerMax;
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDate *)fs_dateBySubtractingMonths:(NSInteger)months
|
|
||||||
{
|
|
||||||
return [self fs_dateByAddingMonths:-months];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDate *)fs_dateByAddingWeeks:(NSInteger)weeks
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *components = [NSDateComponents fs_sharedDateComponents];
|
|
||||||
components.weekOfYear = weeks;
|
|
||||||
NSDate *date = [calendar dateByAddingComponents:components toDate:self options:0];
|
|
||||||
components.weekOfYear = NSIntegerMax;
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSDate *)fs_dateBySubtractingWeeks:(NSInteger)weeks
|
|
||||||
{
|
|
||||||
return [self fs_dateByAddingWeeks:-weeks];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDate *)fs_dateByAddingDays:(NSInteger)days
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *components = [NSDateComponents fs_sharedDateComponents];
|
|
||||||
components.day = days;
|
|
||||||
NSDate *date = [calendar dateByAddingComponents:components toDate:self options:0];
|
|
||||||
components.day = NSIntegerMax;
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDate *)fs_dateBySubtractingDays:(NSInteger)days
|
|
||||||
{
|
|
||||||
return [self fs_dateByAddingDays:-days];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)fs_yearsFrom:(NSDate *)date
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *components = [calendar components:NSCalendarUnitYear
|
|
||||||
fromDate:date
|
|
||||||
toDate:self
|
|
||||||
options:0];
|
|
||||||
return components.year;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)fs_monthsFrom:(NSDate *)date
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *components = [calendar components:NSCalendarUnitMonth
|
|
||||||
fromDate:date
|
|
||||||
toDate:self
|
|
||||||
options:0];
|
|
||||||
return components.month;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)fs_weeksFrom:(NSDate *)date
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *components = [calendar components:NSCalendarUnitWeekOfYear
|
|
||||||
fromDate:date
|
|
||||||
toDate:self
|
|
||||||
options:0];
|
|
||||||
return components.weekOfYear;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)fs_daysFrom:(NSDate *)date
|
|
||||||
{
|
|
||||||
NSCalendar *calendar = [NSCalendar fs_sharedCalendar];
|
|
||||||
NSDateComponents *components = [calendar components:NSCalendarUnitDay
|
|
||||||
fromDate:date
|
|
||||||
toDate:self
|
|
||||||
options:0];
|
|
||||||
return components.day;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)fs_stringWithFormat:(NSString *)format
|
|
||||||
{
|
|
||||||
NSDateFormatter *formatter = [NSDateFormatter fs_sharedDateFormatter];
|
|
||||||
formatter.dateFormat = format;
|
|
||||||
return [formatter stringFromDate:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)fs_string
|
|
||||||
{
|
|
||||||
return [self fs_stringWithFormat:@"yyyyMMdd"];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)fs_isEqualToDateForMonth:(NSDate *)date
|
|
||||||
{
|
|
||||||
return self.fs_year == date.fs_year && self.fs_month == date.fs_month;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)fs_isEqualToDateForWeek:(NSDate *)date
|
|
||||||
{
|
|
||||||
return self.fs_year == date.fs_year && self.fs_weekOfYear == date.fs_weekOfYear;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)fs_isEqualToDateForDay:(NSDate *)date
|
|
||||||
{
|
|
||||||
return self.fs_year == date.fs_year && self.fs_month == date.fs_month && self.fs_day == date.fs_day;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
@implementation NSCalendar (FSExtension)
|
|
||||||
|
|
||||||
+ (instancetype)fs_sharedCalendar
|
|
||||||
{
|
|
||||||
static id instance;
|
|
||||||
static dispatch_once_t fs_sharedCalendar_onceToken;
|
|
||||||
dispatch_once(&fs_sharedCalendar_onceToken, ^{
|
|
||||||
instance = [NSCalendar currentCalendar];
|
|
||||||
});
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
@implementation NSDateFormatter (FSExtension)
|
|
||||||
|
|
||||||
+ (instancetype)fs_sharedDateFormatter
|
|
||||||
{
|
|
||||||
static id instance;
|
|
||||||
static dispatch_once_t fs_sharedDateFormatter_onceToken;
|
|
||||||
dispatch_once(&fs_sharedDateFormatter_onceToken, ^{
|
|
||||||
instance = [[NSDateFormatter alloc] init];
|
|
||||||
});
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation NSDateComponents (FSExtension)
|
|
||||||
|
|
||||||
+ (instancetype)fs_sharedDateComponents
|
|
||||||
{
|
|
||||||
static id instance;
|
|
||||||
static dispatch_once_t fs_sharedDateFormatter_onceToken;
|
|
||||||
dispatch_once(&fs_sharedDateFormatter_onceToken, ^{
|
|
||||||
instance = [[NSDateComponents alloc] init];
|
|
||||||
});
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
//
|
|
||||||
// NSString+FSExtension.h
|
|
||||||
// FSCalendar
|
|
||||||
//
|
|
||||||
// Created by Wenchao Ding on 8/29/15.
|
|
||||||
// Copyright (c) 2015 wenchaoios. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
@interface NSString (FSExtension)
|
|
||||||
|
|
||||||
- (NSDate *)fs_dateWithFormat:(NSString *)format;
|
|
||||||
- (NSDate *)fs_date;
|
|
||||||
|
|
||||||
@end
|
|
|
@ -1,26 +0,0 @@
|
||||||
//
|
|
||||||
// NSString+FSExtension.m
|
|
||||||
// FSCalendar
|
|
||||||
//
|
|
||||||
// Created by Wenchao Ding on 8/29/15.
|
|
||||||
// Copyright (c) 2015 wenchaoios. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "NSString+FSExtension.h"
|
|
||||||
#import "NSDate+FSExtension.h"
|
|
||||||
|
|
||||||
@implementation NSString (FSExtension)
|
|
||||||
|
|
||||||
- (NSDate *)fs_dateWithFormat:(NSString *)format
|
|
||||||
{
|
|
||||||
NSDateFormatter *formatter = [NSDateFormatter fs_sharedDateFormatter];
|
|
||||||
formatter.dateFormat = format;
|
|
||||||
return [formatter dateFromString:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDate *)fs_date
|
|
||||||
{
|
|
||||||
return [self fs_dateWithFormat:@"yyyyMMdd"];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
|
@ -1,21 +0,0 @@
|
||||||
//
|
|
||||||
// UIView+FSExtension.h
|
|
||||||
// Pods
|
|
||||||
//
|
|
||||||
// Created by Wenchao Ding on 29/1/15.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
|
|
||||||
@interface UIView (FSExtension)
|
|
||||||
|
|
||||||
@property (nonatomic) CGFloat fs_width;
|
|
||||||
@property (nonatomic) CGFloat fs_height;
|
|
||||||
|
|
||||||
@property (nonatomic) CGFloat fs_top;
|
|
||||||
@property (nonatomic) CGFloat fs_left;
|
|
||||||
@property (nonatomic) CGFloat fs_bottom;
|
|
||||||
@property (nonatomic) CGFloat fs_right;
|
|
||||||
|
|
||||||
@end
|
|
|
@ -1,73 +0,0 @@
|
||||||
//
|
|
||||||
// UIView+FSExtension.m
|
|
||||||
// Pods
|
|
||||||
//
|
|
||||||
// Created by Wenchao Ding on 29/1/15.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "UIView+FSExtension.h"
|
|
||||||
|
|
||||||
@implementation UIView (FSExtension)
|
|
||||||
|
|
||||||
- (CGFloat)fs_width
|
|
||||||
{
|
|
||||||
return CGRectGetWidth(self.frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFs_width:(CGFloat)fs_width
|
|
||||||
{
|
|
||||||
self.frame = CGRectMake(self.fs_left, self.fs_top, fs_width, self.fs_height);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)fs_height
|
|
||||||
{
|
|
||||||
return CGRectGetHeight(self.frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFs_height:(CGFloat)fs_height
|
|
||||||
{
|
|
||||||
self.frame = CGRectMake(self.fs_left, self.fs_top, self.fs_width, fs_height);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)fs_top
|
|
||||||
{
|
|
||||||
return CGRectGetMinY(self.frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFs_top:(CGFloat)fs_top
|
|
||||||
{
|
|
||||||
self.frame = CGRectMake(self.fs_left, fs_top, self.fs_width, self.fs_height);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)fs_bottom
|
|
||||||
{
|
|
||||||
return CGRectGetMaxY(self.frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFs_bottom:(CGFloat)fs_bottom
|
|
||||||
{
|
|
||||||
self.fs_top = fs_bottom - self.fs_height;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)fs_left
|
|
||||||
{
|
|
||||||
return CGRectGetMinX(self.frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFs_left:(CGFloat)fs_left
|
|
||||||
{
|
|
||||||
self.frame = CGRectMake(fs_left, self.fs_top, self.fs_width, self.fs_height);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)fs_right
|
|
||||||
{
|
|
||||||
return CGRectGetMaxX(self.frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setFs_right:(CGFloat)fs_right
|
|
||||||
{
|
|
||||||
self.fs_left = self.fs_right - self.fs_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2013-2016 FSCalendar (https://github.com/WenchaoIOS/FSCalendar)
|
Copyright (c) 2013-2016 FSCalendar (https://github.com/WenchaoD/FSCalendar)
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
@ -1,45 +1,90 @@
|
||||||
![fscalendar](https://cloud.githubusercontent.com/assets/5186464/6655324/213a814a-cb36-11e4-9add-f80515a83291.png)<br/><br/>
|
|
||||||
|
![logo](https://cloud.githubusercontent.com/assets/5186464/16540124/efc51f72-408b-11e6-934a-4e750b8b55bb.png)
|
||||||
|
<br/><br/>
|
||||||
|
[![Apps Using](https://img.shields.io/badge/Apps%20Using-%3E%2010,000-00BFFF.svg?style=plastic)](https://cocoapods.org/pods/FSCalendar)
|
||||||
|
[![Total Downloads](https://img.shields.io/badge/Total%20Downloads-%3E%20500,000-00BFFF.svg?style=plastic)](https://cocoapods.org/pods/FSCalendar)
|
||||||
|
<br>
|
||||||
[![Travis](https://travis-ci.org/WenchaoD/FSCalendar.svg?branch=master)](https://travis-ci.org/WenchaoD/FSCalendar)
|
[![Travis](https://travis-ci.org/WenchaoD/FSCalendar.svg?branch=master)](https://travis-ci.org/WenchaoD/FSCalendar)
|
||||||
[![Version](https://img.shields.io/cocoapods/v/FSCalendar.svg?style=flat)](http://cocoadocs.org/docsets/FSCalendar)
|
[![Version](https://img.shields.io/cocoapods/v/FSCalendar.svg?style=flat)](http://cocoadocs.org/docsets/FSCalendar)
|
||||||
[![Platform](https://img.shields.io/badge/platform-iOS%207%2B-blue.svg?style=flat)](http://cocoadocs.org/docsets/FSCalendar)
|
[![Platform](https://img.shields.io/badge/platform-iOS%207%2B-blue.svg?style=flat)](http://cocoadocs.org/docsets/FSCalendar)
|
||||||
[![Swift2 compatible](https://img.shields.io/badge/swift2-compatible-4BC51D.svg?style=flat)](https://developer.apple.com/swift/)
|
|
||||||
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
|
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
|
||||||
[![License](https://img.shields.io/cocoapods/l/FSCalendar.svg?style=flat)](http://cocoadocs.org/docsets/FSCalendar)
|
<br>
|
||||||
[![Join the chat at https://gitter.im/WenchaoD/FSCalendar](https://badges.gitter.im/WenchaoD/FSCalendar.svg)](https://gitter.im/WenchaoD/FSCalendar?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[![Languages](https://img.shields.io/badge/language-objc%20|%20swift-FF69B4.svg?style=plastic)](#)
|
||||||
|
|
||||||
# Screenshots
|
# Table of contents
|
||||||
|
* [Screenshots](#screenshots)
|
||||||
|
* [Installation](#installation)
|
||||||
|
* [Pre-knowledge](#pre-knowledge)
|
||||||
|
* [Support](#support)
|
||||||
|
* [Contact](#contact)
|
||||||
|
|
||||||
## iPhone
|
## <a id="screenshots"></a>Screenshots
|
||||||
|
|
||||||
|
### iPhone
|
||||||
![fscalendar](https://cloud.githubusercontent.com/assets/5186464/10262249/4fabae40-69f2-11e5-97ab-afbacd0a3da2.jpg)
|
![fscalendar](https://cloud.githubusercontent.com/assets/5186464/10262249/4fabae40-69f2-11e5-97ab-afbacd0a3da2.jpg)
|
||||||
|
|
||||||
## iPad
|
### iPad
|
||||||
![fscalendar-ipad](https://cloud.githubusercontent.com/assets/5186464/10927681/d2448cb6-82dc-11e5-9d11-f664a06698a7.jpg)
|
![fscalendar-ipad](https://cloud.githubusercontent.com/assets/5186464/10927681/d2448cb6-82dc-11e5-9d11-f664a06698a7.jpg)
|
||||||
|
|
||||||
## Working with AutoLayout and Orientation
|
### Safe Orientation
|
||||||
![fscalendar-scope-orientation-autolayout](https://cloud.githubusercontent.com/assets/5186464/13728798/59855e3e-e95e-11e5-84db-60f843427ef3.gif)
|
![fscalendar-scope-orientation-autolayout](https://cloud.githubusercontent.com/assets/5186464/20325758/ea125e1e-abc0-11e6-9e29-491acbcb0d07.gif)
|
||||||
|
|
||||||
# Installation
|
### Today Extension
|
||||||
|
| iOS8/9 | iOS10 |
|
||||||
|
|--------------|-------------|
|
||||||
|
|![today1](https://cloud.githubusercontent.com/assets/5186464/20288375/ed3fba0e-ab0d-11e6-8b15-43d3dc656f22.gif)|![today2](https://cloud.githubusercontent.com/assets/5186464/20288378/f11e318c-ab0d-11e6-8d1d-9d89b563e9d7.gif)|
|
||||||
|
|
||||||
|
### Interactive Scope Gesture
|
||||||
|
| ![1](https://cloud.githubusercontent.com/assets/5186464/21559640/e92a9ccc-ce8a-11e6-8c60-e52204f33249.gif) |
|
||||||
|
| ---- |
|
||||||
|
|
||||||
|
### DIY support
|
||||||
|
| ![1](https://cloud.githubusercontent.com/assets/5186464/20026983/22354a0e-a342-11e6-8ae6-0614ea7f35ae.gif) |
|
||||||
|
| ------------- |
|
||||||
|
> To customize your own cell, view DIY Example in `Example-Swift` or `Example-Objc`
|
||||||
|
|
||||||
|
|
||||||
|
### Swipe-To-Choose
|
||||||
|
|
||||||
|
|Single-Selection<br/>Swipe-To-Choose|Multiple-Selection<br/>Swipe-To-Choose|DIY<br/>Swipe-To-Choose|
|
||||||
|
|----------|--------|--------|
|
||||||
|
|![1](https://cloud.githubusercontent.com/assets/5186464/20257768/cb1905d4-aa86-11e6-9ef7-af76f9caa024.gif)|![2](https://cloud.githubusercontent.com/assets/5186464/20257826/254070ec-aa87-11e6-81b1-1815453fd464.gif)|![3](https://cloud.githubusercontent.com/assets/5186464/20257836/2ffa3252-aa87-11e6-8ff9-3b40f5b2307b.gif)|
|
||||||
|
|
||||||
|
## Achievement of Users <a id="achievement"></a>
|
||||||
|
|
||||||
|
| ![1](https://cloud.githubusercontent.com/assets/5186464/21747193/3111e4ee-d59a-11e6-8e4d-ca695b53e421.png) | ![2](https://cloud.githubusercontent.com/assets/5186464/21747393/42a753fa-d5a0-11e6-9cb2-de7cc642e69e.png) | ![3](https://cloud.githubusercontent.com/assets/5186464/21897255/ff78fcdc-d923-11e6-9d59-62119bc4343f.png) | ![4](https://cloud.githubusercontent.com/assets/5186464/21747192/3111cacc-d59a-11e6-8626-44cd75ebd794.png) |
|
||||||
|
| ------------- | ------------- | ------------- | ------------- |
|
||||||
|
|
||||||
|
#### [***More Achievements***](https://github.com/WenchaoD/FSCalendar/wiki/) are available in [***FSCalendar Gallery***](https://github.com/WenchaoD/FSCalendar/wiki/)
|
||||||
|
|
||||||
|
===
|
||||||
|
|
||||||
|
# <a id="installation"></a>Installation
|
||||||
|
|
||||||
## CocoaPods:
|
## CocoaPods:
|
||||||
|
|
||||||
* For iOS8+: 👍
|
* For iOS8+: 👍
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
use_frameworks!
|
use_frameworks!
|
||||||
pod 'FSCalendar'
|
target '<Your Target Name>' do
|
||||||
|
pod 'FSCalendar'
|
||||||
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
* For iOS7+:
|
* For iOS7+:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
pod 'FSCalendar'
|
target '<Your Target Name>' do
|
||||||
|
pod 'FSCalendar'
|
||||||
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
* Alternatively to give it a test run, run the command:
|
> [NSCalendarExtension](https://github.com/WenchaoD/NSCalendarExtension) is required to get iOS7 compatibility.
|
||||||
```ruby
|
|
||||||
pod try FSCalendar
|
|
||||||
```
|
|
||||||
|
|
||||||
## Carthage:
|
## Carthage:
|
||||||
* For iOS8+
|
* For iOS8+
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
github "WenchaoD/FSCalendar"
|
github "WenchaoD/FSCalendar"
|
||||||
```
|
```
|
||||||
|
@ -47,20 +92,21 @@ github "WenchaoD/FSCalendar"
|
||||||
## Manually:
|
## Manually:
|
||||||
* Drag all files under `FSCalendar` folder into your project. 👍
|
* Drag all files under `FSCalendar` folder into your project. 👍
|
||||||
|
|
||||||
## Support IBInspectable / IBDesignable
|
> Alternatively to give it a test run, simply press `command+u` in `Example-Objc` or `Example-Swift` and launch the ***UITest Target***. <br>
|
||||||
Only the methods marked "👍" support IBInspectable / IBDesignable feature. [Have fun with Interface builder](#roll_with_interface_builder)
|
> Only the methods marked "👍" support IBInspectable / IBDesignable feature. [Have fun with Interface builder](#roll_with_interface_builder)
|
||||||
|
|
||||||
|
|
||||||
# Setup
|
# Setup
|
||||||
|
|
||||||
## Use Interface Builder
|
## Use Interface Builder
|
||||||
|
|
||||||
1. Drag an UIView object to ViewController Scene
|
1、 Drag an UIView object to ViewController Scene
|
||||||
2. Change the `Custom Class` to `FSCalendar`<br/>
|
2、 Change the `Custom Class` to `FSCalendar`<br/>
|
||||||
3. Link `dataSource` and `delegate` to the ViewController <br/>
|
3、 Link `dataSource` and `delegate` to the ViewController <br/>
|
||||||
|
|
||||||
![fscalendar-ib](https://cloud.githubusercontent.com/assets/5186464/9488580/a360297e-4c0d-11e5-8548-ee9274e7c4af.jpg)
|
![fscalendar-ib](https://cloud.githubusercontent.com/assets/5186464/9488580/a360297e-4c0d-11e5-8548-ee9274e7c4af.jpg)
|
||||||
|
|
||||||
4. Finally, you should implement `FSCalendarDataSource` and `FSCalendarDelegate` in ViewController.m
|
4、 Finally, implement `FSCalendarDataSource` and `FSCalendarDelegate` in your `ViewController`
|
||||||
|
|
||||||
## Or use code
|
## Or use code
|
||||||
|
|
||||||
|
@ -83,7 +129,7 @@ self.calendar = calendar;
|
||||||
|
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
private weak var calendar: FSCalendar!
|
fileprivate weak var calendar: FSCalendar!
|
||||||
```
|
```
|
||||||
```swift
|
```swift
|
||||||
// In loadView or viewDidLoad
|
// In loadView or viewDidLoad
|
||||||
|
@ -93,57 +139,190 @@ calendar.delegate = self
|
||||||
view.addSubview(calendar)
|
view.addSubview(calendar)
|
||||||
self.calendar = calendar
|
self.calendar = calendar
|
||||||
```
|
```
|
||||||
<br/>
|
|
||||||
|
> To use **FSCalendar** in Swift3, see `Example-Swift` for details.
|
||||||
|
|
||||||
|
|
||||||
## Hide placeholder dates
|
## <a id='adjusts_frame_dynamicly' /></a>Warning
|
||||||
![fscalendar-showsplaceholder](https://cloud.githubusercontent.com/assets/5186464/13727902/21a90042-e940-11e5-9b9f-392f38cf007d.gif)
|
`FSCalendar` ***doesn't*** update frame by itself, Please implement
|
||||||
|
|
||||||
1. Set `calendar.showsPlaceholders = NO`;
|
* For ***AutoLayout***
|
||||||
2. <a id="implement_bounding_rect_will_change"></a> Implement `-calendar:boundingRectWillChange:animated:`
|
|
||||||
|
|
||||||
```objc
|
```objc
|
||||||
// For autoLayout
|
|
||||||
- (void)calendar:(FSCalendar *)calendar boundingRectWillChange:(CGRect)bounds animated:(BOOL)animated
|
- (void)calendar:(FSCalendar *)calendar boundingRectWillChange:(CGRect)bounds animated:(BOOL)animated
|
||||||
{
|
{
|
||||||
_calendarHeightConstraint.constant = CGRectGetHeight(bounds);
|
self.calendarHeightConstraint.constant = CGRectGetHeight(bounds);
|
||||||
|
// Do other updates here
|
||||||
[self.view layoutIfNeeded];
|
[self.view layoutIfNeeded];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* For ***Manual Layout***
|
||||||
|
|
||||||
```objc
|
```objc
|
||||||
// For manual layout
|
|
||||||
- (void)calendar:(FSCalendar *)calendar boundingRectWillChange:(CGRect)bounds animated:(BOOL)animated
|
- (void)calendar:(FSCalendar *)calendar boundingRectWillChange:(CGRect)bounds animated:(BOOL)animated
|
||||||
{
|
{
|
||||||
calendar.frame = (CGRect){calendar.frame.origin,bounds.size};
|
calendar.frame = (CGRect){calendar.frame.origin,bounds.size};
|
||||||
|
// Do other updates here
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* If you are using ***Masonry***
|
||||||
|
|
||||||
|
```objc
|
||||||
|
- (void)calendar:(FSCalendar *)calendar boundingRectWillChange:(CGRect)bounds animated:(BOOL)animated
|
||||||
|
{
|
||||||
|
[calendar mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.height.equalTo(@(bounds.size.height));
|
||||||
|
// Do other updates
|
||||||
|
}];
|
||||||
|
[self.view layoutIfNeeded];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* If you are using ***SnapKit***
|
||||||
|
|
||||||
|
```swift
|
||||||
|
func calendar(_ calendar: FSCalendar, boundingRectWillChange bounds: CGRect, animated: Bool) {
|
||||||
|
calendar.snp.updateConstraints { (make) in
|
||||||
|
make.height.equalTo(bounds.height)
|
||||||
|
// Do other updates
|
||||||
|
}
|
||||||
|
self.view.layoutIfNeeded()
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### <a id="roll_with_interface_builder"></a> Roll with Interface Builder
|
### <a id="roll_with_interface_builder"></a> Roll with Interface Builder
|
||||||
![fscalendar - ibdesignable](https://cloud.githubusercontent.com/assets/5186464/9301716/2e76a2ca-4503-11e5-8450-1fa7aa93e9fd.gif)
|
![fscalendar - ibdesignable](https://cloud.githubusercontent.com/assets/5186464/9301716/2e76a2ca-4503-11e5-8450-1fa7aa93e9fd.gif)
|
||||||
|
|
||||||
## More Usage
|
# <a id="pre-knowledge"></a>Pre-knowledge
|
||||||
* To view more usage, download the zip file and read the example.
|
|
||||||
* Or you could refer to [this document](https://github.com/WenchaoD/FSCalendar/blob/master/MOREUSAGE.md)
|
|
||||||
* To view the full documentation, see [CocoaPods Documentation](http://cocoadocs.org/docsets/FSCalendar/2.0.1/)
|
|
||||||
|
|
||||||
# If you like this repo
|
> In `Swift3`, `NSDate` and `NSDateFormatter` have been renamed to ***Date*** and ***DateFormatter*** , see `Example-Swift` for details.
|
||||||
* ***Star*** this repo.
|
|
||||||
* Send your calendar screenshot or `itunes link address` [here](https://github.com/WenchaoD/FSCalendar/issues/2).
|
## How to create NSDate object
|
||||||
|
|
||||||
|
* By **NSCalendar**.
|
||||||
|
|
||||||
|
```objc
|
||||||
|
self.gregorian = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
|
||||||
|
```
|
||||||
|
|
||||||
|
Then:
|
||||||
|
|
||||||
|
```objc
|
||||||
|
NSDate *date = [gregorian dateWithEra:1 year:2016 month:9 day:10 hour:0 minute:0 second:0 nanosecond:0];
|
||||||
|
// 2016-09-10 00:00:00
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
* Or by **NSDateFormatter**
|
||||||
|
|
||||||
|
```objc
|
||||||
|
self.formatter = [[NSDateFormatter alloc] init];
|
||||||
|
self.formatter.dateFormat = @"yyyy-MM-dd";
|
||||||
|
```
|
||||||
|
|
||||||
|
Then:
|
||||||
|
|
||||||
|
```objc
|
||||||
|
NSDate *date = [self.formatter dateFromString:@"2016-09-10"];
|
||||||
|
```
|
||||||
|
|
||||||
|
## How to print out NSDate object
|
||||||
|
|
||||||
|
* Use **NSDateFormatter**
|
||||||
|
|
||||||
|
```objc
|
||||||
|
self.formatter = [[NSDateFormatter alloc] init];
|
||||||
|
self.formatter.dateFormat = @"yyyy/MM/dd";
|
||||||
|
```
|
||||||
|
|
||||||
|
```objc
|
||||||
|
NSString *string = [self.formatter stringFromDate:date];
|
||||||
|
NSLog(@"Date is %@", string);
|
||||||
|
```
|
||||||
|
|
||||||
|
## How to manipulate NSDate with NSCalendar
|
||||||
|
|
||||||
|
```objc
|
||||||
|
self.gregorian = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
|
||||||
|
```
|
||||||
|
* Get component of NSDate
|
||||||
|
|
||||||
|
```objc
|
||||||
|
NSInteger era = [self.gregorian component:NSCalendarUnitEra fromDate:date];
|
||||||
|
NSInteger year = [self.gregorian component:NSCalendarUnitYear fromDate:date];
|
||||||
|
NSInteger month = [self.gregorian component:NSCalendarUnitMonth fromDate:date];
|
||||||
|
NSInteger day = [self.gregorian component:NSCalendarUnitDay fromDate:date];
|
||||||
|
NSInteger hour = [self.gregorian component:NSCalendarUnitHour fromDate:date];
|
||||||
|
NSInteger minute = [self.gregorian component:NSCalendarUnitMinute fromDate:date];
|
||||||
|
...
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
* Get next **month**
|
||||||
|
|
||||||
|
```objc
|
||||||
|
NSDate *nextMonth = [self.gregorain dateByAddingUnit:NSCalendarUnitMonth value:1 toDate:date options:0];
|
||||||
|
```
|
||||||
|
|
||||||
|
* Get next **day**
|
||||||
|
|
||||||
|
```objc
|
||||||
|
NSDate *nextDay = [self.gregorain dateByAddingUnit:NSCalendarUnitDay value:1 toDate:date options:0];
|
||||||
|
```
|
||||||
|
|
||||||
|
* Is date in today/tomorrow/yesterday/weekend
|
||||||
|
|
||||||
|
```objc
|
||||||
|
BOOL isToday = [self.gregorian isDateInToday:date];
|
||||||
|
BOOL isYesterday = [self.gregorian isDateInYesterday:date];
|
||||||
|
BOOL isTomorrow = [self.gregorian isDateInTomorrow:date];
|
||||||
|
BOOL isWeekend = [self.gregorian isDateInWeekend:date];
|
||||||
|
```
|
||||||
|
|
||||||
|
* Compare two dates
|
||||||
|
|
||||||
|
```objc
|
||||||
|
|
||||||
|
BOOL sameDay = [self.gregorian isDate:date1 inSameDayAsDate:date2];
|
||||||
|
// Yes if the date1 and date2 are in same day
|
||||||
|
|
||||||
|
|
||||||
|
[self.gregorian compareDate:date1 toDate:date2 toUnitGranularity:unit];
|
||||||
|
// compare the era/year/month/day/hour/minute .etc ...
|
||||||
|
// return NSOrderAscending/NSOrderSame/NSOrderDecending
|
||||||
|
|
||||||
|
BOOL inSameUnit = [self.gregorian isDate:date1 equalToDate:date2 toUnitGranularity:unit];
|
||||||
|
// if the given unit (era/year/month/day/hour/minute .etc) are the same
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## <a id="support"></a>Support this repo
|
||||||
|
* [**★Star**](#) this repo
|
||||||
|
<br/>
|
||||||
|
* Support with <a href="https://www.paypal.me/WenchaoD" target="_blank"><img src="https://www.paypalobjects.com/webstatic/i/logo/rebrand/ppcom.svg" width="100" height="40" style="margin-bottom:-15px;"></a>
|
||||||
|
<br/>
|
||||||
|
* Support with <a href="https://cloud.githubusercontent.com/assets/5186464/15096775/bacc0506-1539-11e6-91b7-b1a7a773622b.png" target="_blank"><img src="http://a1.mzstatic.com/us/r30/Purple49/v4/50/16/b3/5016b341-39c1-b47b-2994-d7e23823baed/icon175x175.png" width="40" height="40" style="margin-bottom:-15px;-webkit-border-radius:10px;border:1px solid rgba(30, 154, 236, 1);"></a> or
|
||||||
|
<a href="https://cloud.githubusercontent.com/assets/5186464/15096872/b06f3a3a-153c-11e6-89f9-2e9c7b88ef42.png" target="_blank"><img src="http://a4.mzstatic.com/us/r30/Purple49/v4/23/31/14/233114f8-2e8d-7b63-8dc5-85d29893061e/icon175x175.jpeg" height="40" width="40" style="margin-bottom:-15px; -webkit-border-radius: 10px;border:1px solid rgba(43, 177, 0, 1)"></a>
|
||||||
|
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
|
## <a id='contact'/></a> Contact
|
||||||
|
* 微博: [**@WenchaoD**](http://weibo.com/WenchaoD)
|
||||||
|
* Twitter:[**@WenchaoD**](https://twitter.com/WenchaoD)
|
||||||
|
* <a id='qq_group'/></a>QQ支持群: <br><br>
|
||||||
|
|
||||||
|
![fscalendar](https://cloud.githubusercontent.com/assets/5186464/18407011/8e4b6e48-7738-11e6-9fad-0e23cc881516.JPG)
|
||||||
|
|
||||||
|
> If your made a beautiful calendar with this library in your app, please take a screen shot and [@me](https://twitter.com/WenchaoD) in twitter. Your help really means a lot to me! <br/>
|
||||||
|
|
||||||
# Support me via [![paypal](https://www.paypalobjects.com/webstatic/i/logo/rebrand/ppcom.svg)](https://www.paypalobjects.com/webstatic/i/logo/rebrand/ppcom.svg)
|
|
||||||
* ☕️ [This coffee is on me!](https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=Z84P82H3V4Q26&lc=C2&item_name=This%20coffee%20is%20on%20me%21&item_number=Support%20FSCalendar%20%2d%20WenchaoIOS&amount=5%2e00¤cy_code=USD&button_subtype=services&bn=PP%2dBuyNowBF%3abtn_buynowCC_LG%2egif%3aNonHosted)
|
|
||||||
* [Lunch is on me!](https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=Z84P82H3V4Q26&lc=C2&item_name=Lunch%20is%20on%20me%21&item_number=Support%20FSCalendar&amount=10%2e00¤cy_code=USD&button_subtype=services&bn=PP%2dBuyNowBF%3abtn_buynowCC_LG%2egif%3aNonHosted)
|
|
||||||
* [Have a nice dinner!](https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=Z84P82H3V4Q26&lc=C2&item_name=Tonight%27s%20dinner%20is%20on%20me%21&item_number=Support%20FSCalendar%20%2d%20WenchaoIOS&amount=25%2e00¤cy_code=USD&button_subtype=services&bn=PP%2dBuyNowBF%3abtn_buynowCC_LG%2egif%3aNonHosted)
|
|
||||||
* [Greate work! Keep the change!](https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=Z84P82H3V4Q26&lc=C2&item_name=Great%20work%21%20Keep%20the%20change%21&item_number=Support%20FSCalendar%20%2d%20WenchaoIOS&amount=100%2e00¤cy_code=USD&button_subtype=services&bn=PP%2dBuyNowBF%3abtn_buynowCC_LG%2egif%3aNonHosted)
|
|
||||||
|
|
||||||
# License
|
# License
|
||||||
FSCalendar is available under the MIT license. See the LICENSE file for more info.
|
FSCalendar is available under the MIT license. See the LICENSE file for more info.
|
||||||
|
|
||||||
# Contributions
|
### [Documentation](http://cocoadocs.org/docsets/FSCalendar/) | [More Usage](https://github.com/WenchaoD/FSCalendar/blob/master/MOREUSAGE.md) | [简书](http://www.jianshu.com/notebooks/4276521/latest)
|
||||||
* Issues and pull requests are absolutely welcome.
|
|
||||||
* For code contributions, please follow [Coding Guidelines for Cocoa](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html)
|
|
||||||
|
|
||||||
# Contact
|
|
||||||
* Email: `f33chobits@gmail.com`
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
#import <FirebaseCore/FirebaseCore.h>
|
||||||
|
|
||||||
|
#if !defined(__has_include)
|
||||||
|
#error "Firebase.h won't import anything if your compiler doesn't support __has_include. Please \
|
||||||
|
import the headers individually."
|
||||||
|
#else
|
||||||
|
#if __has_include(<FirebaseAnalytics/FirebaseAnalytics.h>)
|
||||||
|
#import <FirebaseAnalytics/FirebaseAnalytics.h>
|
||||||
|
#else
|
||||||
|
#ifndef FIREBASE_ANALYTICS_SUPPRESS_WARNING
|
||||||
|
#warning "FirebaseAnalytics.framework is not included in your target. Please add \
|
||||||
|
`Firebase/Core` to your Podfile or add FirebaseAnalytics.framework to your project to ensure \
|
||||||
|
Firebase services work as intended."
|
||||||
|
#endif // #ifndef FIREBASE_ANALYTICS_SUPPRESS_WARNING
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseAuth/FirebaseAuth.h>)
|
||||||
|
#import <FirebaseAuth/FirebaseAuth.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseCrash/FirebaseCrash.h>)
|
||||||
|
#import <FirebaseCrash/FirebaseCrash.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseDatabase/FirebaseDatabase.h>)
|
||||||
|
#import <FirebaseDatabase/FirebaseDatabase.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseDynamicLinks/FirebaseDynamicLinks.h>)
|
||||||
|
#import <FirebaseDynamicLinks/FirebaseDynamicLinks.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
|
||||||
|
#import <FirebaseFirestore/FirebaseFirestore.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseFunctions/FirebaseFunctions.h>)
|
||||||
|
#import <FirebaseFunctions/FirebaseFunctions.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseInstanceID/FirebaseInstanceID.h>)
|
||||||
|
#import <FirebaseInstanceID/FirebaseInstanceID.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseInvites/FirebaseInvites.h>)
|
||||||
|
#import <FirebaseInvites/FirebaseInvites.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseMessaging/FirebaseMessaging.h>)
|
||||||
|
#import <FirebaseMessaging/FirebaseMessaging.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseMLModelInterpreter/FirebaseMLModelInterpreter.h>)
|
||||||
|
#import <FirebaseMLModelInterpreter/FirebaseMLModelInterpreter.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseMLVision/FirebaseMLVision.h>)
|
||||||
|
#import <FirebaseMLVision/FirebaseMLVision.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseMLVisionBarcodeModel/FirebaseMLVisionBarcodeModel.h>)
|
||||||
|
#import <FirebaseMLVisionBarcodeModel/FirebaseMLVisionBarcodeModel.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseMLVisionFaceModel/FirebaseMLVisionFaceModel.h>)
|
||||||
|
#import <FirebaseMLVisionFaceModel/FirebaseMLVisionFaceModel.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseMLVisionLabelModel/FirebaseMLVisionLabelModel.h>)
|
||||||
|
#import <FirebaseMLVisionLabelModel/FirebaseMLVisionLabelModel.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseMLVisionTextModel/FirebaseMLVisionTextModel.h>)
|
||||||
|
#import <FirebaseMLVisionTextModel/FirebaseMLVisionTextModel.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<FirebasePerformance/FirebasePerformance.h>)
|
||||||
|
#import <FirebasePerformance/FirebasePerformance.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseRemoteConfig/FirebaseRemoteConfig.h>)
|
||||||
|
#import <FirebaseRemoteConfig/FirebaseRemoteConfig.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<FirebaseStorage/FirebaseStorage.h>)
|
||||||
|
#import <FirebaseStorage/FirebaseStorage.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<GoogleMobileAds/GoogleMobileAds.h>)
|
||||||
|
#import <GoogleMobileAds/GoogleMobileAds.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<Fabric/Fabric.h>)
|
||||||
|
#import <Fabric/Fabric.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<Crashlytics/Crashlytics.h>)
|
||||||
|
#import <Crashlytics/Crashlytics.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // defined(__has_include)
|
|
@ -0,0 +1,4 @@
|
||||||
|
module Firebase {
|
||||||
|
export *
|
||||||
|
header "Firebase.h"
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
# Firebase APIs for iOS
|
||||||
|
|
||||||
|
Simplify your iOS development, grow your user base, and monetize more
|
||||||
|
effectively with Firebase services.
|
||||||
|
|
||||||
|
Much more information can be found at [https://firebase.google.com](https://firebase.google.com).
|
||||||
|
|
||||||
|
## Install a Firebase SDK using CocoaPods
|
||||||
|
|
||||||
|
Firebase distributes several iOS specific APIs and SDKs via CocoaPods.
|
||||||
|
You can install the CocoaPods tool on OS X by running the following command from
|
||||||
|
the terminal. Detailed information is available in the [Getting Started
|
||||||
|
guide](https://guides.cocoapods.org/using/getting-started.html#getting-started).
|
||||||
|
|
||||||
|
```
|
||||||
|
$ sudo gem install cocoapods
|
||||||
|
```
|
||||||
|
|
||||||
|
## Try out an SDK
|
||||||
|
|
||||||
|
You can try any of the SDKs with `pod try`. Run the following command and select
|
||||||
|
the SDK you are interested in when prompted:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ pod try Firebase
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that some SDKs may require credentials. More information is available in
|
||||||
|
the SDK-specific documentation at [https://firebase.google.com/docs/](https://firebase.google.com/docs/).
|
||||||
|
|
||||||
|
## Add a Firebase SDK to your iOS app
|
||||||
|
|
||||||
|
CocoaPods is used to install and manage dependencies in existing Xcode projects.
|
||||||
|
|
||||||
|
1. Create an Xcode project, and save it to your local machine.
|
||||||
|
2. Create a file named `Podfile` in your project directory. This file defines
|
||||||
|
your project's dependencies, and is commonly referred to as a Podspec.
|
||||||
|
3. Open `Podfile`, and add your dependencies. A simple Podspec is shown here:
|
||||||
|
|
||||||
|
```
|
||||||
|
platform :ios, '8.0'
|
||||||
|
pod 'Firebase'
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Save the file.
|
||||||
|
|
||||||
|
5. Open a terminal and `cd` to the directory containing the Podfile.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd <path-to-project>/project/
|
||||||
|
```
|
||||||
|
|
||||||
|
6. Run the `pod install` command. This will install the SDKs specified in the
|
||||||
|
Podspec, along with any dependencies they may have.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ pod install
|
||||||
|
```
|
||||||
|
|
||||||
|
7. Open your app's `.xcworkspace` file to launch Xcode. Use this file for all
|
||||||
|
development on your app.
|
||||||
|
|
||||||
|
8. You can also install other Firebase SDKs by adding the subspecs in the
|
||||||
|
Podfile.
|
||||||
|
|
||||||
|
```
|
||||||
|
pod 'Firebase/AdMob'
|
||||||
|
pod 'Firebase/Analytics'
|
||||||
|
pod 'Firebase/Auth'
|
||||||
|
pod 'Firebase/Crash'
|
||||||
|
pod 'Firebase/Database'
|
||||||
|
pod 'Firebase/DynamicLinks'
|
||||||
|
pod 'Firebase/Firestore'
|
||||||
|
pod 'Firebase/Functions'
|
||||||
|
pod 'Firebase/Invites'
|
||||||
|
pod 'Firebase/Messaging'
|
||||||
|
pod 'Firebase/MLCommon'
|
||||||
|
pod 'Firebase/MLModelInterpreter'
|
||||||
|
pod 'Firebase/MLVision'
|
||||||
|
pod 'Firebase/MLVisionBarcodeModel'
|
||||||
|
pod 'Firebase/MLVisionFaceModel'
|
||||||
|
pod 'Firebase/MLVisionLabelModel'
|
||||||
|
pod 'Firebase/MLVisionTextModel'
|
||||||
|
pod 'Firebase/Performance'
|
||||||
|
pod 'Firebase/RemoteConfig'
|
||||||
|
pod 'Firebase/Storage'
|
||||||
|
```
|
BIN
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics
generated
Executable file
BIN
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics
generated
Executable file
Binary file not shown.
62
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics+AppDelegate.h
generated
Executable file
62
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics+AppDelegate.h
generated
Executable file
|
@ -0,0 +1,62 @@
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import "FIRAnalytics.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides App Delegate handlers to be used in your App Delegate.
|
||||||
|
*
|
||||||
|
* To save time integrating Firebase Analytics in an application, Firebase Analytics does not
|
||||||
|
* require delegation implementation from the AppDelegate. Instead this is automatically done by
|
||||||
|
* Firebase Analytics. Should you choose instead to delegate manually, you can turn off the App
|
||||||
|
* Delegate Proxy by adding FirebaseAppDelegateProxyEnabled into your app's Info.plist and setting
|
||||||
|
* it to NO, and adding the methods in this category to corresponding delegation handlers.
|
||||||
|
*
|
||||||
|
* To handle Universal Links, you must return YES in
|
||||||
|
* [UIApplicationDelegate application:didFinishLaunchingWithOptions:].
|
||||||
|
*/
|
||||||
|
@interface FIRAnalytics (AppDelegate)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles events related to a URL session that are waiting to be processed.
|
||||||
|
*
|
||||||
|
* For optimal use of Firebase Analytics, call this method from the
|
||||||
|
* [UIApplicationDelegate application:handleEventsForBackgroundURLSession:completionHandler]
|
||||||
|
* method of the app delegate in your app.
|
||||||
|
*
|
||||||
|
* @param identifier The identifier of the URL session requiring attention.
|
||||||
|
* @param completionHandler The completion handler to call when you finish processing the events.
|
||||||
|
* Calling this completion handler lets the system know that your app's user interface is
|
||||||
|
* updated and a new snapshot can be taken.
|
||||||
|
*/
|
||||||
|
+ (void)handleEventsForBackgroundURLSession:(NSString *)identifier
|
||||||
|
completionHandler:(nullable void (^)(void))completionHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the event when the app is launched by a URL.
|
||||||
|
*
|
||||||
|
* Call this method from [UIApplicationDelegate application:openURL:options:] (on iOS 9.0 and
|
||||||
|
* above), or [UIApplicationDelegate application:openURL:sourceApplication:annotation:] (on
|
||||||
|
* iOS 8.x and below) in your app.
|
||||||
|
*
|
||||||
|
* @param url The URL resource to open. This resource can be a network resource or a file.
|
||||||
|
*/
|
||||||
|
+ (void)handleOpenURL:(NSURL *)url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the event when the app receives data associated with user activity that includes a
|
||||||
|
* Universal Link (on iOS 9.0 and above).
|
||||||
|
*
|
||||||
|
* Call this method from [UIApplication continueUserActivity:restorationHandler:] in your app
|
||||||
|
* delegate (on iOS 9.0 and above).
|
||||||
|
*
|
||||||
|
* @param userActivity The activity object containing the data associated with the task the user
|
||||||
|
* was performing.
|
||||||
|
*/
|
||||||
|
+ (void)handleUserActivity:(id)userActivity;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
119
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics.h
generated
Executable file
119
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics.h
generated
Executable file
|
@ -0,0 +1,119 @@
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import "FIREventNames.h"
|
||||||
|
#import "FIRParameterNames.h"
|
||||||
|
#import "FIRUserPropertyNames.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/// The top level Firebase Analytics singleton that provides methods for logging events and setting
|
||||||
|
/// user properties. See <a href="http://goo.gl/gz8SLz">the developer guides</a> for general
|
||||||
|
/// information on using Firebase Analytics in your apps.
|
||||||
|
NS_SWIFT_NAME(Analytics)
|
||||||
|
@interface FIRAnalytics : NSObject
|
||||||
|
|
||||||
|
/// Logs an app event. The event can have up to 25 parameters. Events with the same name must have
|
||||||
|
/// the same parameters. Up to 500 event names are supported. Using predefined events and/or
|
||||||
|
/// parameters is recommended for optimal reporting.
|
||||||
|
///
|
||||||
|
/// The following event names are reserved and cannot be used:
|
||||||
|
/// <ul>
|
||||||
|
/// <li>ad_activeview</li>
|
||||||
|
/// <li>ad_click</li>
|
||||||
|
/// <li>ad_exposure</li>
|
||||||
|
/// <li>ad_impression</li>
|
||||||
|
/// <li>ad_query</li>
|
||||||
|
/// <li>adunit_exposure</li>
|
||||||
|
/// <li>app_clear_data</li>
|
||||||
|
/// <li>app_remove</li>
|
||||||
|
/// <li>app_update</li>
|
||||||
|
/// <li>error</li>
|
||||||
|
/// <li>first_open</li>
|
||||||
|
/// <li>in_app_purchase</li>
|
||||||
|
/// <li>notification_dismiss</li>
|
||||||
|
/// <li>notification_foreground</li>
|
||||||
|
/// <li>notification_open</li>
|
||||||
|
/// <li>notification_receive</li>
|
||||||
|
/// <li>os_update</li>
|
||||||
|
/// <li>screen_view</li>
|
||||||
|
/// <li>session_start</li>
|
||||||
|
/// <li>user_engagement</li>
|
||||||
|
/// </ul>
|
||||||
|
///
|
||||||
|
/// @param name The name of the event. Should contain 1 to 40 alphanumeric characters or
|
||||||
|
/// underscores. The name must start with an alphabetic character. Some event names are
|
||||||
|
/// reserved. See FIREventNames.h for the list of reserved event names. The "firebase_",
|
||||||
|
/// "google_", and "ga_" prefixes are reserved and should not be used. Note that event names are
|
||||||
|
/// case-sensitive and that logging two events whose names differ only in case will result in
|
||||||
|
/// two distinct events.
|
||||||
|
/// @param parameters The dictionary of event parameters. Passing nil indicates that the event has
|
||||||
|
/// no parameters. Parameter names can be up to 40 characters long and must start with an
|
||||||
|
/// alphabetic character and contain only alphanumeric characters and underscores. Only NSString
|
||||||
|
/// and NSNumber (signed 64-bit integer and 64-bit floating-point number) parameter types are
|
||||||
|
/// supported. NSString parameter values can be up to 100 characters long. The "firebase_",
|
||||||
|
/// "google_", and "ga_" prefixes are reserved and should not be used for parameter names.
|
||||||
|
+ (void)logEventWithName:(NSString *)name
|
||||||
|
parameters:(nullable NSDictionary<NSString *, id> *)parameters
|
||||||
|
NS_SWIFT_NAME(logEvent(_:parameters:));
|
||||||
|
|
||||||
|
/// Sets a user property to a given value. Up to 25 user property names are supported. Once set,
|
||||||
|
/// user property values persist throughout the app lifecycle and across sessions.
|
||||||
|
///
|
||||||
|
/// The following user property names are reserved and cannot be used:
|
||||||
|
/// <ul>
|
||||||
|
/// <li>first_open_time</li>
|
||||||
|
/// <li>last_deep_link_referrer</li>
|
||||||
|
/// <li>user_id</li>
|
||||||
|
/// </ul>
|
||||||
|
///
|
||||||
|
/// @param value The value of the user property. Values can be up to 36 characters long. Setting the
|
||||||
|
/// value to nil removes the user property.
|
||||||
|
/// @param name The name of the user property to set. Should contain 1 to 24 alphanumeric characters
|
||||||
|
/// or underscores and must start with an alphabetic character. The "firebase_", "google_", and
|
||||||
|
/// "ga_" prefixes are reserved and should not be used for user property names.
|
||||||
|
+ (void)setUserPropertyString:(nullable NSString *)value forName:(NSString *)name
|
||||||
|
NS_SWIFT_NAME(setUserProperty(_:forName:));
|
||||||
|
|
||||||
|
/// Sets the user ID property. This feature must be used in accordance with
|
||||||
|
/// <a href="https://www.google.com/policies/privacy">Google's Privacy Policy</a>
|
||||||
|
///
|
||||||
|
/// @param userID The user ID to ascribe to the user of this app on this device, which must be
|
||||||
|
/// non-empty and no more than 256 characters long. Setting userID to nil removes the user ID.
|
||||||
|
+ (void)setUserID:(nullable NSString *)userID;
|
||||||
|
|
||||||
|
/// Sets the current screen name, which specifies the current visual context in your app. This helps
|
||||||
|
/// identify the areas in your app where users spend their time and how they interact with your app.
|
||||||
|
/// Must be called on the main thread.
|
||||||
|
///
|
||||||
|
/// Note that screen reporting is enabled automatically and records the class name of the current
|
||||||
|
/// UIViewController for you without requiring you to call this method. If you implement
|
||||||
|
/// viewDidAppear in your UIViewController but do not call [super viewDidAppear:], that screen class
|
||||||
|
/// will not be automatically tracked. The class name can optionally be overridden by calling this
|
||||||
|
/// method in the viewDidAppear callback of your UIViewController and specifying the
|
||||||
|
/// screenClassOverride parameter. setScreenName:screenClass: must be called after
|
||||||
|
/// [super viewDidAppear:].
|
||||||
|
///
|
||||||
|
/// If your app does not use a distinct UIViewController for each screen, you should call this
|
||||||
|
/// method and specify a distinct screenName each time a new screen is presented to the user.
|
||||||
|
///
|
||||||
|
/// The screen name and screen class remain in effect until the current UIViewController changes or
|
||||||
|
/// a new call to setScreenName:screenClass: is made.
|
||||||
|
///
|
||||||
|
/// @param screenName The name of the current screen. Should contain 1 to 100 characters. Set to nil
|
||||||
|
/// to clear the current screen name.
|
||||||
|
/// @param screenClassOverride The name of the screen class. Should contain 1 to 100 characters. By
|
||||||
|
/// default this is the class name of the current UIViewController. Set to nil to revert to the
|
||||||
|
/// default class name.
|
||||||
|
+ (void)setScreenName:(nullable NSString *)screenName
|
||||||
|
screenClass:(nullable NSString *)screenClassOverride;
|
||||||
|
|
||||||
|
/// The unique ID for this instance of the application.
|
||||||
|
+ (NSString *)appInstanceID;
|
||||||
|
|
||||||
|
/// Clears all analytics data for this instance from the device and resets the app instance ID.
|
||||||
|
/// FIRAnalyticsConfiguration values will be reset to the default values.
|
||||||
|
+ (void)resetAnalyticsData;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef FIR_SWIFT_NAME
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
// NS_SWIFT_NAME can only translate factory methods before the iOS 9.3 SDK.
|
||||||
|
// Wrap it in our own macro if it's a non-compatible SDK.
|
||||||
|
#ifdef __IPHONE_9_3
|
||||||
|
#define FIR_SWIFT_NAME(X) NS_SWIFT_NAME(X)
|
||||||
|
#else
|
||||||
|
#define FIR_SWIFT_NAME(X) // Intentionally blank.
|
||||||
|
#endif // #ifdef __IPHONE_9_3
|
||||||
|
|
||||||
|
#endif // FIR_SWIFT_NAME
|
407
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h
generated
Executable file
407
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h
generated
Executable file
|
@ -0,0 +1,407 @@
|
||||||
|
/// @file FIREventNames.h
|
||||||
|
///
|
||||||
|
/// Predefined event names.
|
||||||
|
///
|
||||||
|
/// An Event is an important occurrence in your app that you want to measure. You can report up to
|
||||||
|
/// 500 different types of Events per app and you can associate up to 25 unique parameters with each
|
||||||
|
/// Event type. Some common events are suggested below, but you may also choose to specify custom
|
||||||
|
/// Event types that are associated with your specific app. Each event type is identified by a
|
||||||
|
/// unique name. Event names can be up to 40 characters long, may only contain alphanumeric
|
||||||
|
/// characters and underscores ("_"), and must start with an alphabetic character. The "firebase_",
|
||||||
|
/// "google_", and "ga_" prefixes are reserved and should not be used.
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
/// Add Payment Info event. This event signifies that a user has submitted their payment information
|
||||||
|
/// to your app.
|
||||||
|
static NSString *const kFIREventAddPaymentInfo NS_SWIFT_NAME(AnalyticsEventAddPaymentInfo) =
|
||||||
|
@"add_payment_info";
|
||||||
|
|
||||||
|
/// E-Commerce Add To Cart event. This event signifies that an item was added to a cart for
|
||||||
|
/// purchase. Add this event to a funnel with kFIREventEcommercePurchase to gauge the effectiveness
|
||||||
|
/// of your checkout process. Note: If you supply the @c kFIRParameterValue parameter, you must
|
||||||
|
/// also supply the @c kFIRParameterCurrency parameter so that revenue metrics can be computed
|
||||||
|
/// accurately. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterQuantity (signed 64-bit integer as NSNumber)</li>
|
||||||
|
/// <li>@c kFIRParameterItemID (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterItemName (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterItemCategory (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterItemLocationID (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterPrice (double as NSNumber) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterOrigin (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterDestination (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterStartDate (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterEndDate (NSString) (optional)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventAddToCart NS_SWIFT_NAME(AnalyticsEventAddToCart) = @"add_to_cart";
|
||||||
|
|
||||||
|
/// E-Commerce Add To Wishlist event. This event signifies that an item was added to a wishlist.
|
||||||
|
/// Use this event to identify popular gift items in your app. Note: If you supply the
|
||||||
|
/// @c kFIRParameterValue parameter, you must also supply the @c kFIRParameterCurrency
|
||||||
|
/// parameter so that revenue metrics can be computed accurately. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterQuantity (signed 64-bit integer as NSNumber)</li>
|
||||||
|
/// <li>@c kFIRParameterItemID (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterItemName (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterItemCategory (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterItemLocationID (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterPrice (double as NSNumber) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventAddToWishlist NS_SWIFT_NAME(AnalyticsEventAddToWishlist) =
|
||||||
|
@"add_to_wishlist";
|
||||||
|
|
||||||
|
/// App Open event. By logging this event when an App becomes active, developers can understand how
|
||||||
|
/// often users leave and return during the course of a Session. Although Sessions are automatically
|
||||||
|
/// reported, this event can provide further clarification around the continuous engagement of
|
||||||
|
/// app-users.
|
||||||
|
static NSString *const kFIREventAppOpen NS_SWIFT_NAME(AnalyticsEventAppOpen) = @"app_open";
|
||||||
|
|
||||||
|
/// E-Commerce Begin Checkout event. This event signifies that a user has begun the process of
|
||||||
|
/// checking out. Add this event to a funnel with your kFIREventEcommercePurchase event to gauge the
|
||||||
|
/// effectiveness of your checkout process. Note: If you supply the @c kFIRParameterValue
|
||||||
|
/// parameter, you must also supply the @c kFIRParameterCurrency parameter so that revenue
|
||||||
|
/// metrics can be computed accurately. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterTransactionID (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterStartDate (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterEndDate (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterNumberOfNights (signed 64-bit integer as NSNumber) (optional) for
|
||||||
|
/// hotel bookings</li>
|
||||||
|
/// <li>@c kFIRParameterNumberOfRooms (signed 64-bit integer as NSNumber) (optional) for
|
||||||
|
/// hotel bookings</li>
|
||||||
|
/// <li>@c kFIRParameterNumberOfPassengers (signed 64-bit integer as NSNumber) (optional)
|
||||||
|
/// for travel bookings</li>
|
||||||
|
/// <li>@c kFIRParameterOrigin (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterDestination (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterTravelClass (NSString) (optional) for travel bookings</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventBeginCheckout NS_SWIFT_NAME(AnalyticsEventBeginCheckout) =
|
||||||
|
@"begin_checkout";
|
||||||
|
|
||||||
|
/// Campaign Detail event. Log this event to supply the referral details of a re-engagement
|
||||||
|
/// campaign. Note: you must supply at least one of the required parameters kFIRParameterSource,
|
||||||
|
/// kFIRParameterMedium or kFIRParameterCampaign. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterSource (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterMedium (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterCampaign (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterTerm (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterContent (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterAdNetworkClickID (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterCP1 (NSString) (optional)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventCampaignDetails NS_SWIFT_NAME(AnalyticsEventCampaignDetails) =
|
||||||
|
@"campaign_details";
|
||||||
|
|
||||||
|
/// Checkout progress. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterCheckoutStep (unsigned 64-bit integer as NSNumber)</li>
|
||||||
|
/// <li>@c kFIRParameterCheckoutOption (NSString) (optional)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventCheckoutProgress NS_SWIFT_NAME(AnalyticsEventCheckoutProgress) =
|
||||||
|
@"checkout_progress";
|
||||||
|
|
||||||
|
/// Earn Virtual Currency event. This event tracks the awarding of virtual currency in your app. Log
|
||||||
|
/// this along with @c kFIREventSpendVirtualCurrency to better understand your virtual economy.
|
||||||
|
/// Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterVirtualCurrencyName (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterValue (signed 64-bit integer or double as NSNumber)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventEarnVirtualCurrency
|
||||||
|
NS_SWIFT_NAME(AnalyticsEventEarnVirtualCurrency) = @"earn_virtual_currency";
|
||||||
|
|
||||||
|
/// E-Commerce Purchase event. This event signifies that an item was purchased by a user. Note:
|
||||||
|
/// This is different from the in-app purchase event, which is reported automatically for App
|
||||||
|
/// Store-based apps. Note: If you supply the @c kFIRParameterValue parameter, you must also
|
||||||
|
/// supply the @c kFIRParameterCurrency parameter so that revenue metrics can be computed
|
||||||
|
/// accurately. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterTransactionID (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterTax (double as NSNumber) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterShipping (double as NSNumber) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterCoupon (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterLocation (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterStartDate (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterEndDate (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterNumberOfNights (signed 64-bit integer as NSNumber) (optional) for
|
||||||
|
/// hotel bookings</li>
|
||||||
|
/// <li>@c kFIRParameterNumberOfRooms (signed 64-bit integer as NSNumber) (optional) for
|
||||||
|
/// hotel bookings</li>
|
||||||
|
/// <li>@c kFIRParameterNumberOfPassengers (signed 64-bit integer as NSNumber) (optional)
|
||||||
|
/// for travel bookings</li>
|
||||||
|
/// <li>@c kFIRParameterOrigin (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterDestination (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterTravelClass (NSString) (optional) for travel bookings</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventEcommercePurchase NS_SWIFT_NAME(AnalyticsEventEcommercePurchase) =
|
||||||
|
@"ecommerce_purchase";
|
||||||
|
|
||||||
|
/// Generate Lead event. Log this event when a lead has been generated in the app to understand the
|
||||||
|
/// efficacy of your install and re-engagement campaigns. Note: If you supply the
|
||||||
|
/// @c kFIRParameterValue parameter, you must also supply the @c kFIRParameterCurrency
|
||||||
|
/// parameter so that revenue metrics can be computed accurately. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventGenerateLead NS_SWIFT_NAME(AnalyticsEventGenerateLead) =
|
||||||
|
@"generate_lead";
|
||||||
|
|
||||||
|
/// Join Group event. Log this event when a user joins a group such as a guild, team or family. Use
|
||||||
|
/// this event to analyze how popular certain groups or social features are in your app. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterGroupID (NSString)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventJoinGroup NS_SWIFT_NAME(AnalyticsEventJoinGroup) = @"join_group";
|
||||||
|
|
||||||
|
/// Level Up event. This event signifies that a player has leveled up in your gaming app. It can
|
||||||
|
/// help you gauge the level distribution of your userbase and help you identify certain levels that
|
||||||
|
/// are difficult to pass. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterLevel (signed 64-bit integer as NSNumber)</li>
|
||||||
|
/// <li>@c kFIRParameterCharacter (NSString) (optional)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventLevelUp NS_SWIFT_NAME(AnalyticsEventLevelUp) = @"level_up";
|
||||||
|
|
||||||
|
/// Login event. Apps with a login feature can report this event to signify that a user has logged
|
||||||
|
/// in.
|
||||||
|
static NSString *const kFIREventLogin NS_SWIFT_NAME(AnalyticsEventLogin) = @"login";
|
||||||
|
|
||||||
|
/// Post Score event. Log this event when the user posts a score in your gaming app. This event can
|
||||||
|
/// help you understand how users are actually performing in your game and it can help you correlate
|
||||||
|
/// high scores with certain audiences or behaviors. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterScore (signed 64-bit integer as NSNumber)</li>
|
||||||
|
/// <li>@c kFIRParameterLevel (signed 64-bit integer as NSNumber) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterCharacter (NSString) (optional)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventPostScore NS_SWIFT_NAME(AnalyticsEventPostScore) = @"post_score";
|
||||||
|
|
||||||
|
/// Present Offer event. This event signifies that the app has presented a purchase offer to a user.
|
||||||
|
/// Add this event to a funnel with the kFIREventAddToCart and kFIREventEcommercePurchase to gauge
|
||||||
|
/// your conversion process. Note: If you supply the @c kFIRParameterValue parameter, you must
|
||||||
|
/// also supply the @c kFIRParameterCurrency parameter so that revenue metrics can be computed
|
||||||
|
/// accurately. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterQuantity (signed 64-bit integer as NSNumber)</li>
|
||||||
|
/// <li>@c kFIRParameterItemID (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterItemName (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterItemCategory (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterItemLocationID (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterPrice (double as NSNumber) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventPresentOffer NS_SWIFT_NAME(AnalyticsEventPresentOffer) =
|
||||||
|
@"present_offer";
|
||||||
|
|
||||||
|
/// E-Commerce Purchase Refund event. This event signifies that an item purchase was refunded.
|
||||||
|
/// Note: If you supply the @c kFIRParameterValue parameter, you must also supply the
|
||||||
|
/// @c kFIRParameterCurrency parameter so that revenue metrics can be computed accurately.
|
||||||
|
/// Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterTransactionID (NSString) (optional)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventPurchaseRefund NS_SWIFT_NAME(AnalyticsEventPurchaseRefund) =
|
||||||
|
@"purchase_refund";
|
||||||
|
|
||||||
|
/// Remove from cart event. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterQuantity (signed 64-bit integer as NSNumber)</li>
|
||||||
|
/// <li>@c kFIRParameterItemID (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterItemName (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterItemCategory (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterItemLocationID (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterPrice (double as NSNumber) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterOrigin (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterDestination (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterStartDate (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterEndDate (NSString) (optional)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventRemoveFromCart NS_SWIFT_NAME(AnalyticsEventRemoveFromCart) =
|
||||||
|
@"remove_from_cart";
|
||||||
|
|
||||||
|
/// Search event. Apps that support search features can use this event to contextualize search
|
||||||
|
/// operations by supplying the appropriate, corresponding parameters. This event can help you
|
||||||
|
/// identify the most popular content in your app. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterSearchTerm (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterStartDate (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterEndDate (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterNumberOfNights (signed 64-bit integer as NSNumber) (optional) for
|
||||||
|
/// hotel bookings</li>
|
||||||
|
/// <li>@c kFIRParameterNumberOfRooms (signed 64-bit integer as NSNumber) (optional) for
|
||||||
|
/// hotel bookings</li>
|
||||||
|
/// <li>@c kFIRParameterNumberOfPassengers (signed 64-bit integer as NSNumber) (optional)
|
||||||
|
/// for travel bookings</li>
|
||||||
|
/// <li>@c kFIRParameterOrigin (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterDestination (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterTravelClass (NSString) (optional) for travel bookings</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventSearch NS_SWIFT_NAME(AnalyticsEventSearch) = @"search";
|
||||||
|
|
||||||
|
/// Select Content event. This general purpose event signifies that a user has selected some content
|
||||||
|
/// of a certain type in an app. The content can be any object in your app. This event can help you
|
||||||
|
/// identify popular content and categories of content in your app. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterContentType (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterItemID (NSString)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventSelectContent NS_SWIFT_NAME(AnalyticsEventSelectContent) =
|
||||||
|
@"select_content";
|
||||||
|
|
||||||
|
/// Set checkout option. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterCheckoutStep (unsigned 64-bit integer as NSNumber)</li>
|
||||||
|
/// <li>@c kFIRParameterCheckoutOption (NSString)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventSetCheckoutOption NS_SWIFT_NAME(AnalyticsEventSetCheckoutOption) =
|
||||||
|
@"set_checkout_option";
|
||||||
|
|
||||||
|
/// Share event. Apps with social features can log the Share event to identify the most viral
|
||||||
|
/// content. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterContentType (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterItemID (NSString)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventShare NS_SWIFT_NAME(AnalyticsEventShare) = @"share";
|
||||||
|
|
||||||
|
/// Sign Up event. This event indicates that a user has signed up for an account in your app. The
|
||||||
|
/// parameter signifies the method by which the user signed up. Use this event to understand the
|
||||||
|
/// different behaviors between logged in and logged out users. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterSignUpMethod (NSString)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventSignUp NS_SWIFT_NAME(AnalyticsEventSignUp) = @"sign_up";
|
||||||
|
|
||||||
|
/// Spend Virtual Currency event. This event tracks the sale of virtual goods in your app and can
|
||||||
|
/// help you identify which virtual goods are the most popular objects of purchase. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterItemName (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterVirtualCurrencyName (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterValue (signed 64-bit integer or double as NSNumber)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventSpendVirtualCurrency
|
||||||
|
NS_SWIFT_NAME(AnalyticsEventSpendVirtualCurrency) = @"spend_virtual_currency";
|
||||||
|
|
||||||
|
/// Tutorial Begin event. This event signifies the start of the on-boarding process in your app. Use
|
||||||
|
/// this in a funnel with kFIREventTutorialComplete to understand how many users complete this
|
||||||
|
/// process and move on to the full app experience.
|
||||||
|
static NSString *const kFIREventTutorialBegin NS_SWIFT_NAME(AnalyticsEventTutorialBegin) =
|
||||||
|
@"tutorial_begin";
|
||||||
|
|
||||||
|
/// Tutorial End event. Use this event to signify the user's completion of your app's on-boarding
|
||||||
|
/// process. Add this to a funnel with kFIREventTutorialBegin to gauge the completion rate of your
|
||||||
|
/// on-boarding process.
|
||||||
|
static NSString *const kFIREventTutorialComplete NS_SWIFT_NAME(AnalyticsEventTutorialComplete) =
|
||||||
|
@"tutorial_complete";
|
||||||
|
|
||||||
|
/// Unlock Achievement event. Log this event when the user has unlocked an achievement in your
|
||||||
|
/// game. Since achievements generally represent the breadth of a gaming experience, this event can
|
||||||
|
/// help you understand how many users are experiencing all that your game has to offer. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterAchievementID (NSString)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventUnlockAchievement NS_SWIFT_NAME(AnalyticsEventUnlockAchievement) =
|
||||||
|
@"unlock_achievement";
|
||||||
|
|
||||||
|
/// View Item event. This event signifies that some content was shown to the user. This content may
|
||||||
|
/// be a product, a webpage or just a simple image or text. Use the appropriate parameters to
|
||||||
|
/// contextualize the event. Use this event to discover the most popular items viewed in your app.
|
||||||
|
/// Note: If you supply the @c kFIRParameterValue parameter, you must also supply the
|
||||||
|
/// @c kFIRParameterCurrency parameter so that revenue metrics can be computed accurately.
|
||||||
|
/// Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterItemID (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterItemName (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterItemCategory (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterItemLocationID (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterPrice (double as NSNumber) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterQuantity (signed 64-bit integer as NSNumber) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterStartDate (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterEndDate (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterFlightNumber (NSString) (optional) for travel bookings</li>
|
||||||
|
/// <li>@c kFIRParameterNumberOfPassengers (signed 64-bit integer as NSNumber) (optional)
|
||||||
|
/// for travel bookings</li>
|
||||||
|
/// <li>@c kFIRParameterNumberOfNights (signed 64-bit integer as NSNumber) (optional) for
|
||||||
|
/// travel bookings</li>
|
||||||
|
/// <li>@c kFIRParameterNumberOfRooms (signed 64-bit integer as NSNumber) (optional) for
|
||||||
|
/// travel bookings</li>
|
||||||
|
/// <li>@c kFIRParameterOrigin (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterDestination (NSString) (optional)</li>
|
||||||
|
/// <li>@c kFIRParameterSearchTerm (NSString) (optional) for travel bookings</li>
|
||||||
|
/// <li>@c kFIRParameterTravelClass (NSString) (optional) for travel bookings</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventViewItem NS_SWIFT_NAME(AnalyticsEventViewItem) = @"view_item";
|
||||||
|
|
||||||
|
/// View Item List event. Log this event when the user has been presented with a list of items of a
|
||||||
|
/// certain category. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterItemCategory (NSString)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventViewItemList NS_SWIFT_NAME(AnalyticsEventViewItemList) =
|
||||||
|
@"view_item_list";
|
||||||
|
|
||||||
|
/// View Search Results event. Log this event when the user has been presented with the results of a
|
||||||
|
/// search. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterSearchTerm (NSString)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventViewSearchResults NS_SWIFT_NAME(AnalyticsEventViewSearchResults) =
|
||||||
|
@"view_search_results";
|
||||||
|
|
||||||
|
/// Level Start event. Log this event when the user starts a new level. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterLevelName (NSString)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventLevelStart NS_SWIFT_NAME(AnalyticsEventLevelStart) =
|
||||||
|
@"level_start";
|
||||||
|
|
||||||
|
/// Level End event. Log this event when the user finishes a level. Params:
|
||||||
|
///
|
||||||
|
/// <ul>
|
||||||
|
/// <li>@c kFIRParameterLevelName (NSString)</li>
|
||||||
|
/// <li>@c kFIRParameterSuccess (NSString)</li>
|
||||||
|
/// </ul>
|
||||||
|
static NSString *const kFIREventLevelEnd NS_SWIFT_NAME(AnalyticsEventLevelEnd) = @"level_end";
|
507
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRParameterNames.h
generated
Executable file
507
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRParameterNames.h
generated
Executable file
|
@ -0,0 +1,507 @@
|
||||||
|
/// @file FIRParameterNames.h
|
||||||
|
///
|
||||||
|
/// Predefined event parameter names.
|
||||||
|
///
|
||||||
|
/// Params supply information that contextualize Events. You can associate up to 25 unique Params
|
||||||
|
/// with each Event type. Some Params are suggested below for certain common Events, but you are
|
||||||
|
/// not limited to these. You may supply extra Params for suggested Events or custom Params for
|
||||||
|
/// Custom events. Param names can be up to 40 characters long, may only contain alphanumeric
|
||||||
|
/// characters and underscores ("_"), and must start with an alphabetic character. Param values can
|
||||||
|
/// be up to 100 characters long. The "firebase_", "google_", and "ga_" prefixes are reserved and
|
||||||
|
/// should not be used.
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
/// Game achievement ID (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterAchievementID : @"10_matches_won",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterAchievementID NS_SWIFT_NAME(AnalyticsParameterAchievementID) =
|
||||||
|
@"achievement_id";
|
||||||
|
|
||||||
|
/// Ad Network Click ID (NSString). Used for network-specific click IDs which vary in format.
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterAdNetworkClickID : @"1234567",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterAdNetworkClickID
|
||||||
|
NS_SWIFT_NAME(AnalyticsParameterAdNetworkClickID) = @"aclid";
|
||||||
|
|
||||||
|
/// The store or affiliation from which this transaction occurred (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterAffiliation : @"Google Store",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterAffiliation NS_SWIFT_NAME(AnalyticsParameterAffiliation) =
|
||||||
|
@"affiliation";
|
||||||
|
|
||||||
|
/// The individual campaign name, slogan, promo code, etc. Some networks have pre-defined macro to
|
||||||
|
/// capture campaign information, otherwise can be populated by developer. Highly Recommended
|
||||||
|
/// (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterCampaign : @"winter_promotion",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterCampaign NS_SWIFT_NAME(AnalyticsParameterCampaign) =
|
||||||
|
@"campaign";
|
||||||
|
|
||||||
|
/// Character used in game (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterCharacter : @"beat_boss",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterCharacter NS_SWIFT_NAME(AnalyticsParameterCharacter) =
|
||||||
|
@"character";
|
||||||
|
|
||||||
|
/// The checkout step (1..N) (unsigned 64-bit integer as NSNumber).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterCheckoutStep : @"1",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterCheckoutStep NS_SWIFT_NAME(AnalyticsParameterCheckoutStep) =
|
||||||
|
@"checkout_step";
|
||||||
|
|
||||||
|
/// Some option on a step in an ecommerce flow (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterCheckoutOption : @"Visa",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterCheckoutOption
|
||||||
|
NS_SWIFT_NAME(AnalyticsParameterCheckoutOption) = @"checkout_option";
|
||||||
|
|
||||||
|
/// Campaign content (NSString).
|
||||||
|
static NSString *const kFIRParameterContent NS_SWIFT_NAME(AnalyticsParameterContent) = @"content";
|
||||||
|
|
||||||
|
/// Type of content selected (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterContentType : @"news article",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterContentType NS_SWIFT_NAME(AnalyticsParameterContentType) =
|
||||||
|
@"content_type";
|
||||||
|
|
||||||
|
/// Coupon code for a purchasable item (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterCoupon : @"zz123",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterCoupon NS_SWIFT_NAME(AnalyticsParameterCoupon) = @"coupon";
|
||||||
|
|
||||||
|
/// Campaign custom parameter (NSString). Used as a method of capturing custom data in a campaign.
|
||||||
|
/// Use varies by network.
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterCP1 : @"custom_data",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterCP1 NS_SWIFT_NAME(AnalyticsParameterCP1) = @"cp1";
|
||||||
|
|
||||||
|
/// The name of a creative used in a promotional spot (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterCreativeName : @"Summer Sale",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterCreativeName NS_SWIFT_NAME(AnalyticsParameterCreativeName) =
|
||||||
|
@"creative_name";
|
||||||
|
|
||||||
|
/// The name of a creative slot (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterCreativeSlot : @"summer_banner2",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterCreativeSlot NS_SWIFT_NAME(AnalyticsParameterCreativeSlot) =
|
||||||
|
@"creative_slot";
|
||||||
|
|
||||||
|
/// Purchase currency in 3-letter <a href="http://en.wikipedia.org/wiki/ISO_4217#Active_codes">
|
||||||
|
/// ISO_4217</a> format (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterCurrency : @"USD",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterCurrency NS_SWIFT_NAME(AnalyticsParameterCurrency) =
|
||||||
|
@"currency";
|
||||||
|
|
||||||
|
/// Flight or Travel destination (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterDestination : @"Mountain View, CA",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterDestination NS_SWIFT_NAME(AnalyticsParameterDestination) =
|
||||||
|
@"destination";
|
||||||
|
|
||||||
|
/// The arrival date, check-out date or rental end date for the item. This should be in
|
||||||
|
/// YYYY-MM-DD format (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterEndDate : @"2015-09-14",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterEndDate NS_SWIFT_NAME(AnalyticsParameterEndDate) = @"end_date";
|
||||||
|
|
||||||
|
/// Flight number for travel events (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterFlightNumber : @"ZZ800",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterFlightNumber NS_SWIFT_NAME(AnalyticsParameterFlightNumber) =
|
||||||
|
@"flight_number";
|
||||||
|
|
||||||
|
/// Group/clan/guild ID (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterGroupID : @"g1",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterGroupID NS_SWIFT_NAME(AnalyticsParameterGroupID) = @"group_id";
|
||||||
|
|
||||||
|
/// Index of an item in a list (signed 64-bit integer as NSNumber).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterIndex : @(1),
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterIndex NS_SWIFT_NAME(AnalyticsParameterIndex) = @"index";
|
||||||
|
|
||||||
|
/// Item brand (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterItemBrand : @"Google",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterItemBrand NS_SWIFT_NAME(AnalyticsParameterItemBrand) =
|
||||||
|
@"item_brand";
|
||||||
|
|
||||||
|
/// Item category (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterItemCategory : @"t-shirts",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterItemCategory NS_SWIFT_NAME(AnalyticsParameterItemCategory) =
|
||||||
|
@"item_category";
|
||||||
|
|
||||||
|
/// Item ID (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterItemID : @"p7654",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterItemID NS_SWIFT_NAME(AnalyticsParameterItemID) = @"item_id";
|
||||||
|
|
||||||
|
/// The Google <a href="https://developers.google.com/places/place-id">Place ID</a> (NSString) that
|
||||||
|
/// corresponds to the associated item. Alternatively, you can supply your own custom Location ID.
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterItemLocationID : @"ChIJiyj437sx3YAR9kUWC8QkLzQ",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterItemLocationID
|
||||||
|
NS_SWIFT_NAME(AnalyticsParameterItemLocationID) = @"item_location_id";
|
||||||
|
|
||||||
|
/// Item name (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterItemName : @"abc",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterItemName NS_SWIFT_NAME(AnalyticsParameterItemName) =
|
||||||
|
@"item_name";
|
||||||
|
|
||||||
|
/// The list in which the item was presented to the user (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterItemList : @"Search Results",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterItemList NS_SWIFT_NAME(AnalyticsParameterItemList) =
|
||||||
|
@"item_list";
|
||||||
|
|
||||||
|
/// Item variant (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterItemVariant : @"Red",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterItemVariant NS_SWIFT_NAME(AnalyticsParameterItemVariant) =
|
||||||
|
@"item_variant";
|
||||||
|
|
||||||
|
/// Level in game (signed 64-bit integer as NSNumber).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterLevel : @(42),
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterLevel NS_SWIFT_NAME(AnalyticsParameterLevel) = @"level";
|
||||||
|
|
||||||
|
/// Location (NSString). The Google <a href="https://developers.google.com/places/place-id">Place ID
|
||||||
|
/// </a> that corresponds to the associated event. Alternatively, you can supply your own custom
|
||||||
|
/// Location ID.
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterLocation : @"ChIJiyj437sx3YAR9kUWC8QkLzQ",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterLocation NS_SWIFT_NAME(AnalyticsParameterLocation) =
|
||||||
|
@"location";
|
||||||
|
|
||||||
|
/// The advertising or marketing medium, for example: cpc, banner, email, push. Highly recommended
|
||||||
|
/// (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterMedium : @"email",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterMedium NS_SWIFT_NAME(AnalyticsParameterMedium) = @"medium";
|
||||||
|
|
||||||
|
/// Number of nights staying at hotel (signed 64-bit integer as NSNumber).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterNumberOfNights : @(3),
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterNumberOfNights
|
||||||
|
NS_SWIFT_NAME(AnalyticsParameterNumberOfNights) = @"number_of_nights";
|
||||||
|
|
||||||
|
/// Number of passengers traveling (signed 64-bit integer as NSNumber).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterNumberOfPassengers : @(11),
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterNumberOfPassengers
|
||||||
|
NS_SWIFT_NAME(AnalyticsParameterNumberOfPassengers) = @"number_of_passengers";
|
||||||
|
|
||||||
|
/// Number of rooms for travel events (signed 64-bit integer as NSNumber).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterNumberOfRooms : @(2),
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterNumberOfRooms NS_SWIFT_NAME(AnalyticsParameterNumberOfRooms) =
|
||||||
|
@"number_of_rooms";
|
||||||
|
|
||||||
|
/// Flight or Travel origin (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterOrigin : @"Mountain View, CA",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterOrigin NS_SWIFT_NAME(AnalyticsParameterOrigin) = @"origin";
|
||||||
|
|
||||||
|
/// Purchase price (double as NSNumber).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterPrice : @(1.0),
|
||||||
|
/// kFIRParameterCurrency : @"USD", // e.g. $1.00 USD
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterPrice NS_SWIFT_NAME(AnalyticsParameterPrice) = @"price";
|
||||||
|
|
||||||
|
/// Purchase quantity (signed 64-bit integer as NSNumber).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterQuantity : @(1),
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterQuantity NS_SWIFT_NAME(AnalyticsParameterQuantity) =
|
||||||
|
@"quantity";
|
||||||
|
|
||||||
|
/// Score in game (signed 64-bit integer as NSNumber).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterScore : @(4200),
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterScore NS_SWIFT_NAME(AnalyticsParameterScore) = @"score";
|
||||||
|
|
||||||
|
/// The search string/keywords used (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterSearchTerm : @"periodic table",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterSearchTerm NS_SWIFT_NAME(AnalyticsParameterSearchTerm) =
|
||||||
|
@"search_term";
|
||||||
|
|
||||||
|
/// Shipping cost (double as NSNumber).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterShipping : @(9.50),
|
||||||
|
/// kFIRParameterCurrency : @"USD", // e.g. $9.50 USD
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterShipping NS_SWIFT_NAME(AnalyticsParameterShipping) =
|
||||||
|
@"shipping";
|
||||||
|
|
||||||
|
/// Sign up method (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterSignUpMethod : @"google",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterSignUpMethod NS_SWIFT_NAME(AnalyticsParameterSignUpMethod) =
|
||||||
|
@"sign_up_method";
|
||||||
|
|
||||||
|
/// The origin of your traffic, such as an Ad network (for example, google) or partner (urban
|
||||||
|
/// airship). Identify the advertiser, site, publication, etc. that is sending traffic to your
|
||||||
|
/// property. Highly recommended (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterSource : @"InMobi",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterSource NS_SWIFT_NAME(AnalyticsParameterSource) = @"source";
|
||||||
|
|
||||||
|
/// The departure date, check-in date or rental start date for the item. This should be in
|
||||||
|
/// YYYY-MM-DD format (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterStartDate : @"2015-09-14",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterStartDate NS_SWIFT_NAME(AnalyticsParameterStartDate) =
|
||||||
|
@"start_date";
|
||||||
|
|
||||||
|
/// Tax amount (double as NSNumber).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterTax : @(1.0),
|
||||||
|
/// kFIRParameterCurrency : @"USD", // e.g. $1.00 USD
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterTax NS_SWIFT_NAME(AnalyticsParameterTax) = @"tax";
|
||||||
|
|
||||||
|
/// If you're manually tagging keyword campaigns, you should use utm_term to specify the keyword
|
||||||
|
/// (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterTerm : @"game",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterTerm NS_SWIFT_NAME(AnalyticsParameterTerm) = @"term";
|
||||||
|
|
||||||
|
/// A single ID for a ecommerce group transaction (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterTransactionID : @"ab7236dd9823",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterTransactionID NS_SWIFT_NAME(AnalyticsParameterTransactionID) =
|
||||||
|
@"transaction_id";
|
||||||
|
|
||||||
|
/// Travel class (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterTravelClass : @"business",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterTravelClass NS_SWIFT_NAME(AnalyticsParameterTravelClass) =
|
||||||
|
@"travel_class";
|
||||||
|
|
||||||
|
/// A context-specific numeric value which is accumulated automatically for each event type. This is
|
||||||
|
/// a general purpose parameter that is useful for accumulating a key metric that pertains to an
|
||||||
|
/// event. Examples include revenue, distance, time and points. Value should be specified as signed
|
||||||
|
/// 64-bit integer or double as NSNumber. Notes: Values for pre-defined currency-related events
|
||||||
|
/// (such as @c kFIREventAddToCart) should be supplied using double as NSNumber and must be
|
||||||
|
/// accompanied by a @c kFIRParameterCurrency parameter. The valid range of accumulated values is
|
||||||
|
/// [-9,223,372,036,854.77, 9,223,372,036,854.77]. Supplying a non-numeric value, omitting the
|
||||||
|
/// corresponding @c kFIRParameterCurrency parameter, or supplying an invalid
|
||||||
|
/// <a href="https://goo.gl/qqX3J2">currency code</a> for conversion events will cause that
|
||||||
|
/// conversion to be omitted from reporting.
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterValue : @(3.99),
|
||||||
|
/// kFIRParameterCurrency : @"USD", // e.g. $3.99 USD
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterValue NS_SWIFT_NAME(AnalyticsParameterValue) = @"value";
|
||||||
|
|
||||||
|
/// Name of virtual currency type (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterVirtualCurrencyName : @"virtual_currency_name",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterVirtualCurrencyName
|
||||||
|
NS_SWIFT_NAME(AnalyticsParameterVirtualCurrencyName) = @"virtual_currency_name";
|
||||||
|
|
||||||
|
/// The name of a level in a game (NSString).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterLevelName : @"room_1",
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterLevelName NS_SWIFT_NAME(AnalyticsParameterLevelName) =
|
||||||
|
@"level_name";
|
||||||
|
|
||||||
|
/// The result of an operation. Specify 1 to indicate success and 0 to indicate failure (unsigned
|
||||||
|
/// integer as NSNumber).
|
||||||
|
/// <pre>
|
||||||
|
/// NSDictionary *params = @{
|
||||||
|
/// kFIRParameterSuccess : @(1),
|
||||||
|
/// // ...
|
||||||
|
/// };
|
||||||
|
/// </pre>
|
||||||
|
static NSString *const kFIRParameterSuccess NS_SWIFT_NAME(AnalyticsParameterSuccess) = @"success";
|
17
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRUserPropertyNames.h
generated
Executable file
17
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRUserPropertyNames.h
generated
Executable file
|
@ -0,0 +1,17 @@
|
||||||
|
/// @file FIRUserPropertyNames.h
|
||||||
|
///
|
||||||
|
/// Predefined user property names.
|
||||||
|
///
|
||||||
|
/// A UserProperty is an attribute that describes the app-user. By supplying UserProperties, you can
|
||||||
|
/// later analyze different behaviors of various segments of your userbase. You may supply up to 25
|
||||||
|
/// unique UserProperties per app, and you can use the name and value of your choosing for each one.
|
||||||
|
/// UserProperty names can be up to 24 characters long, may only contain alphanumeric characters and
|
||||||
|
/// underscores ("_"), and must start with an alphabetic character. UserProperty values can be up to
|
||||||
|
/// 36 characters long. The "firebase_", "google_", and "ga_" prefixes are reserved and should not
|
||||||
|
/// be used.
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
/// The method used to sign in. For example, "google", "facebook" or "twitter".
|
||||||
|
static NSString *const kFIRUserPropertySignUpMethod
|
||||||
|
NS_SWIFT_NAME(AnalyticsUserPropertySignUpMethod) = @"sign_up_method";
|
6
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FirebaseAnalytics.h
generated
Executable file
6
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FirebaseAnalytics.h
generated
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
#import "FIRAnalytics+AppDelegate.h"
|
||||||
|
#import "FIRAnalytics.h"
|
||||||
|
#import "FIRAnalyticsSwiftNameSupport.h"
|
||||||
|
#import "FIREventNames.h"
|
||||||
|
#import "FIRParameterNames.h"
|
||||||
|
#import "FIRUserPropertyNames.h"
|
10
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Modules/module.modulemap
generated
Executable file
10
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Modules/module.modulemap
generated
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
framework module FirebaseAnalytics {
|
||||||
|
umbrella header "FirebaseAnalytics.h"
|
||||||
|
export *
|
||||||
|
module * { export *}
|
||||||
|
link "sqlite3"
|
||||||
|
link "z"
|
||||||
|
link framework "Security"
|
||||||
|
link framework "StoreKit"
|
||||||
|
link framework "SystemConfiguration"
|
||||||
|
link framework "UIKit"}
|
BIN
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseCoreDiagnostics.framework/FirebaseCoreDiagnostics
generated
Executable file
BIN
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseCoreDiagnostics.framework/FirebaseCoreDiagnostics
generated
Executable file
Binary file not shown.
6
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseCoreDiagnostics.framework/Modules/module.modulemap
generated
Executable file
6
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseCoreDiagnostics.framework/Modules/module.modulemap
generated
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
framework module FirebaseCoreDiagnostics {
|
||||||
|
export *
|
||||||
|
module * { export *}
|
||||||
|
link "z"
|
||||||
|
link framework "Security"
|
||||||
|
link framework "SystemConfiguration"}
|
BIN
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseNanoPB.framework/FirebaseNanoPB
generated
Executable file
BIN
Verdnaturaventas/Pods/FirebaseAnalytics/Frameworks/FirebaseNanoPB.framework/FirebaseNanoPB
generated
Executable file
Binary file not shown.
69
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/FIRAnalyticsConfiguration.m
generated
Normal file
69
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/FIRAnalyticsConfiguration.m
generated
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
// Copyright 2017 Google
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#import "FIRAnalyticsConfiguration.h"
|
||||||
|
|
||||||
|
#import "Private/FIRAnalyticsConfiguration+Internal.h"
|
||||||
|
|
||||||
|
@implementation FIRAnalyticsConfiguration
|
||||||
|
|
||||||
|
+ (FIRAnalyticsConfiguration *)sharedInstance {
|
||||||
|
static FIRAnalyticsConfiguration *sharedInstance = nil;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
sharedInstance = [[FIRAnalyticsConfiguration alloc] init];
|
||||||
|
});
|
||||||
|
return sharedInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)postNotificationName:(NSString *)name value:(id)value {
|
||||||
|
if (!name.length || !value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:name
|
||||||
|
object:self
|
||||||
|
userInfo:@{name : value}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setMinimumSessionInterval:(NSTimeInterval)minimumSessionInterval {
|
||||||
|
[self postNotificationName:kFIRAnalyticsConfigurationSetMinimumSessionIntervalNotification
|
||||||
|
value:@(minimumSessionInterval)];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setSessionTimeoutInterval:(NSTimeInterval)sessionTimeoutInterval {
|
||||||
|
[self postNotificationName:kFIRAnalyticsConfigurationSetSessionTimeoutIntervalNotification
|
||||||
|
value:@(sessionTimeoutInterval)];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled {
|
||||||
|
[self setAnalyticsCollectionEnabled:analyticsCollectionEnabled persistSetting:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled
|
||||||
|
persistSetting:(BOOL)shouldPersist {
|
||||||
|
// Persist the measurementEnabledState. Use FIRAnalyticsEnabledState values instead of YES/NO.
|
||||||
|
FIRAnalyticsEnabledState analyticsEnabledState =
|
||||||
|
analyticsCollectionEnabled ? kFIRAnalyticsEnabledStateSetYes : kFIRAnalyticsEnabledStateSetNo;
|
||||||
|
if (shouldPersist) {
|
||||||
|
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
|
||||||
|
[userDefaults setObject:@(analyticsEnabledState)
|
||||||
|
forKey:kFIRAPersistedConfigMeasurementEnabledStateKey];
|
||||||
|
[userDefaults synchronize];
|
||||||
|
}
|
||||||
|
|
||||||
|
[self postNotificationName:kFIRAnalyticsConfigurationSetEnabledNotification
|
||||||
|
value:@(analyticsCollectionEnabled)];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,737 @@
|
||||||
|
// Copyright 2017 Google
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <sys/utsname.h>
|
||||||
|
|
||||||
|
#import "FIRApp.h"
|
||||||
|
#import "FIRConfiguration.h"
|
||||||
|
#import "Private/FIRAnalyticsConfiguration+Internal.h"
|
||||||
|
#import "Private/FIRAppInternal.h"
|
||||||
|
#import "Private/FIRBundleUtil.h"
|
||||||
|
#import "Private/FIRLogger.h"
|
||||||
|
#import "Private/FIROptionsInternal.h"
|
||||||
|
#import "third_party/FIRAppEnvironmentUtil.h"
|
||||||
|
|
||||||
|
NSString *const kFIRServiceAdMob = @"AdMob";
|
||||||
|
NSString *const kFIRServiceAuth = @"Auth";
|
||||||
|
NSString *const kFIRServiceAuthUI = @"AuthUI";
|
||||||
|
NSString *const kFIRServiceCrash = @"Crash";
|
||||||
|
NSString *const kFIRServiceDatabase = @"Database";
|
||||||
|
NSString *const kFIRServiceDynamicLinks = @"DynamicLinks";
|
||||||
|
NSString *const kFIRServiceFirestore = @"Firestore";
|
||||||
|
NSString *const kFIRServiceFunctions = @"Functions";
|
||||||
|
NSString *const kFIRServiceInstanceID = @"InstanceID";
|
||||||
|
NSString *const kFIRServiceInvites = @"Invites";
|
||||||
|
NSString *const kFIRServiceMessaging = @"Messaging";
|
||||||
|
NSString *const kFIRServiceMeasurement = @"Measurement";
|
||||||
|
NSString *const kFIRServicePerformance = @"Performance";
|
||||||
|
NSString *const kFIRServiceRemoteConfig = @"RemoteConfig";
|
||||||
|
NSString *const kFIRServiceStorage = @"Storage";
|
||||||
|
NSString *const kGGLServiceAnalytics = @"Analytics";
|
||||||
|
NSString *const kGGLServiceSignIn = @"SignIn";
|
||||||
|
|
||||||
|
NSString *const kFIRDefaultAppName = @"__FIRAPP_DEFAULT";
|
||||||
|
NSString *const kFIRAppReadyToConfigureSDKNotification = @"FIRAppReadyToConfigureSDKNotification";
|
||||||
|
NSString *const kFIRAppDeleteNotification = @"FIRAppDeleteNotification";
|
||||||
|
NSString *const kFIRAppIsDefaultAppKey = @"FIRAppIsDefaultAppKey";
|
||||||
|
NSString *const kFIRAppNameKey = @"FIRAppNameKey";
|
||||||
|
NSString *const kFIRGoogleAppIDKey = @"FIRGoogleAppIDKey";
|
||||||
|
|
||||||
|
NSString *const kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat =
|
||||||
|
@"/google/firebase/global_data_collection_enabled:%@";
|
||||||
|
NSString *const kFIRGlobalAppDataCollectionEnabledPlistKey =
|
||||||
|
@"FirebaseAutomaticDataCollectionEnabled";
|
||||||
|
|
||||||
|
NSString *const kFIRAppDiagnosticsNotification = @"FIRAppDiagnosticsNotification";
|
||||||
|
|
||||||
|
NSString *const kFIRAppDiagnosticsConfigurationTypeKey = @"ConfigType";
|
||||||
|
NSString *const kFIRAppDiagnosticsErrorKey = @"Error";
|
||||||
|
NSString *const kFIRAppDiagnosticsFIRAppKey = @"FIRApp";
|
||||||
|
NSString *const kFIRAppDiagnosticsSDKNameKey = @"SDKName";
|
||||||
|
NSString *const kFIRAppDiagnosticsSDKVersionKey = @"SDKVersion";
|
||||||
|
|
||||||
|
// Auth internal notification notification and key.
|
||||||
|
NSString *const FIRAuthStateDidChangeInternalNotification =
|
||||||
|
@"FIRAuthStateDidChangeInternalNotification";
|
||||||
|
NSString *const FIRAuthStateDidChangeInternalNotificationAppKey =
|
||||||
|
@"FIRAuthStateDidChangeInternalNotificationAppKey";
|
||||||
|
NSString *const FIRAuthStateDidChangeInternalNotificationTokenKey =
|
||||||
|
@"FIRAuthStateDidChangeInternalNotificationTokenKey";
|
||||||
|
NSString *const FIRAuthStateDidChangeInternalNotificationUIDKey =
|
||||||
|
@"FIRAuthStateDidChangeInternalNotificationUIDKey";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URL to download plist files.
|
||||||
|
*/
|
||||||
|
static NSString *const kPlistURL = @"https://console.firebase.google.com/";
|
||||||
|
|
||||||
|
@interface FIRApp ()
|
||||||
|
|
||||||
|
@property(nonatomic) BOOL alreadySentConfigureNotification;
|
||||||
|
|
||||||
|
@property(nonatomic) BOOL alreadySentDeleteNotification;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FIRApp
|
||||||
|
|
||||||
|
// This is necessary since our custom getter prevents `_options` from being created.
|
||||||
|
@synthesize options = _options;
|
||||||
|
|
||||||
|
static NSMutableDictionary *sAllApps;
|
||||||
|
static FIRApp *sDefaultApp;
|
||||||
|
static NSMutableDictionary *sLibraryVersions;
|
||||||
|
|
||||||
|
+ (void)configure {
|
||||||
|
FIROptions *options = [FIROptions defaultOptions];
|
||||||
|
if (!options) {
|
||||||
|
[[NSNotificationCenter defaultCenter]
|
||||||
|
postNotificationName:kFIRAppDiagnosticsNotification
|
||||||
|
object:nil
|
||||||
|
userInfo:@{
|
||||||
|
kFIRAppDiagnosticsConfigurationTypeKey : @(FIRConfigTypeCore),
|
||||||
|
kFIRAppDiagnosticsErrorKey : [FIRApp errorForMissingOptions]
|
||||||
|
}];
|
||||||
|
[NSException raise:kFirebaseCoreErrorDomain
|
||||||
|
format:
|
||||||
|
@"`[FIRApp configure];` (`FirebaseApp.configure()` in Swift) could not find "
|
||||||
|
@"a valid GoogleService-Info.plist in your project. Please download one "
|
||||||
|
@"from %@.",
|
||||||
|
kPlistURL];
|
||||||
|
}
|
||||||
|
[FIRApp configureDefaultAppWithOptions:options sendingNotifications:YES];
|
||||||
|
#if TARGET_OS_OSX || TARGET_OS_TV
|
||||||
|
FIRLogNotice(kFIRLoggerCore, @"I-COR000028",
|
||||||
|
@"tvOS and macOS SDK support is not part of the official Firebase product. "
|
||||||
|
@"Instead they are community supported. Details at "
|
||||||
|
@"https://github.com/firebase/firebase-ios-sdk/blob/master/README.md.");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)configureWithOptions:(FIROptions *)options {
|
||||||
|
if (!options) {
|
||||||
|
[NSException raise:kFirebaseCoreErrorDomain
|
||||||
|
format:@"Options is nil. Please pass a valid options."];
|
||||||
|
}
|
||||||
|
[FIRApp configureDefaultAppWithOptions:options sendingNotifications:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)configureDefaultAppWithOptions:(FIROptions *)options
|
||||||
|
sendingNotifications:(BOOL)sendNotifications {
|
||||||
|
if (sDefaultApp) {
|
||||||
|
// FIRApp sets up FirebaseAnalytics and does plist validation, but does not cause it
|
||||||
|
// to fire notifications. So, if the default app already exists, but has not sent out
|
||||||
|
// configuration notifications, then continue re-initializing it.
|
||||||
|
if (!sendNotifications || sDefaultApp.alreadySentConfigureNotification) {
|
||||||
|
[NSException raise:kFirebaseCoreErrorDomain
|
||||||
|
format:@"Default app has already been configured."];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@synchronized(self) {
|
||||||
|
FIRLogDebug(kFIRLoggerCore, @"I-COR000001", @"Configuring the default app.");
|
||||||
|
sDefaultApp = [[FIRApp alloc] initInstanceWithName:kFIRDefaultAppName options:options];
|
||||||
|
[FIRApp addAppToAppDictionary:sDefaultApp];
|
||||||
|
if (!sDefaultApp.alreadySentConfigureNotification && sendNotifications) {
|
||||||
|
[FIRApp sendNotificationsToSDKs:sDefaultApp];
|
||||||
|
sDefaultApp.alreadySentConfigureNotification = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)configureWithName:(NSString *)name options:(FIROptions *)options {
|
||||||
|
if (!name || !options) {
|
||||||
|
[NSException raise:kFirebaseCoreErrorDomain format:@"Neither name nor options can be nil."];
|
||||||
|
}
|
||||||
|
if (name.length == 0) {
|
||||||
|
[NSException raise:kFirebaseCoreErrorDomain format:@"Name cannot be empty."];
|
||||||
|
}
|
||||||
|
if ([name isEqualToString:kFIRDefaultAppName]) {
|
||||||
|
[NSException raise:kFirebaseCoreErrorDomain format:@"Name cannot be __FIRAPP_DEFAULT."];
|
||||||
|
}
|
||||||
|
for (NSInteger charIndex = 0; charIndex < name.length; charIndex++) {
|
||||||
|
char character = [name characterAtIndex:charIndex];
|
||||||
|
if (!((character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z') ||
|
||||||
|
(character >= '0' && character <= '9') || character == '_' || character == '-')) {
|
||||||
|
[NSException raise:kFirebaseCoreErrorDomain
|
||||||
|
format:
|
||||||
|
@"App name should only contain Letters, "
|
||||||
|
@"Numbers, Underscores, and Dashes."];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sAllApps && sAllApps[name]) {
|
||||||
|
[NSException raise:kFirebaseCoreErrorDomain
|
||||||
|
format:@"App named %@ has already been configured.", name];
|
||||||
|
}
|
||||||
|
|
||||||
|
@synchronized(self) {
|
||||||
|
FIRLogDebug(kFIRLoggerCore, @"I-COR000002", @"Configuring app named %@", name);
|
||||||
|
FIRApp *app = [[FIRApp alloc] initInstanceWithName:name options:options];
|
||||||
|
[FIRApp addAppToAppDictionary:app];
|
||||||
|
if (!app.alreadySentConfigureNotification) {
|
||||||
|
[FIRApp sendNotificationsToSDKs:app];
|
||||||
|
app.alreadySentConfigureNotification = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (FIRApp *)defaultApp {
|
||||||
|
if (sDefaultApp) {
|
||||||
|
return sDefaultApp;
|
||||||
|
}
|
||||||
|
FIRLogError(kFIRLoggerCore, @"I-COR000003",
|
||||||
|
@"The default Firebase app has not yet been "
|
||||||
|
@"configured. Add `[FIRApp configure];` (`FirebaseApp.configure()` in Swift) to your "
|
||||||
|
@"application initialization. Read more: https://goo.gl/ctyzm8.");
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (FIRApp *)appNamed:(NSString *)name {
|
||||||
|
@synchronized(self) {
|
||||||
|
if (sAllApps) {
|
||||||
|
FIRApp *app = sAllApps[name];
|
||||||
|
if (app) {
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FIRLogError(kFIRLoggerCore, @"I-COR000004", @"App with name %@ does not exist.", name);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSDictionary *)allApps {
|
||||||
|
@synchronized(self) {
|
||||||
|
if (!sAllApps) {
|
||||||
|
FIRLogError(kFIRLoggerCore, @"I-COR000005", @"No app has been configured yet.");
|
||||||
|
}
|
||||||
|
NSDictionary *dict = [NSDictionary dictionaryWithDictionary:sAllApps];
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public only for tests
|
||||||
|
+ (void)resetApps {
|
||||||
|
sDefaultApp = nil;
|
||||||
|
[sAllApps removeAllObjects];
|
||||||
|
sAllApps = nil;
|
||||||
|
[sLibraryVersions removeAllObjects];
|
||||||
|
sLibraryVersions = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)deleteApp:(FIRAppVoidBoolCallback)completion {
|
||||||
|
@synchronized([self class]) {
|
||||||
|
if (sAllApps && sAllApps[self.name]) {
|
||||||
|
FIRLogDebug(kFIRLoggerCore, @"I-COR000006", @"Deleting app named %@", self.name);
|
||||||
|
[sAllApps removeObjectForKey:self.name];
|
||||||
|
[self clearDataCollectionSwitchFromUserDefaults];
|
||||||
|
if ([self.name isEqualToString:kFIRDefaultAppName]) {
|
||||||
|
sDefaultApp = nil;
|
||||||
|
}
|
||||||
|
if (!self.alreadySentDeleteNotification) {
|
||||||
|
NSDictionary *appInfoDict = @{kFIRAppNameKey : self.name};
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:kFIRAppDeleteNotification
|
||||||
|
object:[self class]
|
||||||
|
userInfo:appInfoDict];
|
||||||
|
self.alreadySentDeleteNotification = YES;
|
||||||
|
}
|
||||||
|
completion(YES);
|
||||||
|
} else {
|
||||||
|
FIRLogError(kFIRLoggerCore, @"I-COR000007", @"App does not exist.");
|
||||||
|
completion(NO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)addAppToAppDictionary:(FIRApp *)app {
|
||||||
|
if (!sAllApps) {
|
||||||
|
sAllApps = [NSMutableDictionary dictionary];
|
||||||
|
}
|
||||||
|
if ([app configureCore]) {
|
||||||
|
sAllApps[app.name] = app;
|
||||||
|
[[NSNotificationCenter defaultCenter]
|
||||||
|
postNotificationName:kFIRAppDiagnosticsNotification
|
||||||
|
object:nil
|
||||||
|
userInfo:@{
|
||||||
|
kFIRAppDiagnosticsConfigurationTypeKey : @(FIRConfigTypeCore),
|
||||||
|
kFIRAppDiagnosticsFIRAppKey : app
|
||||||
|
}];
|
||||||
|
} else {
|
||||||
|
[NSException raise:kFirebaseCoreErrorDomain
|
||||||
|
format:
|
||||||
|
@"Configuration fails. It may be caused by an invalid GOOGLE_APP_ID in "
|
||||||
|
@"GoogleService-Info.plist or set in the customized options."];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initInstanceWithName:(NSString *)name options:(FIROptions *)options {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_name = [name copy];
|
||||||
|
_options = [options copy];
|
||||||
|
_options.editingLocked = YES;
|
||||||
|
|
||||||
|
FIRApp *app = sAllApps[name];
|
||||||
|
_alreadySentConfigureNotification = app.alreadySentConfigureNotification;
|
||||||
|
_alreadySentDeleteNotification = app.alreadySentDeleteNotification;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)getTokenForcingRefresh:(BOOL)forceRefresh withCallback:(FIRTokenCallback)callback {
|
||||||
|
if (!_getTokenImplementation) {
|
||||||
|
callback(nil, nil);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getTokenImplementation(forceRefresh, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)configureCore {
|
||||||
|
[self checkExpectedBundleID];
|
||||||
|
if (![self isAppIDValid]) {
|
||||||
|
if (_options.usingOptionsFromDefaultPlist) {
|
||||||
|
[[NSNotificationCenter defaultCenter]
|
||||||
|
postNotificationName:kFIRAppDiagnosticsNotification
|
||||||
|
object:nil
|
||||||
|
userInfo:@{
|
||||||
|
kFIRAppDiagnosticsConfigurationTypeKey : @(FIRConfigTypeCore),
|
||||||
|
kFIRAppDiagnosticsErrorKey : [FIRApp errorForInvalidAppID],
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the Analytics once there is a valid options under default app. Analytics should
|
||||||
|
// always initialize first by itself before the other SDKs.
|
||||||
|
if ([self.name isEqualToString:kFIRDefaultAppName]) {
|
||||||
|
Class firAnalyticsClass = NSClassFromString(@"FIRAnalytics");
|
||||||
|
if (!firAnalyticsClass) {
|
||||||
|
FIRLogError(kFIRLoggerCore, @"I-COR000022", @"Firebase Analytics is not available.");
|
||||||
|
} else {
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wundeclared-selector"
|
||||||
|
SEL startWithConfigurationSelector = @selector(startWithConfiguration:options:);
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
if ([firAnalyticsClass respondsToSelector:startWithConfigurationSelector]) {
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||||
|
[firAnalyticsClass performSelector:startWithConfigurationSelector
|
||||||
|
withObject:[FIRConfiguration sharedInstance].analyticsConfiguration
|
||||||
|
withObject:_options];
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (FIROptions *)options {
|
||||||
|
return [_options copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setAutomaticDataCollectionEnabled:(BOOL)automaticDataCollectionEnabled {
|
||||||
|
NSString *key =
|
||||||
|
[NSString stringWithFormat:kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat, self.name];
|
||||||
|
[[NSUserDefaults standardUserDefaults] setBool:automaticDataCollectionEnabled forKey:key];
|
||||||
|
|
||||||
|
// Core also controls the FirebaseAnalytics flag, so check if the Analytics flags are set
|
||||||
|
// within FIROptions and change the Analytics value if necessary. Analytics only works with the
|
||||||
|
// default app, so return if this isn't the default app.
|
||||||
|
if (self != sDefaultApp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the Analytics flag is explicitly set. If so, no further actions are necessary.
|
||||||
|
if ([self.options isAnalyticsCollectionExpicitlySet]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Analytics flag has not been explicitly set, so update with the value being set.
|
||||||
|
[[FIRAnalyticsConfiguration sharedInstance]
|
||||||
|
setAnalyticsCollectionEnabled:automaticDataCollectionEnabled
|
||||||
|
persistSetting:NO];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isAutomaticDataCollectionEnabled {
|
||||||
|
// Check if it's been manually set before in code, and use that as the higher priority value.
|
||||||
|
NSNumber *defaultsObject = [[self class] readDataCollectionSwitchFromUserDefaultsForApp:self];
|
||||||
|
if (defaultsObject) {
|
||||||
|
return [defaultsObject boolValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the Info.plist to see if the flag is set. If it's not set, it should default to `YES`.
|
||||||
|
// As per the implementation of `readDataCollectionSwitchFromPlist`, it's a cached value and has
|
||||||
|
// no performance impact calling multiple times.
|
||||||
|
NSNumber *collectionEnabledPlistValue = [[self class] readDataCollectionSwitchFromPlist];
|
||||||
|
if (collectionEnabledPlistValue) {
|
||||||
|
return [collectionEnabledPlistValue boolValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - private
|
||||||
|
|
||||||
|
+ (void)sendNotificationsToSDKs:(FIRApp *)app {
|
||||||
|
NSNumber *isDefaultApp = [NSNumber numberWithBool:(app == sDefaultApp)];
|
||||||
|
NSDictionary *appInfoDict = @{
|
||||||
|
kFIRAppNameKey : app.name,
|
||||||
|
kFIRAppIsDefaultAppKey : isDefaultApp,
|
||||||
|
kFIRGoogleAppIDKey : app.options.googleAppID
|
||||||
|
};
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:kFIRAppReadyToConfigureSDKNotification
|
||||||
|
object:self
|
||||||
|
userInfo:appInfoDict];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSError *)errorForMissingOptions {
|
||||||
|
NSDictionary *errorDict = @{
|
||||||
|
NSLocalizedDescriptionKey :
|
||||||
|
@"Unable to parse GoogleService-Info.plist in order to configure services.",
|
||||||
|
NSLocalizedRecoverySuggestionErrorKey :
|
||||||
|
@"Check formatting and location of GoogleService-Info.plist."
|
||||||
|
};
|
||||||
|
return [NSError errorWithDomain:kFirebaseCoreErrorDomain
|
||||||
|
code:FIRErrorCodeInvalidPlistFile
|
||||||
|
userInfo:errorDict];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSError *)errorForSubspecConfigurationFailureWithDomain:(NSString *)domain
|
||||||
|
errorCode:(FIRErrorCode)code
|
||||||
|
service:(NSString *)service
|
||||||
|
reason:(NSString *)reason {
|
||||||
|
NSString *description =
|
||||||
|
[NSString stringWithFormat:@"Configuration failed for service %@.", service];
|
||||||
|
NSDictionary *errorDict =
|
||||||
|
@{NSLocalizedDescriptionKey : description, NSLocalizedFailureReasonErrorKey : reason};
|
||||||
|
return [NSError errorWithDomain:domain code:code userInfo:errorDict];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSError *)errorForInvalidAppID {
|
||||||
|
NSDictionary *errorDict = @{
|
||||||
|
NSLocalizedDescriptionKey : @"Unable to validate Google App ID",
|
||||||
|
NSLocalizedRecoverySuggestionErrorKey :
|
||||||
|
@"Check formatting and location of GoogleService-Info.plist or GoogleAppID set in the "
|
||||||
|
@"customized options."
|
||||||
|
};
|
||||||
|
return [NSError errorWithDomain:kFirebaseCoreErrorDomain
|
||||||
|
code:FIRErrorCodeInvalidAppID
|
||||||
|
userInfo:errorDict];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (BOOL)isDefaultAppConfigured {
|
||||||
|
return (sDefaultApp != nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)registerLibrary:(nonnull NSString *)library withVersion:(nonnull NSString *)version {
|
||||||
|
// Create the set of characters which aren't allowed, only if this feature is used.
|
||||||
|
NSMutableCharacterSet *allowedSet = [NSMutableCharacterSet alphanumericCharacterSet];
|
||||||
|
[allowedSet addCharactersInString:@"-_."];
|
||||||
|
NSCharacterSet *disallowedSet = [allowedSet invertedSet];
|
||||||
|
// Make sure the library name and version strings do not contain unexpected characters, and
|
||||||
|
// add the name/version pair to the dictionary.
|
||||||
|
if ([library rangeOfCharacterFromSet:disallowedSet].location == NSNotFound &&
|
||||||
|
[version rangeOfCharacterFromSet:disallowedSet].location == NSNotFound) {
|
||||||
|
if (!sLibraryVersions) {
|
||||||
|
sLibraryVersions = [[NSMutableDictionary alloc] init];
|
||||||
|
}
|
||||||
|
sLibraryVersions[library] = version;
|
||||||
|
} else {
|
||||||
|
FIRLogError(kFIRLoggerCore, @"I-COR000027",
|
||||||
|
@"The library name (%@) or version number (%@) contain illegal characters. "
|
||||||
|
@"Only alphanumeric, dash, underscore and period characters are allowed.",
|
||||||
|
library, version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSString *)firebaseUserAgent {
|
||||||
|
NSMutableArray<NSString *> *libraries =
|
||||||
|
[[NSMutableArray<NSString *> alloc] initWithCapacity:sLibraryVersions.count];
|
||||||
|
for (NSString *libraryName in sLibraryVersions) {
|
||||||
|
[libraries
|
||||||
|
addObject:[NSString stringWithFormat:@"%@/%@", libraryName, sLibraryVersions[libraryName]]];
|
||||||
|
}
|
||||||
|
[libraries sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
|
||||||
|
return [libraries componentsJoinedByString:@" "];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)checkExpectedBundleID {
|
||||||
|
NSArray *bundles = [FIRBundleUtil relevantBundles];
|
||||||
|
NSString *expectedBundleID = [self expectedBundleID];
|
||||||
|
// The checking is only done when the bundle ID is provided in the serviceInfo dictionary for
|
||||||
|
// backward compatibility.
|
||||||
|
if (expectedBundleID != nil &&
|
||||||
|
![FIRBundleUtil hasBundleIdentifier:expectedBundleID inBundles:bundles]) {
|
||||||
|
FIRLogError(kFIRLoggerCore, @"I-COR000008",
|
||||||
|
@"The project's Bundle ID is inconsistent with "
|
||||||
|
@"either the Bundle ID in '%@.%@', or the Bundle ID in the options if you are "
|
||||||
|
@"using a customized options. To ensure that everything can be configured "
|
||||||
|
@"correctly, you may need to make the Bundle IDs consistent. To continue with this "
|
||||||
|
@"plist file, you may change your app's bundle identifier to '%@'. Or you can "
|
||||||
|
@"download a new configuration file that matches your bundle identifier from %@ "
|
||||||
|
@"and replace the current one.",
|
||||||
|
kServiceInfoFileName, kServiceInfoFileType, expectedBundleID, kPlistURL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable NSString *)getUID {
|
||||||
|
if (!_getUIDImplementation) {
|
||||||
|
FIRLogWarning(kFIRLoggerCore, @"I-COR000025", @"FIRAuth getUID implementation wasn't set.");
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
return _getUIDImplementation();
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - private - App ID Validation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the format and fingerprint of the app ID contained in GOOGLE_APP_ID in the plist file.
|
||||||
|
* This is the main method for validating app ID.
|
||||||
|
*
|
||||||
|
* @return YES if the app ID fulfills the expected format and fingerprint, NO otherwise.
|
||||||
|
*/
|
||||||
|
- (BOOL)isAppIDValid {
|
||||||
|
NSString *appID = _options.googleAppID;
|
||||||
|
BOOL isValid = [FIRApp validateAppID:appID];
|
||||||
|
if (!isValid) {
|
||||||
|
NSString *expectedBundleID = [self expectedBundleID];
|
||||||
|
FIRLogError(kFIRLoggerCore, @"I-COR000009",
|
||||||
|
@"The GOOGLE_APP_ID either in the plist file "
|
||||||
|
@"'%@.%@' or the one set in the customized options is invalid. If you are using "
|
||||||
|
@"the plist file, use the iOS version of bundle identifier to download the file, "
|
||||||
|
@"and do not manually edit the GOOGLE_APP_ID. You may change your app's bundle "
|
||||||
|
@"identifier to '%@'. Or you can download a new configuration file that matches "
|
||||||
|
@"your bundle identifier from %@ and replace the current one.",
|
||||||
|
kServiceInfoFileName, kServiceInfoFileType, expectedBundleID, kPlistURL);
|
||||||
|
};
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (BOOL)validateAppID:(NSString *)appID {
|
||||||
|
// Failing validation only occurs when we are sure we are looking at a V2 app ID and it does not
|
||||||
|
// have a valid fingerprint, otherwise we just warn about the potential issue.
|
||||||
|
if (!appID.length) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All app IDs must start with at least "<version number>:".
|
||||||
|
NSString *const versionPattern = @"^\\d+:";
|
||||||
|
NSRegularExpression *versionRegex =
|
||||||
|
[NSRegularExpression regularExpressionWithPattern:versionPattern options:0 error:NULL];
|
||||||
|
if (!versionRegex) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSRange appIDRange = NSMakeRange(0, appID.length);
|
||||||
|
NSArray *versionMatches = [versionRegex matchesInString:appID options:0 range:appIDRange];
|
||||||
|
if (versionMatches.count != 1) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSRange versionRange = [(NSTextCheckingResult *)versionMatches.firstObject range];
|
||||||
|
NSString *appIDVersion = [appID substringWithRange:versionRange];
|
||||||
|
NSArray *knownVersions = @[ @"1:" ];
|
||||||
|
if (![knownVersions containsObject:appIDVersion]) {
|
||||||
|
// Permit unknown yet properly formatted app ID versions.
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (![FIRApp validateAppIDFormat:appID withVersion:appIDVersion]) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (![FIRApp validateAppIDFingerprint:appID withVersion:appIDVersion]) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSString *)actualBundleID {
|
||||||
|
return [[NSBundle mainBundle] bundleIdentifier];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates that the format of the app ID string is what is expected based on the supplied version.
|
||||||
|
* The version must end in ":".
|
||||||
|
*
|
||||||
|
* For v1 app ids the format is expected to be
|
||||||
|
* '<version #>:<project number>:ios:<fingerprint of bundle id>'.
|
||||||
|
*
|
||||||
|
* This method does not verify that the contents of the app id are correct, just that they fulfill
|
||||||
|
* the expected format.
|
||||||
|
*
|
||||||
|
* @param appID Contents of GOOGLE_APP_ID from the plist file.
|
||||||
|
* @param version Indicates what version of the app id format this string should be.
|
||||||
|
* @return YES if provided string fufills the expected format, NO otherwise.
|
||||||
|
*/
|
||||||
|
+ (BOOL)validateAppIDFormat:(NSString *)appID withVersion:(NSString *)version {
|
||||||
|
if (!appID.length || !version.length) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (![version hasSuffix:@":"]) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (![appID hasPrefix:version]) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *const pattern = @"^\\d+:ios:[a-f0-9]+$";
|
||||||
|
NSRegularExpression *regex =
|
||||||
|
[NSRegularExpression regularExpressionWithPattern:pattern options:0 error:NULL];
|
||||||
|
if (!regex) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSRange localRange = NSMakeRange(version.length, appID.length - version.length);
|
||||||
|
NSUInteger numberOfMatches = [regex numberOfMatchesInString:appID options:0 range:localRange];
|
||||||
|
if (numberOfMatches != 1) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates that the fingerprint of the app ID string is what is expected based on the supplied
|
||||||
|
* version. The version must end in ":".
|
||||||
|
*
|
||||||
|
* Note that the v1 hash algorithm is not permitted on the client and cannot be fully validated.
|
||||||
|
*
|
||||||
|
* @param appID Contents of GOOGLE_APP_ID from the plist file.
|
||||||
|
* @param version Indicates what version of the app id format this string should be.
|
||||||
|
* @return YES if provided string fufills the expected fingerprint and the version is known, NO
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
+ (BOOL)validateAppIDFingerprint:(NSString *)appID withVersion:(NSString *)version {
|
||||||
|
if (!appID.length || !version.length) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (![version hasSuffix:@":"]) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (![appID hasPrefix:version]) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the supplied fingerprint from the supplied app ID.
|
||||||
|
// This assumes the app ID format is the same for all known versions below. If the app ID format
|
||||||
|
// changes in future versions, the tokenizing of the app ID format will need to take into account
|
||||||
|
// the version of the app ID.
|
||||||
|
NSArray *components = [appID componentsSeparatedByString:@":"];
|
||||||
|
if (components.count != 4) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *suppliedFingerprintString = components[3];
|
||||||
|
if (!suppliedFingerprintString.length) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t suppliedFingerprint;
|
||||||
|
NSScanner *scanner = [NSScanner scannerWithString:suppliedFingerprintString];
|
||||||
|
if (![scanner scanHexLongLong:&suppliedFingerprint]) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([version isEqual:@"1:"]) {
|
||||||
|
// The v1 hash algorithm is not permitted on the client so the actual hash cannot be validated.
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unknown version.
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)expectedBundleID {
|
||||||
|
return _options.bundleID;
|
||||||
|
}
|
||||||
|
|
||||||
|
// end App ID validation
|
||||||
|
|
||||||
|
#pragma mark - Reading From Plist & User Defaults
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the data collection switch from the standard NSUserDefaults for easier testing and
|
||||||
|
* readability.
|
||||||
|
*/
|
||||||
|
- (void)clearDataCollectionSwitchFromUserDefaults {
|
||||||
|
NSString *key =
|
||||||
|
[NSString stringWithFormat:kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat, self.name];
|
||||||
|
[[NSUserDefaults standardUserDefaults] removeObjectForKey:key];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the data collection switch from the standard NSUserDefaults for easier testing and
|
||||||
|
* readability.
|
||||||
|
*/
|
||||||
|
+ (nullable NSNumber *)readDataCollectionSwitchFromUserDefaultsForApp:(FIRApp *)app {
|
||||||
|
// Read the object in user defaults, and only return if it's an NSNumber.
|
||||||
|
NSString *key =
|
||||||
|
[NSString stringWithFormat:kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat, app.name];
|
||||||
|
id collectionEnabledDefaultsObject = [[NSUserDefaults standardUserDefaults] objectForKey:key];
|
||||||
|
if ([collectionEnabledDefaultsObject isKindOfClass:[NSNumber class]]) {
|
||||||
|
return collectionEnabledDefaultsObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the data collection switch from the Info.plist for easier testing and readability. Will
|
||||||
|
* only read once from the plist and return the cached value.
|
||||||
|
*/
|
||||||
|
+ (nullable NSNumber *)readDataCollectionSwitchFromPlist {
|
||||||
|
static NSNumber *collectionEnabledPlistObject;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
// Read the data from the `Info.plist`, only assign it if it's there and an NSNumber.
|
||||||
|
id plistValue = [[NSBundle mainBundle]
|
||||||
|
objectForInfoDictionaryKey:kFIRGlobalAppDataCollectionEnabledPlistKey];
|
||||||
|
if (plistValue && [plistValue isKindOfClass:[NSNumber class]]) {
|
||||||
|
collectionEnabledPlistObject = (NSNumber *)plistValue;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return collectionEnabledPlistObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Sending Logs
|
||||||
|
|
||||||
|
- (void)sendLogsWithServiceName:(NSString *)serviceName
|
||||||
|
version:(NSString *)version
|
||||||
|
error:(NSError *)error {
|
||||||
|
// If the user has manually turned off data collection, return and don't send logs.
|
||||||
|
if (![self isAutomaticDataCollectionEnabled]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithDictionary:@{
|
||||||
|
kFIRAppDiagnosticsConfigurationTypeKey : @(FIRConfigTypeSDK),
|
||||||
|
kFIRAppDiagnosticsSDKNameKey : serviceName,
|
||||||
|
kFIRAppDiagnosticsSDKVersionKey : version,
|
||||||
|
kFIRAppDiagnosticsFIRAppKey : self
|
||||||
|
}];
|
||||||
|
if (error) {
|
||||||
|
userInfo[kFIRAppDiagnosticsErrorKey] = error;
|
||||||
|
}
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:kFIRAppDiagnosticsNotification
|
||||||
|
object:nil
|
||||||
|
userInfo:userInfo];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
47
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/FIRAppAssociationRegistration.m
generated
Normal file
47
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/FIRAppAssociationRegistration.m
generated
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright 2017 Google
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#import "Private/FIRAppAssociationRegistration.h"
|
||||||
|
|
||||||
|
#import <objc/runtime.h>
|
||||||
|
|
||||||
|
@implementation FIRAppAssociationRegistration
|
||||||
|
|
||||||
|
+ (nullable id)registeredObjectWithHost:(id)host
|
||||||
|
key:(NSString *)key
|
||||||
|
creationBlock:(id _Nullable (^)(void))creationBlock {
|
||||||
|
@synchronized(self) {
|
||||||
|
SEL dictKey = @selector(registeredObjectWithHost:key:creationBlock:);
|
||||||
|
NSMutableDictionary<NSString *, id> *objectsByKey = objc_getAssociatedObject(host, dictKey);
|
||||||
|
if (!objectsByKey) {
|
||||||
|
objectsByKey = [[NSMutableDictionary alloc] init];
|
||||||
|
objc_setAssociatedObject(host, dictKey, objectsByKey, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||||
|
}
|
||||||
|
id obj = objectsByKey[key];
|
||||||
|
NSValue *creationBlockBeingCalled = [NSValue valueWithPointer:dictKey];
|
||||||
|
if (obj) {
|
||||||
|
if ([creationBlockBeingCalled isEqual:obj]) {
|
||||||
|
[NSException raise:@"Reentering registeredObjectWithHost:key:creationBlock: not allowed"
|
||||||
|
format:@"host: %@ key: %@", host, key];
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
objectsByKey[key] = creationBlockBeingCalled;
|
||||||
|
obj = creationBlock();
|
||||||
|
objectsByKey[key] = obj;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,57 @@
|
||||||
|
// Copyright 2017 Google
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#import "Private/FIRBundleUtil.h"
|
||||||
|
|
||||||
|
@implementation FIRBundleUtil
|
||||||
|
|
||||||
|
+ (NSArray *)relevantBundles {
|
||||||
|
return @[ [NSBundle mainBundle], [NSBundle bundleForClass:[self class]] ];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSString *)optionsDictionaryPathWithResourceName:(NSString *)resourceName
|
||||||
|
andFileType:(NSString *)fileType
|
||||||
|
inBundles:(NSArray *)bundles {
|
||||||
|
// Loop through all bundles to find the config dict.
|
||||||
|
for (NSBundle *bundle in bundles) {
|
||||||
|
NSString *path = [bundle pathForResource:resourceName ofType:fileType];
|
||||||
|
// Use the first one we find.
|
||||||
|
if (path) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSArray *)relevantURLSchemes {
|
||||||
|
NSMutableArray *result = [[NSMutableArray alloc] init];
|
||||||
|
for (NSBundle *bundle in [[self class] relevantBundles]) {
|
||||||
|
NSArray *urlTypes = [bundle objectForInfoDictionaryKey:@"CFBundleURLTypes"];
|
||||||
|
for (NSDictionary *urlType in urlTypes) {
|
||||||
|
[result addObjectsFromArray:urlType[@"CFBundleURLSchemes"]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (BOOL)hasBundleIdentifier:(NSString *)bundleIdentifier inBundles:(NSArray *)bundles {
|
||||||
|
for (NSBundle *bundle in bundles) {
|
||||||
|
if ([bundle.bundleIdentifier isEqualToString:bundleIdentifier]) {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright 2017 Google
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#import "FIRConfiguration.h"
|
||||||
|
|
||||||
|
extern void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel);
|
||||||
|
|
||||||
|
@implementation FIRConfiguration
|
||||||
|
|
||||||
|
+ (instancetype)sharedInstance {
|
||||||
|
static FIRConfiguration *sharedInstance = nil;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
sharedInstance = [[FIRConfiguration alloc] init];
|
||||||
|
});
|
||||||
|
return sharedInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)init {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_analyticsConfiguration = [FIRAnalyticsConfiguration sharedInstance];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setLoggerLevel:(FIRLoggerLevel)loggerLevel {
|
||||||
|
NSAssert(loggerLevel <= FIRLoggerLevelMax && loggerLevel >= FIRLoggerLevelMin,
|
||||||
|
@"Invalid logger level, %ld", (long)loggerLevel);
|
||||||
|
FIRSetLoggerLevel(loggerLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright 2017 Google
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#import "Private/FIRErrors.h"
|
||||||
|
|
||||||
|
NSString *const kFirebaseErrorDomain = @"com.firebase";
|
||||||
|
NSString *const kFirebaseAdMobErrorDomain = @"com.firebase.admob";
|
||||||
|
NSString *const kFirebaseAppInviteErrorDomain = @"com.firebase.appinvite";
|
||||||
|
NSString *const kFirebaseAuthErrorDomain = @"com.firebase.auth";
|
||||||
|
NSString *const kFirebaseCloudMessagingErrorDomain = @"com.firebase.cloudmessaging";
|
||||||
|
NSString *const kFirebaseConfigErrorDomain = @"com.firebase.config";
|
||||||
|
NSString *const kFirebaseCoreErrorDomain = @"com.firebase.core";
|
||||||
|
NSString *const kFirebaseCrashReportingErrorDomain = @"com.firebase.crashreporting";
|
||||||
|
NSString *const kFirebaseDatabaseErrorDomain = @"com.firebase.database";
|
||||||
|
NSString *const kFirebaseDurableDeepLinkErrorDomain = @"com.firebase.durabledeeplink";
|
||||||
|
NSString *const kFirebaseInstanceIDErrorDomain = @"com.firebase.instanceid";
|
||||||
|
NSString *const kFirebasePerfErrorDomain = @"com.firebase.perf";
|
||||||
|
NSString *const kFirebaseStorageErrorDomain = @"com.firebase.storage";
|
|
@ -0,0 +1,282 @@
|
||||||
|
// Copyright 2017 Google
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#import "Private/FIRLogger.h"
|
||||||
|
|
||||||
|
#import "FIRLoggerLevel.h"
|
||||||
|
#import "Private/FIRVersion.h"
|
||||||
|
#import "third_party/FIRAppEnvironmentUtil.h"
|
||||||
|
|
||||||
|
#include <asl.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
FIRLoggerService kFIRLoggerABTesting = @"[Firebase/ABTesting]";
|
||||||
|
FIRLoggerService kFIRLoggerAdMob = @"[Firebase/AdMob]";
|
||||||
|
FIRLoggerService kFIRLoggerAnalytics = @"[Firebase/Analytics]";
|
||||||
|
FIRLoggerService kFIRLoggerAuth = @"[Firebase/Auth]";
|
||||||
|
FIRLoggerService kFIRLoggerCore = @"[Firebase/Core]";
|
||||||
|
FIRLoggerService kFIRLoggerCrash = @"[Firebase/Crash]";
|
||||||
|
FIRLoggerService kFIRLoggerDatabase = @"[Firebase/Database]";
|
||||||
|
FIRLoggerService kFIRLoggerDynamicLinks = @"[Firebase/DynamicLinks]";
|
||||||
|
FIRLoggerService kFIRLoggerFirestore = @"[Firebase/Firestore]";
|
||||||
|
FIRLoggerService kFIRLoggerInstanceID = @"[Firebase/InstanceID]";
|
||||||
|
FIRLoggerService kFIRLoggerInvites = @"[Firebase/Invites]";
|
||||||
|
FIRLoggerService kFIRLoggerMLKit = @"[Firebase/MLKit]";
|
||||||
|
FIRLoggerService kFIRLoggerMessaging = @"[Firebase/Messaging]";
|
||||||
|
FIRLoggerService kFIRLoggerPerf = @"[Firebase/Performance]";
|
||||||
|
FIRLoggerService kFIRLoggerRemoteConfig = @"[Firebase/RemoteConfig]";
|
||||||
|
FIRLoggerService kFIRLoggerStorage = @"[Firebase/Storage]";
|
||||||
|
FIRLoggerService kFIRLoggerSwizzler = @"[FirebaseSwizzlingUtilities]";
|
||||||
|
|
||||||
|
/// Arguments passed on launch.
|
||||||
|
NSString *const kFIRDisableDebugModeApplicationArgument = @"-FIRDebugDisabled";
|
||||||
|
NSString *const kFIREnableDebugModeApplicationArgument = @"-FIRDebugEnabled";
|
||||||
|
NSString *const kFIRLoggerForceSDTERRApplicationArgument = @"-FIRLoggerForceSTDERR";
|
||||||
|
|
||||||
|
/// Key for the debug mode bit in NSUserDefaults.
|
||||||
|
NSString *const kFIRPersistedDebugModeKey = @"/google/firebase/debug_mode";
|
||||||
|
|
||||||
|
/// ASL client facility name used by FIRLogger.
|
||||||
|
const char *kFIRLoggerASLClientFacilityName = "com.firebase.app.logger";
|
||||||
|
|
||||||
|
/// Message format used by ASL client that matches format of NSLog.
|
||||||
|
const char *kFIRLoggerCustomASLMessageFormat =
|
||||||
|
"$((Time)(J.3)) $(Sender)[$(PID)] <$((Level)(str))> $Message";
|
||||||
|
|
||||||
|
/// Keys for the number of errors and warnings logged.
|
||||||
|
NSString *const kFIRLoggerErrorCountKey = @"/google/firebase/count_of_errors_logged";
|
||||||
|
NSString *const kFIRLoggerWarningCountKey = @"/google/firebase/count_of_warnings_logged";
|
||||||
|
|
||||||
|
static dispatch_once_t sFIRLoggerOnceToken;
|
||||||
|
|
||||||
|
static aslclient sFIRLoggerClient;
|
||||||
|
|
||||||
|
static dispatch_queue_t sFIRClientQueue;
|
||||||
|
|
||||||
|
static BOOL sFIRLoggerDebugMode;
|
||||||
|
|
||||||
|
// The sFIRAnalyticsDebugMode flag is here to support the -FIRDebugEnabled/-FIRDebugDisabled
|
||||||
|
// flags used by Analytics. Users who use those flags expect Analytics to log verbosely,
|
||||||
|
// while the rest of Firebase logs at the default level. This flag is introduced to support
|
||||||
|
// that behavior.
|
||||||
|
static BOOL sFIRAnalyticsDebugMode;
|
||||||
|
|
||||||
|
static FIRLoggerLevel sFIRLoggerMaximumLevel;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
/// The regex pattern for the message code.
|
||||||
|
static NSString *const kMessageCodePattern = @"^I-[A-Z]{3}[0-9]{6}$";
|
||||||
|
static NSRegularExpression *sMessageCodeRegex;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void FIRLoggerInitializeASL() {
|
||||||
|
dispatch_once(&sFIRLoggerOnceToken, ^{
|
||||||
|
NSInteger majorOSVersion = [[FIRAppEnvironmentUtil systemVersion] integerValue];
|
||||||
|
uint32_t aslOptions = ASL_OPT_STDERR;
|
||||||
|
#if TARGET_OS_SIMULATOR
|
||||||
|
// The iOS 11 simulator doesn't need the ASL_OPT_STDERR flag.
|
||||||
|
if (majorOSVersion >= 11) {
|
||||||
|
aslOptions = 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Devices running iOS 10 or higher don't need the ASL_OPT_STDERR flag.
|
||||||
|
if (majorOSVersion >= 10) {
|
||||||
|
aslOptions = 0;
|
||||||
|
}
|
||||||
|
#endif // TARGET_OS_SIMULATOR
|
||||||
|
|
||||||
|
// Override the aslOptions to ASL_OPT_STDERR if the override argument is passed in.
|
||||||
|
NSArray *arguments = [NSProcessInfo processInfo].arguments;
|
||||||
|
if ([arguments containsObject:kFIRLoggerForceSDTERRApplicationArgument]) {
|
||||||
|
aslOptions = ASL_OPT_STDERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations" // asl is deprecated
|
||||||
|
// Initialize the ASL client handle.
|
||||||
|
sFIRLoggerClient = asl_open(NULL, kFIRLoggerASLClientFacilityName, aslOptions);
|
||||||
|
|
||||||
|
// Set the filter used by system/device log. Initialize in default mode.
|
||||||
|
asl_set_filter(sFIRLoggerClient, ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE));
|
||||||
|
sFIRLoggerDebugMode = NO;
|
||||||
|
sFIRAnalyticsDebugMode = NO;
|
||||||
|
sFIRLoggerMaximumLevel = FIRLoggerLevelNotice;
|
||||||
|
|
||||||
|
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
|
||||||
|
BOOL debugMode = [userDefaults boolForKey:kFIRPersistedDebugModeKey];
|
||||||
|
|
||||||
|
if ([arguments containsObject:kFIRDisableDebugModeApplicationArgument]) { // Default mode
|
||||||
|
[userDefaults removeObjectForKey:kFIRPersistedDebugModeKey];
|
||||||
|
} else if ([arguments containsObject:kFIREnableDebugModeApplicationArgument] ||
|
||||||
|
debugMode) { // Debug mode
|
||||||
|
[userDefaults setBool:YES forKey:kFIRPersistedDebugModeKey];
|
||||||
|
asl_set_filter(sFIRLoggerClient, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
|
||||||
|
sFIRLoggerDebugMode = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should disable debug mode if we are running from App Store.
|
||||||
|
if (sFIRLoggerDebugMode && [FIRAppEnvironmentUtil isFromAppStore]) {
|
||||||
|
sFIRLoggerDebugMode = NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
sFIRClientQueue = dispatch_queue_create("FIRLoggingClientQueue", DISPATCH_QUEUE_SERIAL);
|
||||||
|
dispatch_set_target_queue(sFIRClientQueue,
|
||||||
|
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
sMessageCodeRegex =
|
||||||
|
[NSRegularExpression regularExpressionWithPattern:kMessageCodePattern options:0 error:NULL];
|
||||||
|
#endif
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void FIRSetAnalyticsDebugMode(BOOL analyticsDebugMode) {
|
||||||
|
FIRLoggerInitializeASL();
|
||||||
|
dispatch_async(sFIRClientQueue, ^{
|
||||||
|
// We should not enable debug mode if we are running from App Store.
|
||||||
|
if (analyticsDebugMode && [FIRAppEnvironmentUtil isFromAppStore]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sFIRAnalyticsDebugMode = analyticsDebugMode;
|
||||||
|
asl_set_filter(sFIRLoggerClient, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel) {
|
||||||
|
if (loggerLevel < FIRLoggerLevelMin || loggerLevel > FIRLoggerLevelMax) {
|
||||||
|
FIRLogError(kFIRLoggerCore, @"I-COR000023", @"Invalid logger level, %ld", (long)loggerLevel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FIRLoggerInitializeASL();
|
||||||
|
// We should not raise the logger level if we are running from App Store.
|
||||||
|
if (loggerLevel >= FIRLoggerLevelNotice && [FIRAppEnvironmentUtil isFromAppStore]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sFIRLoggerMaximumLevel = loggerLevel;
|
||||||
|
dispatch_async(sFIRClientQueue, ^{
|
||||||
|
asl_set_filter(sFIRLoggerClient, ASL_FILTER_MASK_UPTO(loggerLevel));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the level is high enough to be loggable.
|
||||||
|
*
|
||||||
|
* Analytics can override the log level with an intentional race condition.
|
||||||
|
* Add the attribute to get a clean thread sanitizer run.
|
||||||
|
*/
|
||||||
|
__attribute__((no_sanitize("thread"))) BOOL FIRIsLoggableLevel(FIRLoggerLevel loggerLevel,
|
||||||
|
BOOL analyticsComponent) {
|
||||||
|
FIRLoggerInitializeASL();
|
||||||
|
if (sFIRLoggerDebugMode) {
|
||||||
|
return YES;
|
||||||
|
} else if (sFIRAnalyticsDebugMode && analyticsComponent) {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return (BOOL)(loggerLevel <= sFIRLoggerMaximumLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
void FIRResetLogger() {
|
||||||
|
sFIRLoggerOnceToken = 0;
|
||||||
|
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kFIRPersistedDebugModeKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
aslclient getFIRLoggerClient() {
|
||||||
|
return sFIRLoggerClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch_queue_t getFIRClientQueue() {
|
||||||
|
return sFIRClientQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL getFIRLoggerDebugMode() {
|
||||||
|
return sFIRLoggerDebugMode;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void FIRLogBasic(FIRLoggerLevel level,
|
||||||
|
FIRLoggerService service,
|
||||||
|
NSString *messageCode,
|
||||||
|
NSString *message,
|
||||||
|
va_list args_ptr) {
|
||||||
|
FIRLoggerInitializeASL();
|
||||||
|
BOOL canLog = level <= sFIRLoggerMaximumLevel;
|
||||||
|
|
||||||
|
if (sFIRLoggerDebugMode) {
|
||||||
|
canLog = YES;
|
||||||
|
} else if (sFIRAnalyticsDebugMode && [kFIRLoggerAnalytics isEqualToString:service]) {
|
||||||
|
canLog = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!canLog) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#ifdef DEBUG
|
||||||
|
NSCAssert(messageCode.length == 11, @"Incorrect message code length.");
|
||||||
|
NSRange messageCodeRange = NSMakeRange(0, messageCode.length);
|
||||||
|
NSUInteger numberOfMatches =
|
||||||
|
[sMessageCodeRegex numberOfMatchesInString:messageCode options:0 range:messageCodeRange];
|
||||||
|
NSCAssert(numberOfMatches == 1, @"Incorrect message code format.");
|
||||||
|
#endif
|
||||||
|
NSString *logMsg = [[NSString alloc] initWithFormat:message arguments:args_ptr];
|
||||||
|
logMsg =
|
||||||
|
[NSString stringWithFormat:@"%s - %@[%@] %@", FIRVersionString, service, messageCode, logMsg];
|
||||||
|
dispatch_async(sFIRClientQueue, ^{
|
||||||
|
asl_log(sFIRLoggerClient, NULL, level, "%s", logMsg.UTF8String);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the logging functions using macros.
|
||||||
|
*
|
||||||
|
* Calling FIRLogError(kFIRLoggerCore, @"I-COR000001", @"Configure %@ failed.", @"blah") shows:
|
||||||
|
* yyyy-mm-dd hh:mm:ss.SSS sender[PID] <Error> [Firebase/Core][I-COR000001] Configure blah failed.
|
||||||
|
* Calling FIRLogDebug(kFIRLoggerCore, @"I-COR000001", @"Configure succeed.") shows:
|
||||||
|
* yyyy-mm-dd hh:mm:ss.SSS sender[PID] <Debug> [Firebase/Core][I-COR000001] Configure succeed.
|
||||||
|
*/
|
||||||
|
#define FIR_LOGGING_FUNCTION(level) \
|
||||||
|
void FIRLog##level(FIRLoggerService service, NSString *messageCode, NSString *message, ...) { \
|
||||||
|
va_list args_ptr; \
|
||||||
|
va_start(args_ptr, message); \
|
||||||
|
FIRLogBasic(FIRLoggerLevel##level, service, messageCode, message, args_ptr); \
|
||||||
|
va_end(args_ptr); \
|
||||||
|
}
|
||||||
|
|
||||||
|
FIR_LOGGING_FUNCTION(Error)
|
||||||
|
FIR_LOGGING_FUNCTION(Warning)
|
||||||
|
FIR_LOGGING_FUNCTION(Notice)
|
||||||
|
FIR_LOGGING_FUNCTION(Info)
|
||||||
|
FIR_LOGGING_FUNCTION(Debug)
|
||||||
|
|
||||||
|
#undef FIR_MAKE_LOGGER
|
||||||
|
|
||||||
|
#pragma mark - FIRLoggerWrapper
|
||||||
|
|
||||||
|
@implementation FIRLoggerWrapper
|
||||||
|
|
||||||
|
+ (void)logWithLevel:(FIRLoggerLevel)level
|
||||||
|
withService:(FIRLoggerService)service
|
||||||
|
withCode:(NSString *)messageCode
|
||||||
|
withMessage:(NSString *)message
|
||||||
|
withArgs:(va_list)args {
|
||||||
|
FIRLogBasic(level, service, messageCode, message, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,97 @@
|
||||||
|
// Copyright 2017 Google
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#import "Private/FIRMutableDictionary.h"
|
||||||
|
|
||||||
|
@implementation FIRMutableDictionary {
|
||||||
|
/// The mutable dictionary.
|
||||||
|
NSMutableDictionary *_objects;
|
||||||
|
|
||||||
|
/// Serial synchronization queue. All reads should use dispatch_sync, while writes use
|
||||||
|
/// dispatch_async.
|
||||||
|
dispatch_queue_t _queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)init {
|
||||||
|
self = [super init];
|
||||||
|
|
||||||
|
if (self) {
|
||||||
|
_objects = [[NSMutableDictionary alloc] init];
|
||||||
|
_queue = dispatch_queue_create("FIRMutableDictionary", DISPATCH_QUEUE_SERIAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)description {
|
||||||
|
__block NSString *description;
|
||||||
|
dispatch_sync(_queue, ^{
|
||||||
|
description = self->_objects.description;
|
||||||
|
});
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)objectForKey:(id)key {
|
||||||
|
__block id object;
|
||||||
|
dispatch_sync(_queue, ^{
|
||||||
|
object = self->_objects[key];
|
||||||
|
});
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setObject:(id)object forKey:(id<NSCopying>)key {
|
||||||
|
dispatch_async(_queue, ^{
|
||||||
|
self->_objects[key] = object;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)removeObjectForKey:(id)key {
|
||||||
|
dispatch_async(_queue, ^{
|
||||||
|
[self->_objects removeObjectForKey:key];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)removeAllObjects {
|
||||||
|
dispatch_async(_queue, ^{
|
||||||
|
[self->_objects removeAllObjects];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)count {
|
||||||
|
__block NSUInteger count;
|
||||||
|
dispatch_sync(_queue, ^{
|
||||||
|
count = self->_objects.count;
|
||||||
|
});
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)objectForKeyedSubscript:(id<NSCopying>)key {
|
||||||
|
// The method this calls is already synchronized.
|
||||||
|
return [self objectForKey:key];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key {
|
||||||
|
// The method this calls is already synchronized.
|
||||||
|
[self setObject:obj forKey:key];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)dictionary {
|
||||||
|
__block NSDictionary *dictionary;
|
||||||
|
dispatch_sync(_queue, ^{
|
||||||
|
dictionary = [self->_objects copy];
|
||||||
|
});
|
||||||
|
return dictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,390 @@
|
||||||
|
// Copyright 2017 Google
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#import "Private/FIRNetwork.h"
|
||||||
|
#import "Private/FIRNetworkMessageCode.h"
|
||||||
|
|
||||||
|
#import "Private/FIRLogger.h"
|
||||||
|
#import "Private/FIRMutableDictionary.h"
|
||||||
|
#import "Private/FIRNetworkConstants.h"
|
||||||
|
#import "Private/FIRReachabilityChecker.h"
|
||||||
|
|
||||||
|
#import <GoogleToolboxForMac/GTMNSData+zlib.h>
|
||||||
|
|
||||||
|
/// Constant string for request header Content-Encoding.
|
||||||
|
static NSString *const kFIRNetworkContentCompressionKey = @"Content-Encoding";
|
||||||
|
|
||||||
|
/// Constant string for request header Content-Encoding value.
|
||||||
|
static NSString *const kFIRNetworkContentCompressionValue = @"gzip";
|
||||||
|
|
||||||
|
/// Constant string for request header Content-Length.
|
||||||
|
static NSString *const kFIRNetworkContentLengthKey = @"Content-Length";
|
||||||
|
|
||||||
|
/// Constant string for request header Content-Type.
|
||||||
|
static NSString *const kFIRNetworkContentTypeKey = @"Content-Type";
|
||||||
|
|
||||||
|
/// Constant string for request header Content-Type value.
|
||||||
|
static NSString *const kFIRNetworkContentTypeValue = @"application/x-www-form-urlencoded";
|
||||||
|
|
||||||
|
/// Constant string for GET request method.
|
||||||
|
static NSString *const kFIRNetworkGETRequestMethod = @"GET";
|
||||||
|
|
||||||
|
/// Constant string for POST request method.
|
||||||
|
static NSString *const kFIRNetworkPOSTRequestMethod = @"POST";
|
||||||
|
|
||||||
|
/// Default constant string as a prefix for network logger.
|
||||||
|
static NSString *const kFIRNetworkLogTag = @"Firebase/Network";
|
||||||
|
|
||||||
|
@interface FIRNetwork () <FIRReachabilityDelegate, FIRNetworkLoggerDelegate>
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FIRNetwork {
|
||||||
|
/// Network reachability.
|
||||||
|
FIRReachabilityChecker *_reachability;
|
||||||
|
|
||||||
|
/// The dictionary of requests by session IDs { NSString : id }.
|
||||||
|
FIRMutableDictionary *_requests;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)init {
|
||||||
|
return [self initWithReachabilityHost:kFIRNetworkReachabilityHost];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithReachabilityHost:(NSString *)reachabilityHost {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
// Setup reachability.
|
||||||
|
_reachability = [[FIRReachabilityChecker alloc] initWithReachabilityDelegate:self
|
||||||
|
loggerDelegate:self
|
||||||
|
withHost:reachabilityHost];
|
||||||
|
if (![_reachability start]) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
_requests = [[FIRMutableDictionary alloc] init];
|
||||||
|
_timeoutInterval = kFIRNetworkTimeOutInterval;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc {
|
||||||
|
_reachability.reachabilityDelegate = nil;
|
||||||
|
[_reachability stop];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - External Methods
|
||||||
|
|
||||||
|
+ (void)handleEventsForBackgroundURLSessionID:(NSString *)sessionID
|
||||||
|
completionHandler:(FIRNetworkSystemCompletionHandler)completionHandler {
|
||||||
|
[FIRNetworkURLSession handleEventsForBackgroundURLSessionID:sessionID
|
||||||
|
completionHandler:completionHandler];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)postURL:(NSURL *)url
|
||||||
|
payload:(NSData *)payload
|
||||||
|
queue:(dispatch_queue_t)queue
|
||||||
|
usingBackgroundSession:(BOOL)usingBackgroundSession
|
||||||
|
completionHandler:(FIRNetworkCompletionHandler)handler {
|
||||||
|
if (!url.absoluteString.length) {
|
||||||
|
[self handleErrorWithCode:FIRErrorCodeNetworkInvalidURL queue:queue withHandler:handler];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSTimeInterval timeOutInterval = _timeoutInterval ?: kFIRNetworkTimeOutInterval;
|
||||||
|
|
||||||
|
NSMutableURLRequest *request =
|
||||||
|
[[NSMutableURLRequest alloc] initWithURL:url
|
||||||
|
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
|
||||||
|
timeoutInterval:timeOutInterval];
|
||||||
|
|
||||||
|
if (!request) {
|
||||||
|
[self handleErrorWithCode:FIRErrorCodeNetworkSessionTaskCreation
|
||||||
|
queue:queue
|
||||||
|
withHandler:handler];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSError *compressError = nil;
|
||||||
|
NSData *compressedData = [NSData gtm_dataByGzippingData:payload error:&compressError];
|
||||||
|
if (!compressedData || compressError) {
|
||||||
|
if (compressError || payload.length > 0) {
|
||||||
|
// If the payload is not empty but it fails to compress the payload, something has been wrong.
|
||||||
|
[self handleErrorWithCode:FIRErrorCodeNetworkPayloadCompression
|
||||||
|
queue:queue
|
||||||
|
withHandler:handler];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
compressedData = [[NSData alloc] init];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *postLength = @(compressedData.length).stringValue;
|
||||||
|
|
||||||
|
// Set up the request with the compressed data.
|
||||||
|
[request setValue:postLength forHTTPHeaderField:kFIRNetworkContentLengthKey];
|
||||||
|
request.HTTPBody = compressedData;
|
||||||
|
request.HTTPMethod = kFIRNetworkPOSTRequestMethod;
|
||||||
|
[request setValue:kFIRNetworkContentTypeValue forHTTPHeaderField:kFIRNetworkContentTypeKey];
|
||||||
|
[request setValue:kFIRNetworkContentCompressionValue
|
||||||
|
forHTTPHeaderField:kFIRNetworkContentCompressionKey];
|
||||||
|
|
||||||
|
FIRNetworkURLSession *fetcher = [[FIRNetworkURLSession alloc] initWithNetworkLoggerDelegate:self];
|
||||||
|
fetcher.backgroundNetworkEnabled = usingBackgroundSession;
|
||||||
|
|
||||||
|
__weak FIRNetwork *weakSelf = self;
|
||||||
|
NSString *requestID = [fetcher
|
||||||
|
sessionIDFromAsyncPOSTRequest:request
|
||||||
|
completionHandler:^(NSHTTPURLResponse *response, NSData *data,
|
||||||
|
NSString *sessionID, NSError *error) {
|
||||||
|
FIRNetwork *strongSelf = weakSelf;
|
||||||
|
if (!strongSelf) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch_queue_t queueToDispatch = queue ? queue : dispatch_get_main_queue();
|
||||||
|
dispatch_async(queueToDispatch, ^{
|
||||||
|
if (sessionID.length) {
|
||||||
|
[strongSelf->_requests removeObjectForKey:sessionID];
|
||||||
|
}
|
||||||
|
if (handler) {
|
||||||
|
handler(response, data, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
if (!requestID) {
|
||||||
|
[self handleErrorWithCode:FIRErrorCodeNetworkSessionTaskCreation
|
||||||
|
queue:queue
|
||||||
|
withHandler:handler];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
[self firNetwork_logWithLevel:kFIRNetworkLogLevelDebug
|
||||||
|
messageCode:kFIRNetworkMessageCodeNetwork000
|
||||||
|
message:@"Uploading data. Host"
|
||||||
|
context:url];
|
||||||
|
_requests[requestID] = fetcher;
|
||||||
|
return requestID;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)getURL:(NSURL *)url
|
||||||
|
headers:(NSDictionary *)headers
|
||||||
|
queue:(dispatch_queue_t)queue
|
||||||
|
usingBackgroundSession:(BOOL)usingBackgroundSession
|
||||||
|
completionHandler:(FIRNetworkCompletionHandler)handler {
|
||||||
|
if (!url.absoluteString.length) {
|
||||||
|
[self handleErrorWithCode:FIRErrorCodeNetworkInvalidURL queue:queue withHandler:handler];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSTimeInterval timeOutInterval = _timeoutInterval ?: kFIRNetworkTimeOutInterval;
|
||||||
|
NSMutableURLRequest *request =
|
||||||
|
[[NSMutableURLRequest alloc] initWithURL:url
|
||||||
|
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
|
||||||
|
timeoutInterval:timeOutInterval];
|
||||||
|
|
||||||
|
if (!request) {
|
||||||
|
[self handleErrorWithCode:FIRErrorCodeNetworkSessionTaskCreation
|
||||||
|
queue:queue
|
||||||
|
withHandler:handler];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
request.HTTPMethod = kFIRNetworkGETRequestMethod;
|
||||||
|
request.allHTTPHeaderFields = headers;
|
||||||
|
|
||||||
|
FIRNetworkURLSession *fetcher = [[FIRNetworkURLSession alloc] initWithNetworkLoggerDelegate:self];
|
||||||
|
fetcher.backgroundNetworkEnabled = usingBackgroundSession;
|
||||||
|
|
||||||
|
__weak FIRNetwork *weakSelf = self;
|
||||||
|
NSString *requestID = [fetcher
|
||||||
|
sessionIDFromAsyncGETRequest:request
|
||||||
|
completionHandler:^(NSHTTPURLResponse *response, NSData *data, NSString *sessionID,
|
||||||
|
NSError *error) {
|
||||||
|
FIRNetwork *strongSelf = weakSelf;
|
||||||
|
if (!strongSelf) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch_queue_t queueToDispatch = queue ? queue : dispatch_get_main_queue();
|
||||||
|
dispatch_async(queueToDispatch, ^{
|
||||||
|
if (sessionID.length) {
|
||||||
|
[strongSelf->_requests removeObjectForKey:sessionID];
|
||||||
|
}
|
||||||
|
if (handler) {
|
||||||
|
handler(response, data, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (!requestID) {
|
||||||
|
[self handleErrorWithCode:FIRErrorCodeNetworkSessionTaskCreation
|
||||||
|
queue:queue
|
||||||
|
withHandler:handler];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
[self firNetwork_logWithLevel:kFIRNetworkLogLevelDebug
|
||||||
|
messageCode:kFIRNetworkMessageCodeNetwork001
|
||||||
|
message:@"Downloading data. Host"
|
||||||
|
context:url];
|
||||||
|
_requests[requestID] = fetcher;
|
||||||
|
return requestID;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)hasUploadInProgress {
|
||||||
|
return _requests.count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Network Reachability
|
||||||
|
|
||||||
|
/// Tells reachability delegate to call reachabilityDidChangeToStatus: to notify the network
|
||||||
|
/// reachability has changed.
|
||||||
|
- (void)reachability:(FIRReachabilityChecker *)reachability
|
||||||
|
statusChanged:(FIRReachabilityStatus)status {
|
||||||
|
_networkConnected = (status == kFIRReachabilityViaCellular || status == kFIRReachabilityViaWifi);
|
||||||
|
[_reachabilityDelegate reachabilityDidChange];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Network logger delegate
|
||||||
|
|
||||||
|
- (void)setLoggerDelegate:(id<FIRNetworkLoggerDelegate>)loggerDelegate {
|
||||||
|
// Explicitly check whether the delegate responds to the methods because conformsToProtocol does
|
||||||
|
// not work correctly even though the delegate does respond to the methods.
|
||||||
|
if (!loggerDelegate ||
|
||||||
|
![loggerDelegate
|
||||||
|
respondsToSelector:@selector(firNetwork_logWithLevel:messageCode:message:contexts:)] ||
|
||||||
|
![loggerDelegate
|
||||||
|
respondsToSelector:@selector(firNetwork_logWithLevel:messageCode:message:context:)] ||
|
||||||
|
!
|
||||||
|
[loggerDelegate respondsToSelector:@selector(firNetwork_logWithLevel:messageCode:message:)]) {
|
||||||
|
FIRLogError(kFIRLoggerAnalytics,
|
||||||
|
[NSString stringWithFormat:@"I-NET%06ld", (long)kFIRNetworkMessageCodeNetwork002],
|
||||||
|
@"Cannot set the network logger delegate: delegate does not conform to the network "
|
||||||
|
"logger protocol.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_loggerDelegate = loggerDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private methods
|
||||||
|
|
||||||
|
/// Handles network error and calls completion handler with the error.
|
||||||
|
- (void)handleErrorWithCode:(NSInteger)code
|
||||||
|
queue:(dispatch_queue_t)queue
|
||||||
|
withHandler:(FIRNetworkCompletionHandler)handler {
|
||||||
|
NSDictionary *userInfo = @{kFIRNetworkErrorContext : @"Failed to create network request"};
|
||||||
|
NSError *error =
|
||||||
|
[[NSError alloc] initWithDomain:kFIRNetworkErrorDomain code:code userInfo:userInfo];
|
||||||
|
[self firNetwork_logWithLevel:kFIRNetworkLogLevelWarning
|
||||||
|
messageCode:kFIRNetworkMessageCodeNetwork002
|
||||||
|
message:@"Failed to create network request. Code, error"
|
||||||
|
contexts:@[ @(code), error ]];
|
||||||
|
if (handler) {
|
||||||
|
dispatch_queue_t queueToDispatch = queue ? queue : dispatch_get_main_queue();
|
||||||
|
dispatch_async(queueToDispatch, ^{
|
||||||
|
handler(nil, nil, error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Network logger
|
||||||
|
|
||||||
|
- (void)firNetwork_logWithLevel:(FIRNetworkLogLevel)logLevel
|
||||||
|
messageCode:(FIRNetworkMessageCode)messageCode
|
||||||
|
message:(NSString *)message
|
||||||
|
contexts:(NSArray *)contexts {
|
||||||
|
// Let the delegate log the message if there is a valid logger delegate. Otherwise, just log
|
||||||
|
// errors/warnings/info messages to the console log.
|
||||||
|
if (_loggerDelegate) {
|
||||||
|
[_loggerDelegate firNetwork_logWithLevel:logLevel
|
||||||
|
messageCode:messageCode
|
||||||
|
message:message
|
||||||
|
contexts:contexts];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_isDebugModeEnabled || logLevel == kFIRNetworkLogLevelError ||
|
||||||
|
logLevel == kFIRNetworkLogLevelWarning || logLevel == kFIRNetworkLogLevelInfo) {
|
||||||
|
NSString *formattedMessage = FIRStringWithLogMessage(message, logLevel, contexts);
|
||||||
|
NSLog(@"%@", formattedMessage);
|
||||||
|
FIRLogBasic((FIRLoggerLevel)logLevel, kFIRLoggerCore,
|
||||||
|
[NSString stringWithFormat:@"I-NET%06ld", (long)messageCode], formattedMessage,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)firNetwork_logWithLevel:(FIRNetworkLogLevel)logLevel
|
||||||
|
messageCode:(FIRNetworkMessageCode)messageCode
|
||||||
|
message:(NSString *)message
|
||||||
|
context:(id)context {
|
||||||
|
if (_loggerDelegate) {
|
||||||
|
[_loggerDelegate firNetwork_logWithLevel:logLevel
|
||||||
|
messageCode:messageCode
|
||||||
|
message:message
|
||||||
|
context:context];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NSArray *contexts = context ? @[ context ] : @[];
|
||||||
|
[self firNetwork_logWithLevel:logLevel messageCode:messageCode message:message contexts:contexts];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)firNetwork_logWithLevel:(FIRNetworkLogLevel)logLevel
|
||||||
|
messageCode:(FIRNetworkMessageCode)messageCode
|
||||||
|
message:(NSString *)message {
|
||||||
|
if (_loggerDelegate) {
|
||||||
|
[_loggerDelegate firNetwork_logWithLevel:logLevel messageCode:messageCode message:message];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[self firNetwork_logWithLevel:logLevel messageCode:messageCode message:message contexts:@[]];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a string for the given log level (e.g. kFIRNetworkLogLevelError -> @"ERROR").
|
||||||
|
static NSString *FIRLogLevelDescriptionFromLogLevel(FIRNetworkLogLevel logLevel) {
|
||||||
|
static NSDictionary *levelNames = nil;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
levelNames = @{
|
||||||
|
@(kFIRNetworkLogLevelError) : @"ERROR",
|
||||||
|
@(kFIRNetworkLogLevelWarning) : @"WARNING",
|
||||||
|
@(kFIRNetworkLogLevelInfo) : @"INFO",
|
||||||
|
@(kFIRNetworkLogLevelDebug) : @"DEBUG"
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return levelNames[@(logLevel)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a formatted string to be used for console logging.
|
||||||
|
static NSString *FIRStringWithLogMessage(NSString *message,
|
||||||
|
FIRNetworkLogLevel logLevel,
|
||||||
|
NSArray *contexts) {
|
||||||
|
if (!message) {
|
||||||
|
message = @"(Message was nil)";
|
||||||
|
} else if (!message.length) {
|
||||||
|
message = @"(Message was empty)";
|
||||||
|
}
|
||||||
|
NSMutableString *result = [[NSMutableString alloc]
|
||||||
|
initWithFormat:@"<%@/%@> %@", kFIRNetworkLogTag, FIRLogLevelDescriptionFromLogLevel(logLevel),
|
||||||
|
message];
|
||||||
|
|
||||||
|
if (!contexts.count) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSMutableArray *formattedContexts = [[NSMutableArray alloc] init];
|
||||||
|
for (id item in contexts) {
|
||||||
|
[formattedContexts addObject:(item != [NSNull null] ? item : @"(nil)")];
|
||||||
|
}
|
||||||
|
|
||||||
|
[result appendString:@": "];
|
||||||
|
[result appendString:[formattedContexts componentsJoinedByString:@", "]];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,39 @@
|
||||||
|
// Copyright 2017 Google
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#import "Private/FIRNetworkConstants.h"
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NSString *const kFIRNetworkBackgroundSessionConfigIDPrefix =
|
||||||
|
@"com.firebase.network.background-upload";
|
||||||
|
NSString *const kFIRNetworkApplicationSupportSubdirectory = @"Firebase/Network";
|
||||||
|
NSString *const kFIRNetworkTempDirectoryName = @"FIRNetworkTemporaryDirectory";
|
||||||
|
const NSTimeInterval kFIRNetworkTempFolderExpireTime = 60 * 60; // 1 hour
|
||||||
|
const NSTimeInterval kFIRNetworkTimeOutInterval = 60; // 1 minute.
|
||||||
|
NSString *const kFIRNetworkReachabilityHost = @"app-measurement.com";
|
||||||
|
NSString *const kFIRNetworkErrorContext = @"Context";
|
||||||
|
|
||||||
|
const int kFIRNetworkHTTPStatusOK = 200;
|
||||||
|
const int kFIRNetworkHTTPStatusNoContent = 204;
|
||||||
|
const int kFIRNetworkHTTPStatusCodeMultipleChoices = 300;
|
||||||
|
const int kFIRNetworkHTTPStatusCodeMovedPermanently = 301;
|
||||||
|
const int kFIRNetworkHTTPStatusCodeFound = 302;
|
||||||
|
const int kFIRNetworkHTTPStatusCodeNotModified = 304;
|
||||||
|
const int kFIRNetworkHTTPStatusCodeMovedTemporarily = 307;
|
||||||
|
const int kFIRNetworkHTTPStatusCodeNotFound = 404;
|
||||||
|
const int kFIRNetworkHTTPStatusCodeCannotAcceptTraffic = 429;
|
||||||
|
const int kFIRNetworkHTTPStatusCodeUnavailable = 503;
|
||||||
|
|
||||||
|
NSString *const kFIRNetworkErrorDomain = @"com.firebase.network.ErrorDomain";
|
|
@ -0,0 +1,669 @@
|
||||||
|
// Copyright 2017 Google
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import "Private/FIRNetworkURLSession.h"
|
||||||
|
|
||||||
|
#import "Private/FIRLogger.h"
|
||||||
|
#import "Private/FIRMutableDictionary.h"
|
||||||
|
#import "Private/FIRNetworkConstants.h"
|
||||||
|
#import "Private/FIRNetworkMessageCode.h"
|
||||||
|
|
||||||
|
@implementation FIRNetworkURLSession {
|
||||||
|
/// The handler to be called when the request completes or error has occurs.
|
||||||
|
FIRNetworkURLSessionCompletionHandler _completionHandler;
|
||||||
|
|
||||||
|
/// Session ID generated randomly with a fixed prefix.
|
||||||
|
NSString *_sessionID;
|
||||||
|
|
||||||
|
/// The session configuration.
|
||||||
|
NSURLSessionConfiguration *_sessionConfig;
|
||||||
|
|
||||||
|
/// The path to the directory where all temporary files are stored before uploading.
|
||||||
|
NSURL *_networkDirectoryURL;
|
||||||
|
|
||||||
|
/// The downloaded data from fetching.
|
||||||
|
NSData *_downloadedData;
|
||||||
|
|
||||||
|
/// The path to the temporary file which stores the uploading data.
|
||||||
|
NSURL *_uploadingFileURL;
|
||||||
|
|
||||||
|
/// The current request.
|
||||||
|
NSURLRequest *_request;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Init
|
||||||
|
|
||||||
|
- (instancetype)initWithNetworkLoggerDelegate:(id<FIRNetworkLoggerDelegate>)networkLoggerDelegate {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
// Create URL to the directory where all temporary files to upload have to be stored.
|
||||||
|
NSArray *paths =
|
||||||
|
NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
||||||
|
NSString *applicationSupportDirectory = paths.firstObject;
|
||||||
|
NSArray *tempPathComponents = @[
|
||||||
|
applicationSupportDirectory, kFIRNetworkApplicationSupportSubdirectory,
|
||||||
|
kFIRNetworkTempDirectoryName
|
||||||
|
];
|
||||||
|
_networkDirectoryURL = [NSURL fileURLWithPathComponents:tempPathComponents];
|
||||||
|
_sessionID = [NSString stringWithFormat:@"%@-%@", kFIRNetworkBackgroundSessionConfigIDPrefix,
|
||||||
|
[[NSUUID UUID] UUIDString]];
|
||||||
|
_loggerDelegate = networkLoggerDelegate;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - External Methods
|
||||||
|
|
||||||
|
#pragma mark - To be called from AppDelegate
|
||||||
|
|
||||||
|
+ (void)handleEventsForBackgroundURLSessionID:(NSString *)sessionID
|
||||||
|
completionHandler:
|
||||||
|
(FIRNetworkSystemCompletionHandler)systemCompletionHandler {
|
||||||
|
// The session may not be FIRAnalytics background. Ignore those that do not have the prefix.
|
||||||
|
if (![sessionID hasPrefix:kFIRNetworkBackgroundSessionConfigIDPrefix]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FIRNetworkURLSession *fetcher = [self fetcherWithSessionIdentifier:sessionID];
|
||||||
|
if (fetcher != nil) {
|
||||||
|
[fetcher addSystemCompletionHandler:systemCompletionHandler forSession:sessionID];
|
||||||
|
} else {
|
||||||
|
FIRLogError(kFIRLoggerCore,
|
||||||
|
[NSString stringWithFormat:@"I-NET%06ld", (long)kFIRNetworkMessageCodeNetwork003],
|
||||||
|
@"Failed to retrieve background session with ID %@ after app is relaunched.",
|
||||||
|
sessionID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - External Methods
|
||||||
|
|
||||||
|
/// Sends an async POST request using NSURLSession for iOS >= 7.0, and returns an ID of the
|
||||||
|
/// connection.
|
||||||
|
- (NSString *)sessionIDFromAsyncPOSTRequest:(NSURLRequest *)request
|
||||||
|
completionHandler:(FIRNetworkURLSessionCompletionHandler)handler {
|
||||||
|
// NSURLSessionUploadTask does not work with NSData in the background.
|
||||||
|
// To avoid this issue, write the data to a temporary file to upload it.
|
||||||
|
// Make a temporary file with the data subset.
|
||||||
|
_uploadingFileURL = [self temporaryFilePathWithSessionID:_sessionID];
|
||||||
|
NSError *writeError;
|
||||||
|
NSURLSessionUploadTask *postRequestTask;
|
||||||
|
NSURLSession *session;
|
||||||
|
BOOL didWriteFile = NO;
|
||||||
|
|
||||||
|
// Clean up the entire temp folder to avoid temp files that remain in case the previous session
|
||||||
|
// crashed and did not clean up.
|
||||||
|
[self maybeRemoveTempFilesAtURL:_networkDirectoryURL
|
||||||
|
expiringTime:kFIRNetworkTempFolderExpireTime];
|
||||||
|
|
||||||
|
// If there is no background network enabled, no need to write to file. This will allow default
|
||||||
|
// network session which runs on the foreground.
|
||||||
|
if (_backgroundNetworkEnabled && [self ensureTemporaryDirectoryExists]) {
|
||||||
|
didWriteFile = [request.HTTPBody writeToFile:_uploadingFileURL.path
|
||||||
|
options:NSDataWritingAtomic
|
||||||
|
error:&writeError];
|
||||||
|
|
||||||
|
if (writeError) {
|
||||||
|
[_loggerDelegate firNetwork_logWithLevel:kFIRNetworkLogLevelError
|
||||||
|
messageCode:kFIRNetworkMessageCodeURLSession000
|
||||||
|
message:@"Failed to write request data to file"
|
||||||
|
context:writeError];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (didWriteFile) {
|
||||||
|
// Exclude this file from backing up to iTunes. There are conflicting reports that excluding
|
||||||
|
// directory from backing up does not excluding files of that directory from backing up.
|
||||||
|
[self excludeFromBackupForURL:_uploadingFileURL];
|
||||||
|
|
||||||
|
_sessionConfig = [self backgroundSessionConfigWithSessionID:_sessionID];
|
||||||
|
[self populateSessionConfig:_sessionConfig withRequest:request];
|
||||||
|
session = [NSURLSession sessionWithConfiguration:_sessionConfig
|
||||||
|
delegate:self
|
||||||
|
delegateQueue:[NSOperationQueue mainQueue]];
|
||||||
|
postRequestTask = [session uploadTaskWithRequest:request fromFile:_uploadingFileURL];
|
||||||
|
} else {
|
||||||
|
// If we cannot write to file, just send it in the foreground.
|
||||||
|
_sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||||
|
[self populateSessionConfig:_sessionConfig withRequest:request];
|
||||||
|
_sessionConfig.URLCache = nil;
|
||||||
|
session = [NSURLSession sessionWithConfiguration:_sessionConfig
|
||||||
|
delegate:self
|
||||||
|
delegateQueue:[NSOperationQueue mainQueue]];
|
||||||
|
postRequestTask = [session uploadTaskWithRequest:request fromData:request.HTTPBody];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!session || !postRequestTask) {
|
||||||
|
NSError *error = [[NSError alloc]
|
||||||
|
initWithDomain:kFIRNetworkErrorDomain
|
||||||
|
code:FIRErrorCodeNetworkRequestCreation
|
||||||
|
userInfo:@{kFIRNetworkErrorContext : @"Cannot create network session"}];
|
||||||
|
[self callCompletionHandler:handler withResponse:nil data:nil error:error];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the session into memory.
|
||||||
|
NSMapTable *sessionIdentifierToFetcherMap = [[self class] sessionIDToFetcherMap];
|
||||||
|
[sessionIdentifierToFetcherMap setObject:self forKey:_sessionID];
|
||||||
|
|
||||||
|
_request = [request copy];
|
||||||
|
|
||||||
|
// Store completion handler because background session does not accept handler block but custom
|
||||||
|
// delegate.
|
||||||
|
_completionHandler = [handler copy];
|
||||||
|
[postRequestTask resume];
|
||||||
|
|
||||||
|
return _sessionID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends an async GET request using NSURLSession for iOS >= 7.0, and returns an ID of the session.
|
||||||
|
- (NSString *)sessionIDFromAsyncGETRequest:(NSURLRequest *)request
|
||||||
|
completionHandler:(FIRNetworkURLSessionCompletionHandler)handler {
|
||||||
|
if (_backgroundNetworkEnabled) {
|
||||||
|
_sessionConfig = [self backgroundSessionConfigWithSessionID:_sessionID];
|
||||||
|
} else {
|
||||||
|
_sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||||
|
}
|
||||||
|
|
||||||
|
[self populateSessionConfig:_sessionConfig withRequest:request];
|
||||||
|
|
||||||
|
// Do not cache the GET request.
|
||||||
|
_sessionConfig.URLCache = nil;
|
||||||
|
|
||||||
|
NSURLSession *session = [NSURLSession sessionWithConfiguration:_sessionConfig
|
||||||
|
delegate:self
|
||||||
|
delegateQueue:[NSOperationQueue mainQueue]];
|
||||||
|
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request];
|
||||||
|
|
||||||
|
if (!session || !downloadTask) {
|
||||||
|
NSError *error = [[NSError alloc]
|
||||||
|
initWithDomain:kFIRNetworkErrorDomain
|
||||||
|
code:FIRErrorCodeNetworkRequestCreation
|
||||||
|
userInfo:@{kFIRNetworkErrorContext : @"Cannot create network session"}];
|
||||||
|
[self callCompletionHandler:handler withResponse:nil data:nil error:error];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the session into memory.
|
||||||
|
NSMapTable *sessionIdentifierToFetcherMap = [[self class] sessionIDToFetcherMap];
|
||||||
|
[sessionIdentifierToFetcherMap setObject:self forKey:_sessionID];
|
||||||
|
|
||||||
|
_request = [request copy];
|
||||||
|
|
||||||
|
_completionHandler = [handler copy];
|
||||||
|
[downloadTask resume];
|
||||||
|
|
||||||
|
return _sessionID;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - NSURLSessionTaskDelegate
|
||||||
|
|
||||||
|
/// Called by the NSURLSession once the download task is completed. The file is saved in the
|
||||||
|
/// provided URL so we need to read the data and store into _downloadedData. Once the session is
|
||||||
|
/// completed, URLSession:task:didCompleteWithError will be called and the completion handler will
|
||||||
|
/// be called with the downloaded data.
|
||||||
|
- (void)URLSession:(NSURLSession *)session
|
||||||
|
downloadTask:(NSURLSessionDownloadTask *)task
|
||||||
|
didFinishDownloadingToURL:(NSURL *)url {
|
||||||
|
if (!url.path) {
|
||||||
|
[_loggerDelegate
|
||||||
|
firNetwork_logWithLevel:kFIRNetworkLogLevelError
|
||||||
|
messageCode:kFIRNetworkMessageCodeURLSession001
|
||||||
|
message:@"Unable to read downloaded data from empty temp path"];
|
||||||
|
_downloadedData = nil;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSError *error;
|
||||||
|
_downloadedData = [NSData dataWithContentsOfFile:url.path options:0 error:&error];
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
[_loggerDelegate firNetwork_logWithLevel:kFIRNetworkLogLevelError
|
||||||
|
messageCode:kFIRNetworkMessageCodeURLSession002
|
||||||
|
message:@"Cannot read the content of downloaded data"
|
||||||
|
context:error];
|
||||||
|
_downloadedData = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||||
|
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
|
||||||
|
[_loggerDelegate firNetwork_logWithLevel:kFIRNetworkLogLevelDebug
|
||||||
|
messageCode:kFIRNetworkMessageCodeURLSession003
|
||||||
|
message:@"Background session finished"
|
||||||
|
context:session.configuration.identifier];
|
||||||
|
[self callSystemCompletionHandler:session.configuration.identifier];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
- (void)URLSession:(NSURLSession *)session
|
||||||
|
task:(NSURLSessionTask *)task
|
||||||
|
didCompleteWithError:(NSError *)error {
|
||||||
|
// Avoid any chance of recursive behavior leading to it being used repeatedly.
|
||||||
|
FIRNetworkURLSessionCompletionHandler handler = _completionHandler;
|
||||||
|
_completionHandler = nil;
|
||||||
|
|
||||||
|
if (task.response) {
|
||||||
|
// The following assertion should always be true for HTTP requests, see https://goo.gl/gVLxT7.
|
||||||
|
NSAssert([task.response isKindOfClass:[NSHTTPURLResponse class]], @"URL response must be HTTP");
|
||||||
|
|
||||||
|
// The server responded so ignore the error created by the system.
|
||||||
|
error = nil;
|
||||||
|
} else if (!error) {
|
||||||
|
error = [[NSError alloc]
|
||||||
|
initWithDomain:kFIRNetworkErrorDomain
|
||||||
|
code:FIRErrorCodeNetworkInvalidResponse
|
||||||
|
userInfo:@{kFIRNetworkErrorContext : @"Network Error: Empty network response"}];
|
||||||
|
}
|
||||||
|
|
||||||
|
[self callCompletionHandler:handler
|
||||||
|
withResponse:(NSHTTPURLResponse *)task.response
|
||||||
|
data:_downloadedData
|
||||||
|
error:error];
|
||||||
|
|
||||||
|
// Remove the temp file to avoid trashing devices with lots of temp files.
|
||||||
|
[self removeTempItemAtURL:_uploadingFileURL];
|
||||||
|
|
||||||
|
// Try to clean up stale files again.
|
||||||
|
[self maybeRemoveTempFilesAtURL:_networkDirectoryURL
|
||||||
|
expiringTime:kFIRNetworkTempFolderExpireTime];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)URLSession:(NSURLSession *)session
|
||||||
|
task:(NSURLSessionTask *)task
|
||||||
|
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||||
|
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
|
||||||
|
NSURLCredential *credential))completionHandler {
|
||||||
|
// The handling is modeled after GTMSessionFetcher.
|
||||||
|
if ([challenge.protectionSpace.authenticationMethod
|
||||||
|
isEqualToString:NSURLAuthenticationMethodServerTrust]) {
|
||||||
|
SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
|
||||||
|
if (serverTrust == NULL) {
|
||||||
|
[_loggerDelegate firNetwork_logWithLevel:kFIRNetworkLogLevelDebug
|
||||||
|
messageCode:kFIRNetworkMessageCodeURLSession004
|
||||||
|
message:@"Received empty server trust for host. Host"
|
||||||
|
context:_request.URL];
|
||||||
|
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NSURLCredential *credential = [NSURLCredential credentialForTrust:serverTrust];
|
||||||
|
if (!credential) {
|
||||||
|
[_loggerDelegate firNetwork_logWithLevel:kFIRNetworkLogLevelWarning
|
||||||
|
messageCode:kFIRNetworkMessageCodeURLSession005
|
||||||
|
message:@"Unable to verify server identity. Host"
|
||||||
|
context:_request.URL];
|
||||||
|
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[_loggerDelegate firNetwork_logWithLevel:kFIRNetworkLogLevelDebug
|
||||||
|
messageCode:kFIRNetworkMessageCodeURLSession006
|
||||||
|
message:@"Received SSL challenge for host. Host"
|
||||||
|
context:_request.URL];
|
||||||
|
|
||||||
|
void (^callback)(BOOL) = ^(BOOL allow) {
|
||||||
|
if (allow) {
|
||||||
|
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
|
||||||
|
} else {
|
||||||
|
[self->_loggerDelegate
|
||||||
|
firNetwork_logWithLevel:kFIRNetworkLogLevelDebug
|
||||||
|
messageCode:kFIRNetworkMessageCodeURLSession007
|
||||||
|
message:@"Cancelling authentication challenge for host. Host"
|
||||||
|
context:self->_request.URL];
|
||||||
|
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Retain the trust object to avoid a SecTrustEvaluate() crash on iOS 7.
|
||||||
|
CFRetain(serverTrust);
|
||||||
|
|
||||||
|
// Evaluate the certificate chain.
|
||||||
|
//
|
||||||
|
// The delegate queue may be the main thread. Trust evaluation could cause some
|
||||||
|
// blocking network activity, so we must evaluate async, as documented at
|
||||||
|
// https://developer.apple.com/library/ios/technotes/tn2232/
|
||||||
|
dispatch_queue_t evaluateBackgroundQueue =
|
||||||
|
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||||
|
|
||||||
|
dispatch_async(evaluateBackgroundQueue, ^{
|
||||||
|
SecTrustResultType trustEval = kSecTrustResultInvalid;
|
||||||
|
BOOL shouldAllow;
|
||||||
|
OSStatus trustError;
|
||||||
|
|
||||||
|
@synchronized([FIRNetworkURLSession class]) {
|
||||||
|
trustError = SecTrustEvaluate(serverTrust, &trustEval);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trustError != errSecSuccess) {
|
||||||
|
[self->_loggerDelegate firNetwork_logWithLevel:kFIRNetworkLogLevelError
|
||||||
|
messageCode:kFIRNetworkMessageCodeURLSession008
|
||||||
|
message:@"Cannot evaluate server trust. Error, host"
|
||||||
|
contexts:@[ @(trustError), self->_request.URL ]];
|
||||||
|
shouldAllow = NO;
|
||||||
|
} else {
|
||||||
|
// Having a trust level "unspecified" by the user is the usual result, described at
|
||||||
|
// https://developer.apple.com/library/mac/qa/qa1360
|
||||||
|
shouldAllow =
|
||||||
|
(trustEval == kSecTrustResultUnspecified || trustEval == kSecTrustResultProceed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the call back with the permission.
|
||||||
|
callback(shouldAllow);
|
||||||
|
|
||||||
|
CFRelease(serverTrust);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default handling for other Auth Challenges.
|
||||||
|
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Internal Methods
|
||||||
|
|
||||||
|
/// Stores system completion handler with session ID as key.
|
||||||
|
- (void)addSystemCompletionHandler:(FIRNetworkSystemCompletionHandler)handler
|
||||||
|
forSession:(NSString *)identifier {
|
||||||
|
if (!handler) {
|
||||||
|
[_loggerDelegate
|
||||||
|
firNetwork_logWithLevel:kFIRNetworkLogLevelError
|
||||||
|
messageCode:kFIRNetworkMessageCodeURLSession009
|
||||||
|
message:@"Cannot store nil system completion handler in network"];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!identifier.length) {
|
||||||
|
[_loggerDelegate
|
||||||
|
firNetwork_logWithLevel:kFIRNetworkLogLevelError
|
||||||
|
messageCode:kFIRNetworkMessageCodeURLSession010
|
||||||
|
message:
|
||||||
|
@"Cannot store system completion handler with empty network "
|
||||||
|
"session identifier"];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FIRMutableDictionary *systemCompletionHandlers =
|
||||||
|
[[self class] sessionIDToSystemCompletionHandlerDictionary];
|
||||||
|
if (systemCompletionHandlers[identifier]) {
|
||||||
|
[_loggerDelegate firNetwork_logWithLevel:kFIRNetworkLogLevelWarning
|
||||||
|
messageCode:kFIRNetworkMessageCodeURLSession011
|
||||||
|
message:@"Got multiple system handlers for a single session ID"
|
||||||
|
context:identifier];
|
||||||
|
}
|
||||||
|
|
||||||
|
systemCompletionHandlers[identifier] = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls the system provided completion handler with the session ID stored in the dictionary.
|
||||||
|
/// The handler will be removed from the dictionary after being called.
|
||||||
|
- (void)callSystemCompletionHandler:(NSString *)identifier {
|
||||||
|
FIRMutableDictionary *systemCompletionHandlers =
|
||||||
|
[[self class] sessionIDToSystemCompletionHandlerDictionary];
|
||||||
|
FIRNetworkSystemCompletionHandler handler = [systemCompletionHandlers objectForKey:identifier];
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
[systemCompletionHandlers removeObjectForKey:identifier];
|
||||||
|
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
handler();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets or updates the session ID of this session.
|
||||||
|
- (void)setSessionID:(NSString *)sessionID {
|
||||||
|
_sessionID = [sessionID copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a background session configuration with the session ID using the supported method.
|
||||||
|
- (NSURLSessionConfiguration *)backgroundSessionConfigWithSessionID:(NSString *)sessionID {
|
||||||
|
#if (TARGET_OS_OSX && defined(MAC_OS_X_VERSION_10_10) && \
|
||||||
|
MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10) || \
|
||||||
|
TARGET_OS_TV || \
|
||||||
|
(TARGET_OS_IOS && defined(__IPHONE_8_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_8_0)
|
||||||
|
|
||||||
|
// iOS 8/10.10 builds require the new backgroundSessionConfiguration method name.
|
||||||
|
return [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:sessionID];
|
||||||
|
|
||||||
|
#elif (TARGET_OS_OSX && defined(MAC_OS_X_VERSION_10_10) && \
|
||||||
|
MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10) || \
|
||||||
|
(TARGET_OS_IOS && defined(__IPHONE_8_0) && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0)
|
||||||
|
|
||||||
|
// Do a runtime check to avoid a deprecation warning about using
|
||||||
|
// +backgroundSessionConfiguration: on iOS 8.
|
||||||
|
if ([NSURLSessionConfiguration
|
||||||
|
respondsToSelector:@selector(backgroundSessionConfigurationWithIdentifier:)]) {
|
||||||
|
// Running on iOS 8+/OS X 10.10+.
|
||||||
|
return [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:sessionID];
|
||||||
|
} else {
|
||||||
|
// Running on iOS 7/OS X 10.9.
|
||||||
|
return [NSURLSessionConfiguration backgroundSessionConfiguration:sessionID];
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
// Building with an SDK earlier than iOS 8/OS X 10.10.
|
||||||
|
return [NSURLSessionConfiguration backgroundSessionConfiguration:sessionID];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)maybeRemoveTempFilesAtURL:(NSURL *)folderURL expiringTime:(NSTimeInterval)staleTime {
|
||||||
|
if (!folderURL.absoluteString.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||||
|
NSError *error = nil;
|
||||||
|
|
||||||
|
NSArray *properties = @[ NSURLCreationDateKey ];
|
||||||
|
NSArray *directoryContent =
|
||||||
|
[fileManager contentsOfDirectoryAtURL:folderURL
|
||||||
|
includingPropertiesForKeys:properties
|
||||||
|
options:NSDirectoryEnumerationSkipsSubdirectoryDescendants
|
||||||
|
error:&error];
|
||||||
|
if (error && error.code != NSFileReadNoSuchFileError) {
|
||||||
|
[_loggerDelegate
|
||||||
|
firNetwork_logWithLevel:kFIRNetworkLogLevelDebug
|
||||||
|
messageCode:kFIRNetworkMessageCodeURLSession012
|
||||||
|
message:@"Cannot get files from the temporary network folder. Error"
|
||||||
|
context:error];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!directoryContent.count) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSTimeInterval now = [NSDate date].timeIntervalSince1970;
|
||||||
|
for (NSURL *tempFile in directoryContent) {
|
||||||
|
NSDate *creationDate;
|
||||||
|
BOOL getCreationDate =
|
||||||
|
[tempFile getResourceValue:&creationDate forKey:NSURLCreationDateKey error:NULL];
|
||||||
|
if (!getCreationDate) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
NSTimeInterval creationTimeInterval = creationDate.timeIntervalSince1970;
|
||||||
|
if (fabs(now - creationTimeInterval) > staleTime) {
|
||||||
|
[self removeTempItemAtURL:tempFile];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes the temporary file written to disk for sending the request. It has to be cleaned up
|
||||||
|
/// after the session is done.
|
||||||
|
- (void)removeTempItemAtURL:(NSURL *)fileURL {
|
||||||
|
if (!fileURL.absoluteString.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||||
|
NSError *error = nil;
|
||||||
|
|
||||||
|
if (![fileManager removeItemAtURL:fileURL error:&error] && error.code != NSFileNoSuchFileError) {
|
||||||
|
[_loggerDelegate
|
||||||
|
firNetwork_logWithLevel:kFIRNetworkLogLevelError
|
||||||
|
messageCode:kFIRNetworkMessageCodeURLSession013
|
||||||
|
message:@"Failed to remove temporary uploading data file. Error"
|
||||||
|
context:error.localizedDescription];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the fetcher with the session ID.
|
||||||
|
+ (instancetype)fetcherWithSessionIdentifier:(NSString *)sessionIdentifier {
|
||||||
|
NSMapTable *sessionIdentifierToFetcherMap = [self sessionIDToFetcherMap];
|
||||||
|
FIRNetworkURLSession *session = [sessionIdentifierToFetcherMap objectForKey:sessionIdentifier];
|
||||||
|
if (!session && [sessionIdentifier hasPrefix:kFIRNetworkBackgroundSessionConfigIDPrefix]) {
|
||||||
|
session = [[FIRNetworkURLSession alloc] initWithNetworkLoggerDelegate:nil];
|
||||||
|
[session setSessionID:sessionIdentifier];
|
||||||
|
[sessionIdentifierToFetcherMap setObject:session forKey:sessionIdentifier];
|
||||||
|
}
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a map of the fetcher by session ID. Creates a map if it is not created.
|
||||||
|
+ (NSMapTable *)sessionIDToFetcherMap {
|
||||||
|
static NSMapTable *sessionIDToFetcherMap;
|
||||||
|
|
||||||
|
static dispatch_once_t sessionMapOnceToken;
|
||||||
|
dispatch_once(&sessionMapOnceToken, ^{
|
||||||
|
sessionIDToFetcherMap = [NSMapTable strongToWeakObjectsMapTable];
|
||||||
|
});
|
||||||
|
return sessionIDToFetcherMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a map of system provided completion handler by session ID. Creates a map if it is not
|
||||||
|
/// created.
|
||||||
|
+ (FIRMutableDictionary *)sessionIDToSystemCompletionHandlerDictionary {
|
||||||
|
static FIRMutableDictionary *systemCompletionHandlers;
|
||||||
|
|
||||||
|
static dispatch_once_t systemCompletionHandlerOnceToken;
|
||||||
|
dispatch_once(&systemCompletionHandlerOnceToken, ^{
|
||||||
|
systemCompletionHandlers = [[FIRMutableDictionary alloc] init];
|
||||||
|
});
|
||||||
|
return systemCompletionHandlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSURL *)temporaryFilePathWithSessionID:(NSString *)sessionID {
|
||||||
|
NSString *tempName = [NSString stringWithFormat:@"FIRUpload_temp_%@", sessionID];
|
||||||
|
return [_networkDirectoryURL URLByAppendingPathComponent:tempName];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Makes sure that the directory to store temp files exists. If not, tries to create it and returns
|
||||||
|
/// YES. If there is anything wrong, returns NO.
|
||||||
|
- (BOOL)ensureTemporaryDirectoryExists {
|
||||||
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||||
|
NSError *error = nil;
|
||||||
|
|
||||||
|
// Create a temporary directory if it does not exist or was deleted.
|
||||||
|
if ([_networkDirectoryURL checkResourceIsReachableAndReturnError:&error]) {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error && error.code != NSFileReadNoSuchFileError) {
|
||||||
|
[_loggerDelegate
|
||||||
|
firNetwork_logWithLevel:kFIRNetworkLogLevelWarning
|
||||||
|
messageCode:kFIRNetworkMessageCodeURLSession014
|
||||||
|
message:@"Error while trying to access Network temp folder. Error"
|
||||||
|
context:error];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSError *writeError = nil;
|
||||||
|
|
||||||
|
[fileManager createDirectoryAtURL:_networkDirectoryURL
|
||||||
|
withIntermediateDirectories:YES
|
||||||
|
attributes:nil
|
||||||
|
error:&writeError];
|
||||||
|
if (writeError) {
|
||||||
|
[_loggerDelegate firNetwork_logWithLevel:kFIRNetworkLogLevelError
|
||||||
|
messageCode:kFIRNetworkMessageCodeURLSession015
|
||||||
|
message:@"Cannot create temporary directory. Error"
|
||||||
|
context:writeError];
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the iCloud exclusion attribute on the Documents URL.
|
||||||
|
[self excludeFromBackupForURL:_networkDirectoryURL];
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)excludeFromBackupForURL:(NSURL *)url {
|
||||||
|
if (!url.path) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the iCloud exclusion attribute on the Documents URL.
|
||||||
|
NSError *preventBackupError = nil;
|
||||||
|
[url setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:&preventBackupError];
|
||||||
|
if (preventBackupError) {
|
||||||
|
[_loggerDelegate firNetwork_logWithLevel:kFIRNetworkLogLevelError
|
||||||
|
messageCode:kFIRNetworkMessageCodeURLSession016
|
||||||
|
message:@"Cannot exclude temporary folder from iTunes backup"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)URLSession:(NSURLSession *)session
|
||||||
|
task:(NSURLSessionTask *)task
|
||||||
|
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
|
||||||
|
newRequest:(NSURLRequest *)request
|
||||||
|
completionHandler:(void (^)(NSURLRequest *))completionHandler {
|
||||||
|
NSArray *nonAllowedRedirectionCodes = @[
|
||||||
|
@(kFIRNetworkHTTPStatusCodeFound), @(kFIRNetworkHTTPStatusCodeMovedPermanently),
|
||||||
|
@(kFIRNetworkHTTPStatusCodeMovedTemporarily), @(kFIRNetworkHTTPStatusCodeMultipleChoices)
|
||||||
|
];
|
||||||
|
|
||||||
|
// Allow those not in the non allowed list to be followed.
|
||||||
|
if (![nonAllowedRedirectionCodes containsObject:@(response.statusCode)]) {
|
||||||
|
completionHandler(request);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not allow redirection if the response code is in the non-allowed list.
|
||||||
|
NSURLRequest *newRequest = request;
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
newRequest = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
completionHandler(newRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Helper Methods
|
||||||
|
|
||||||
|
- (void)callCompletionHandler:(FIRNetworkURLSessionCompletionHandler)handler
|
||||||
|
withResponse:(NSHTTPURLResponse *)response
|
||||||
|
data:(NSData *)data
|
||||||
|
error:(NSError *)error {
|
||||||
|
if (error) {
|
||||||
|
[_loggerDelegate firNetwork_logWithLevel:kFIRNetworkLogLevelError
|
||||||
|
messageCode:kFIRNetworkMessageCodeURLSession017
|
||||||
|
message:@"Encounter network error. Code, error"
|
||||||
|
contexts:@[ @(error.code), error ]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handler) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
handler(response, data, self->_sessionID, error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)populateSessionConfig:(NSURLSessionConfiguration *)sessionConfig
|
||||||
|
withRequest:(NSURLRequest *)request {
|
||||||
|
sessionConfig.HTTPAdditionalHeaders = request.allHTTPHeaderFields;
|
||||||
|
sessionConfig.timeoutIntervalForRequest = request.timeoutInterval;
|
||||||
|
sessionConfig.timeoutIntervalForResource = request.timeoutInterval;
|
||||||
|
sessionConfig.requestCachePolicy = request.cachePolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,445 @@
|
||||||
|
// Copyright 2017 Google
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#import "Private/FIRAppInternal.h"
|
||||||
|
#import "Private/FIRBundleUtil.h"
|
||||||
|
#import "Private/FIRErrors.h"
|
||||||
|
#import "Private/FIRLogger.h"
|
||||||
|
#import "Private/FIROptionsInternal.h"
|
||||||
|
|
||||||
|
// Keys for the strings in the plist file.
|
||||||
|
NSString *const kFIRAPIKey = @"API_KEY";
|
||||||
|
NSString *const kFIRTrackingID = @"TRACKING_ID";
|
||||||
|
NSString *const kFIRGoogleAppID = @"GOOGLE_APP_ID";
|
||||||
|
NSString *const kFIRClientID = @"CLIENT_ID";
|
||||||
|
NSString *const kFIRGCMSenderID = @"GCM_SENDER_ID";
|
||||||
|
NSString *const kFIRAndroidClientID = @"ANDROID_CLIENT_ID";
|
||||||
|
NSString *const kFIRDatabaseURL = @"DATABASE_URL";
|
||||||
|
NSString *const kFIRStorageBucket = @"STORAGE_BUCKET";
|
||||||
|
// The key to locate the expected bundle identifier in the plist file.
|
||||||
|
NSString *const kFIRBundleID = @"BUNDLE_ID";
|
||||||
|
// The key to locate the project identifier in the plist file.
|
||||||
|
NSString *const kFIRProjectID = @"PROJECT_ID";
|
||||||
|
|
||||||
|
NSString *const kFIRIsMeasurementEnabled = @"IS_MEASUREMENT_ENABLED";
|
||||||
|
NSString *const kFIRIsAnalyticsCollectionEnabled = @"FIREBASE_ANALYTICS_COLLECTION_ENABLED";
|
||||||
|
NSString *const kFIRIsAnalyticsCollectionDeactivated = @"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED";
|
||||||
|
|
||||||
|
NSString *const kFIRIsAnalyticsEnabled = @"IS_ANALYTICS_ENABLED";
|
||||||
|
NSString *const kFIRIsSignInEnabled = @"IS_SIGNIN_ENABLED";
|
||||||
|
|
||||||
|
// Library version ID.
|
||||||
|
NSString *const kFIRLibraryVersionID =
|
||||||
|
@"5" // Major version (one or more digits)
|
||||||
|
@"00" // Minor version (exactly 2 digits)
|
||||||
|
@"04" // Build number (exactly 2 digits)
|
||||||
|
@"000"; // Fixed "000"
|
||||||
|
// Plist file name.
|
||||||
|
NSString *const kServiceInfoFileName = @"GoogleService-Info";
|
||||||
|
// Plist file type.
|
||||||
|
NSString *const kServiceInfoFileType = @"plist";
|
||||||
|
|
||||||
|
// Exception raised from attempting to modify a FIROptions after it's been copied to a FIRApp.
|
||||||
|
NSString *const kFIRExceptionBadModification =
|
||||||
|
@"Attempted to modify options after it's set on FIRApp. Please modify all properties before "
|
||||||
|
@"initializing FIRApp.";
|
||||||
|
|
||||||
|
@interface FIROptions ()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This property maintains the actual configuration key-value pairs.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readwrite) NSMutableDictionary *optionsDictionary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls `analyticsOptionsDictionaryWithInfoDictionary:` using [NSBundle mainBundle].infoDictionary.
|
||||||
|
* It combines analytics options from both the infoDictionary and the GoogleService-Info.plist.
|
||||||
|
* Values which are present in the main plist override values from the GoogleService-Info.plist.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) NSDictionary *analyticsOptionsDictionary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combination of analytics options from both the infoDictionary and the GoogleService-Info.plist.
|
||||||
|
* Values which are present in the infoDictionary override values from the GoogleService-Info.plist.
|
||||||
|
*/
|
||||||
|
- (NSDictionary *)analyticsOptionsDictionaryWithInfoDictionary:(NSDictionary *)infoDictionary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throw exception if editing is locked when attempting to modify an option.
|
||||||
|
*/
|
||||||
|
- (void)checkEditingLocked;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FIROptions {
|
||||||
|
/// Backing variable for self.analyticsOptionsDictionary.
|
||||||
|
NSDictionary *_analyticsOptionsDictionary;
|
||||||
|
dispatch_once_t _createAnalyticsOptionsDictionaryOnce;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FIROptions *sDefaultOptions = nil;
|
||||||
|
static NSDictionary *sDefaultOptionsDictionary = nil;
|
||||||
|
|
||||||
|
#pragma mark - Public only for internal class methods
|
||||||
|
|
||||||
|
+ (FIROptions *)defaultOptions {
|
||||||
|
if (sDefaultOptions != nil) {
|
||||||
|
return sDefaultOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDictionary *defaultOptionsDictionary = [self defaultOptionsDictionary];
|
||||||
|
if (defaultOptionsDictionary == nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
sDefaultOptions = [[FIROptions alloc] initInternalWithOptionsDictionary:defaultOptionsDictionary];
|
||||||
|
return sDefaultOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private class methods
|
||||||
|
|
||||||
|
+ (void)initialize {
|
||||||
|
// Report FirebaseCore version for useragent string
|
||||||
|
NSRange major = NSMakeRange(0, 1);
|
||||||
|
NSRange minor = NSMakeRange(1, 2);
|
||||||
|
NSRange patch = NSMakeRange(3, 2);
|
||||||
|
[FIRApp
|
||||||
|
registerLibrary:@"fire-ios"
|
||||||
|
withVersion:[NSString stringWithFormat:@"%@.%d.%d",
|
||||||
|
[kFIRLibraryVersionID substringWithRange:major],
|
||||||
|
[[kFIRLibraryVersionID substringWithRange:minor]
|
||||||
|
intValue],
|
||||||
|
[[kFIRLibraryVersionID substringWithRange:patch]
|
||||||
|
intValue]]];
|
||||||
|
NSDictionary<NSString *, id> *info = [[NSBundle mainBundle] infoDictionary];
|
||||||
|
NSString *xcodeVersion = info[@"DTXcodeBuild"];
|
||||||
|
NSString *sdkVersion = info[@"DTSDKBuild"];
|
||||||
|
if (xcodeVersion) {
|
||||||
|
[FIRApp registerLibrary:@"xcode" withVersion:xcodeVersion];
|
||||||
|
}
|
||||||
|
if (sdkVersion) {
|
||||||
|
[FIRApp registerLibrary:@"apple-sdk" withVersion:sdkVersion];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSDictionary *)defaultOptionsDictionary {
|
||||||
|
if (sDefaultOptionsDictionary != nil) {
|
||||||
|
return sDefaultOptionsDictionary;
|
||||||
|
}
|
||||||
|
NSString *plistFilePath = [FIROptions plistFilePathWithName:kServiceInfoFileName];
|
||||||
|
if (plistFilePath == nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
sDefaultOptionsDictionary = [NSDictionary dictionaryWithContentsOfFile:plistFilePath];
|
||||||
|
if (sDefaultOptionsDictionary == nil) {
|
||||||
|
FIRLogError(kFIRLoggerCore, @"I-COR000011",
|
||||||
|
@"The configuration file is not a dictionary: "
|
||||||
|
@"'%@.%@'.",
|
||||||
|
kServiceInfoFileName, kServiceInfoFileType);
|
||||||
|
}
|
||||||
|
return sDefaultOptionsDictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the path of the plist file with a given file name.
|
||||||
|
+ (NSString *)plistFilePathWithName:(NSString *)fileName {
|
||||||
|
NSArray *bundles = [FIRBundleUtil relevantBundles];
|
||||||
|
NSString *plistFilePath =
|
||||||
|
[FIRBundleUtil optionsDictionaryPathWithResourceName:fileName
|
||||||
|
andFileType:kServiceInfoFileType
|
||||||
|
inBundles:bundles];
|
||||||
|
if (plistFilePath == nil) {
|
||||||
|
FIRLogError(kFIRLoggerCore, @"I-COR000012", @"Could not locate configuration file: '%@.%@'.",
|
||||||
|
fileName, kServiceInfoFileType);
|
||||||
|
}
|
||||||
|
return plistFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)resetDefaultOptions {
|
||||||
|
sDefaultOptions = nil;
|
||||||
|
sDefaultOptionsDictionary = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private instance methods
|
||||||
|
|
||||||
|
- (instancetype)initInternalWithOptionsDictionary:(NSDictionary *)optionsDictionary {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_optionsDictionary = [optionsDictionary mutableCopy];
|
||||||
|
_usingOptionsFromDefaultPlist = YES;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)copyWithZone:(NSZone *)zone {
|
||||||
|
FIROptions *newOptions = [[[self class] allocWithZone:zone] init];
|
||||||
|
if (newOptions) {
|
||||||
|
newOptions.optionsDictionary = self.optionsDictionary;
|
||||||
|
newOptions.deepLinkURLScheme = self.deepLinkURLScheme;
|
||||||
|
newOptions.editingLocked = self.isEditingLocked;
|
||||||
|
newOptions.usingOptionsFromDefaultPlist = self.usingOptionsFromDefaultPlist;
|
||||||
|
}
|
||||||
|
return newOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Public instance methods
|
||||||
|
|
||||||
|
- (instancetype)initWithContentsOfFile:(NSString *)plistPath {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
if (plistPath == nil) {
|
||||||
|
FIRLogError(kFIRLoggerCore, @"I-COR000013", @"The plist file path is nil.");
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
_optionsDictionary = [[NSDictionary dictionaryWithContentsOfFile:plistPath] mutableCopy];
|
||||||
|
if (_optionsDictionary == nil) {
|
||||||
|
FIRLogError(kFIRLoggerCore, @"I-COR000014",
|
||||||
|
@"The configuration file at %@ does not exist or "
|
||||||
|
@"is not a well-formed plist file.",
|
||||||
|
plistPath);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
// TODO: Do we want to validate the dictionary here? It says we do that already in
|
||||||
|
// the public header.
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithGoogleAppID:(NSString *)googleAppID GCMSenderID:(NSString *)GCMSenderID {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
NSMutableDictionary *mutableOptionsDict = [NSMutableDictionary dictionary];
|
||||||
|
[mutableOptionsDict setValue:googleAppID forKey:kFIRGoogleAppID];
|
||||||
|
[mutableOptionsDict setValue:GCMSenderID forKey:kFIRGCMSenderID];
|
||||||
|
[mutableOptionsDict setValue:[[NSBundle mainBundle] bundleIdentifier] forKey:kFIRBundleID];
|
||||||
|
self.optionsDictionary = mutableOptionsDict;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)APIKey {
|
||||||
|
return self.optionsDictionary[kFIRAPIKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)checkEditingLocked {
|
||||||
|
if (self.isEditingLocked) {
|
||||||
|
[NSException raise:kFirebaseCoreErrorDomain format:kFIRExceptionBadModification];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setAPIKey:(NSString *)APIKey {
|
||||||
|
[self checkEditingLocked];
|
||||||
|
_optionsDictionary[kFIRAPIKey] = [APIKey copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)clientID {
|
||||||
|
return self.optionsDictionary[kFIRClientID];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setClientID:(NSString *)clientID {
|
||||||
|
[self checkEditingLocked];
|
||||||
|
_optionsDictionary[kFIRClientID] = [clientID copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)trackingID {
|
||||||
|
return self.optionsDictionary[kFIRTrackingID];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setTrackingID:(NSString *)trackingID {
|
||||||
|
[self checkEditingLocked];
|
||||||
|
_optionsDictionary[kFIRTrackingID] = [trackingID copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)GCMSenderID {
|
||||||
|
return self.optionsDictionary[kFIRGCMSenderID];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setGCMSenderID:(NSString *)GCMSenderID {
|
||||||
|
[self checkEditingLocked];
|
||||||
|
_optionsDictionary[kFIRGCMSenderID] = [GCMSenderID copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)projectID {
|
||||||
|
return self.optionsDictionary[kFIRProjectID];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setProjectID:(NSString *)projectID {
|
||||||
|
[self checkEditingLocked];
|
||||||
|
_optionsDictionary[kFIRProjectID] = [projectID copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)androidClientID {
|
||||||
|
return self.optionsDictionary[kFIRAndroidClientID];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setAndroidClientID:(NSString *)androidClientID {
|
||||||
|
[self checkEditingLocked];
|
||||||
|
_optionsDictionary[kFIRAndroidClientID] = [androidClientID copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)googleAppID {
|
||||||
|
return self.optionsDictionary[kFIRGoogleAppID];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setGoogleAppID:(NSString *)googleAppID {
|
||||||
|
[self checkEditingLocked];
|
||||||
|
_optionsDictionary[kFIRGoogleAppID] = [googleAppID copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)libraryVersionID {
|
||||||
|
return kFIRLibraryVersionID;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setLibraryVersionID:(NSString *)libraryVersionID {
|
||||||
|
_optionsDictionary[kFIRLibraryVersionID] = [libraryVersionID copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)databaseURL {
|
||||||
|
return self.optionsDictionary[kFIRDatabaseURL];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setDatabaseURL:(NSString *)databaseURL {
|
||||||
|
[self checkEditingLocked];
|
||||||
|
|
||||||
|
_optionsDictionary[kFIRDatabaseURL] = [databaseURL copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)storageBucket {
|
||||||
|
return self.optionsDictionary[kFIRStorageBucket];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setStorageBucket:(NSString *)storageBucket {
|
||||||
|
[self checkEditingLocked];
|
||||||
|
_optionsDictionary[kFIRStorageBucket] = [storageBucket copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setDeepLinkURLScheme:(NSString *)deepLinkURLScheme {
|
||||||
|
[self checkEditingLocked];
|
||||||
|
_deepLinkURLScheme = [deepLinkURLScheme copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)bundleID {
|
||||||
|
return self.optionsDictionary[kFIRBundleID];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setBundleID:(NSString *)bundleID {
|
||||||
|
[self checkEditingLocked];
|
||||||
|
_optionsDictionary[kFIRBundleID] = [bundleID copy];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Internal instance methods
|
||||||
|
|
||||||
|
- (NSDictionary *)analyticsOptionsDictionaryWithInfoDictionary:(NSDictionary *)infoDictionary {
|
||||||
|
dispatch_once(&_createAnalyticsOptionsDictionaryOnce, ^{
|
||||||
|
NSMutableDictionary *tempAnalyticsOptions = [[NSMutableDictionary alloc] init];
|
||||||
|
NSArray *measurementKeys = @[
|
||||||
|
kFIRIsMeasurementEnabled, kFIRIsAnalyticsCollectionEnabled,
|
||||||
|
kFIRIsAnalyticsCollectionDeactivated
|
||||||
|
];
|
||||||
|
for (NSString *key in measurementKeys) {
|
||||||
|
id value = infoDictionary[key] ?: self.optionsDictionary[key] ?: nil;
|
||||||
|
if (!value) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tempAnalyticsOptions[key] = value;
|
||||||
|
}
|
||||||
|
self->_analyticsOptionsDictionary = tempAnalyticsOptions;
|
||||||
|
});
|
||||||
|
return _analyticsOptionsDictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)analyticsOptionsDictionary {
|
||||||
|
return [self analyticsOptionsDictionaryWithInfoDictionary:[NSBundle mainBundle].infoDictionary];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not Measurement was enabled. Measurement is enabled unless explicitly disabled in
|
||||||
|
* GoogleService-Info.plist. This uses the old plist flag IS_MEASUREMENT_ENABLED, which should still
|
||||||
|
* be supported.
|
||||||
|
*/
|
||||||
|
- (BOOL)isMeasurementEnabled {
|
||||||
|
if (self.isAnalyticsCollectionDeactivated) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
NSNumber *value = self.analyticsOptionsDictionary[kFIRIsMeasurementEnabled];
|
||||||
|
if (value == nil) {
|
||||||
|
// TODO: This could probably be cleaned up since FIROptions shouldn't know about FIRApp or have
|
||||||
|
// to check if it's the default app. The FIROptions instance can't be modified after
|
||||||
|
// `+configure` is called, so it's not a good place to copy it either in case the flag is
|
||||||
|
// changed at runtime.
|
||||||
|
|
||||||
|
// If no values are set for Analytics, fall back to the global collection switch in FIRApp.
|
||||||
|
// Analytics only supports the default FIRApp, so check that first.
|
||||||
|
if (![FIRApp isDefaultAppConfigured]) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to the default app's collection switch when the key is not in the dictionary.
|
||||||
|
return [FIRApp defaultApp].automaticDataCollectionEnabled;
|
||||||
|
}
|
||||||
|
return [value boolValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isAnalyticsCollectionExpicitlySet {
|
||||||
|
// If it's de-activated, it classifies as explicity set. If not, it's not a good enough indication
|
||||||
|
// that the developer wants FirebaseAnalytics enabled so continue checking.
|
||||||
|
if (self.isAnalyticsCollectionDeactivated) {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the current Analytics flag is set.
|
||||||
|
id collectionEnabledObject = self.analyticsOptionsDictionary[kFIRIsAnalyticsCollectionEnabled];
|
||||||
|
if (collectionEnabledObject && [collectionEnabledObject isKindOfClass:[NSNumber class]]) {
|
||||||
|
// It doesn't matter what the value is, it's explicitly set.
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the old measurement flag is set.
|
||||||
|
id measurementEnabledObject = self.analyticsOptionsDictionary[kFIRIsMeasurementEnabled];
|
||||||
|
if (measurementEnabledObject && [measurementEnabledObject isKindOfClass:[NSNumber class]]) {
|
||||||
|
// It doesn't matter what the value is, it's explicitly set.
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No flags are set to explicitly enable or disable FirebaseAnalytics.
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isAnalyticsCollectionEnabled {
|
||||||
|
if (self.isAnalyticsCollectionDeactivated) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
NSNumber *value = self.analyticsOptionsDictionary[kFIRIsAnalyticsCollectionEnabled];
|
||||||
|
if (value == nil) {
|
||||||
|
return self.isMeasurementEnabled; // Fall back to older plist flag.
|
||||||
|
}
|
||||||
|
return [value boolValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isAnalyticsCollectionDeactivated {
|
||||||
|
NSNumber *value = self.analyticsOptionsDictionary[kFIRIsAnalyticsCollectionDeactivated];
|
||||||
|
if (value == nil) {
|
||||||
|
return NO; // Analytics Collection is not deactivated when the key is not in the dictionary.
|
||||||
|
}
|
||||||
|
return [value boolValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isAnalyticsEnabled {
|
||||||
|
return [self.optionsDictionary[kFIRIsAnalyticsEnabled] boolValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isSignInEnabled {
|
||||||
|
return [self.optionsDictionary[kFIRIsSignInEnabled] boolValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,256 @@
|
||||||
|
// Copyright 2017 Google
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import "Private/FIRReachabilityChecker+Internal.h"
|
||||||
|
#import "Private/FIRReachabilityChecker.h"
|
||||||
|
|
||||||
|
#import "Private/FIRLogger.h"
|
||||||
|
#import "Private/FIRNetwork.h"
|
||||||
|
#import "Private/FIRNetworkMessageCode.h"
|
||||||
|
|
||||||
|
static void ReachabilityCallback(SCNetworkReachabilityRef reachability,
|
||||||
|
SCNetworkReachabilityFlags flags,
|
||||||
|
void *info);
|
||||||
|
|
||||||
|
static const struct FIRReachabilityApi kFIRDefaultReachabilityApi = {
|
||||||
|
SCNetworkReachabilityCreateWithName,
|
||||||
|
SCNetworkReachabilitySetCallback,
|
||||||
|
SCNetworkReachabilityScheduleWithRunLoop,
|
||||||
|
SCNetworkReachabilityUnscheduleFromRunLoop,
|
||||||
|
CFRelease,
|
||||||
|
};
|
||||||
|
|
||||||
|
static NSString *const kFIRReachabilityUnknownStatus = @"Unknown";
|
||||||
|
static NSString *const kFIRReachabilityConnectedStatus = @"Connected";
|
||||||
|
static NSString *const kFIRReachabilityDisconnectedStatus = @"Disconnected";
|
||||||
|
|
||||||
|
@interface FIRReachabilityChecker ()
|
||||||
|
|
||||||
|
@property(nonatomic, assign) const struct FIRReachabilityApi *reachabilityApi;
|
||||||
|
@property(nonatomic, assign) FIRReachabilityStatus reachabilityStatus;
|
||||||
|
@property(nonatomic, copy) NSString *host;
|
||||||
|
@property(nonatomic, assign) SCNetworkReachabilityRef reachability;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation FIRReachabilityChecker
|
||||||
|
|
||||||
|
@synthesize reachabilityApi = reachabilityApi_;
|
||||||
|
@synthesize reachability = reachability_;
|
||||||
|
|
||||||
|
- (const struct FIRReachabilityApi *)reachabilityApi {
|
||||||
|
return reachabilityApi_;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setReachabilityApi:(const struct FIRReachabilityApi *)reachabilityApi {
|
||||||
|
if (reachability_) {
|
||||||
|
NSString *message =
|
||||||
|
@"Cannot change reachability API while reachability is running. "
|
||||||
|
@"Call stop first.";
|
||||||
|
[loggerDelegate_ firNetwork_logWithLevel:kFIRNetworkLogLevelError
|
||||||
|
messageCode:kFIRNetworkMessageCodeReachabilityChecker000
|
||||||
|
message:message];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reachabilityApi_ = reachabilityApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
@synthesize reachabilityStatus = reachabilityStatus_;
|
||||||
|
@synthesize host = host_;
|
||||||
|
@synthesize reachabilityDelegate = reachabilityDelegate_;
|
||||||
|
@synthesize loggerDelegate = loggerDelegate_;
|
||||||
|
|
||||||
|
- (BOOL)isActive {
|
||||||
|
return reachability_ != nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setReachabilityDelegate:(id<FIRReachabilityDelegate>)reachabilityDelegate {
|
||||||
|
if (reachabilityDelegate &&
|
||||||
|
(![(NSObject *)reachabilityDelegate conformsToProtocol:@protocol(FIRReachabilityDelegate)])) {
|
||||||
|
FIRLogError(kFIRLoggerCore,
|
||||||
|
[NSString stringWithFormat:@"I-NET%06ld",
|
||||||
|
(long)kFIRNetworkMessageCodeReachabilityChecker005],
|
||||||
|
@"Reachability delegate doesn't conform to Reachability protocol.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reachabilityDelegate_ = reachabilityDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setLoggerDelegate:(id<FIRNetworkLoggerDelegate>)loggerDelegate {
|
||||||
|
if (loggerDelegate &&
|
||||||
|
(![(NSObject *)loggerDelegate conformsToProtocol:@protocol(FIRNetworkLoggerDelegate)])) {
|
||||||
|
FIRLogError(kFIRLoggerCore,
|
||||||
|
[NSString stringWithFormat:@"I-NET%06ld",
|
||||||
|
(long)kFIRNetworkMessageCodeReachabilityChecker006],
|
||||||
|
@"Reachability delegate doesn't conform to Logger protocol.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loggerDelegate_ = loggerDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithReachabilityDelegate:(id<FIRReachabilityDelegate>)reachabilityDelegate
|
||||||
|
loggerDelegate:(id<FIRNetworkLoggerDelegate>)loggerDelegate
|
||||||
|
withHost:(NSString *)host {
|
||||||
|
self = [super init];
|
||||||
|
|
||||||
|
[self setLoggerDelegate:loggerDelegate];
|
||||||
|
|
||||||
|
if (!host || !host.length) {
|
||||||
|
[loggerDelegate_ firNetwork_logWithLevel:kFIRNetworkLogLevelError
|
||||||
|
messageCode:kFIRNetworkMessageCodeReachabilityChecker001
|
||||||
|
message:@"Invalid host specified"];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
if (self) {
|
||||||
|
[self setReachabilityDelegate:reachabilityDelegate];
|
||||||
|
reachabilityApi_ = &kFIRDefaultReachabilityApi;
|
||||||
|
reachabilityStatus_ = kFIRReachabilityUnknown;
|
||||||
|
host_ = [host copy];
|
||||||
|
reachability_ = nil;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc {
|
||||||
|
reachabilityDelegate_ = nil;
|
||||||
|
loggerDelegate_ = nil;
|
||||||
|
[self stop];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)start {
|
||||||
|
if (!reachability_) {
|
||||||
|
reachability_ = reachabilityApi_->createWithNameFn(kCFAllocatorDefault, [host_ UTF8String]);
|
||||||
|
if (!reachability_) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
SCNetworkReachabilityContext context = {
|
||||||
|
0, /* version */
|
||||||
|
(__bridge void *)(self), /* info (passed as last parameter to reachability callback) */
|
||||||
|
NULL, /* retain */
|
||||||
|
NULL, /* release */
|
||||||
|
NULL /* copyDescription */
|
||||||
|
};
|
||||||
|
if (!reachabilityApi_->setCallbackFn(reachability_, ReachabilityCallback, &context) ||
|
||||||
|
!reachabilityApi_->scheduleWithRunLoopFn(reachability_, CFRunLoopGetMain(),
|
||||||
|
kCFRunLoopCommonModes)) {
|
||||||
|
reachabilityApi_->releaseFn(reachability_);
|
||||||
|
reachability_ = nil;
|
||||||
|
[loggerDelegate_ firNetwork_logWithLevel:kFIRNetworkLogLevelError
|
||||||
|
messageCode:kFIRNetworkMessageCodeReachabilityChecker002
|
||||||
|
message:@"Failed to start reachability handle"];
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[loggerDelegate_ firNetwork_logWithLevel:kFIRNetworkLogLevelDebug
|
||||||
|
messageCode:kFIRNetworkMessageCodeReachabilityChecker003
|
||||||
|
message:@"Monitoring the network status"];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)stop {
|
||||||
|
if (reachability_) {
|
||||||
|
reachabilityStatus_ = kFIRReachabilityUnknown;
|
||||||
|
reachabilityApi_->unscheduleFromRunLoopFn(reachability_, CFRunLoopGetMain(),
|
||||||
|
kCFRunLoopCommonModes);
|
||||||
|
reachabilityApi_->releaseFn(reachability_);
|
||||||
|
reachability_ = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (FIRReachabilityStatus)statusForFlags:(SCNetworkReachabilityFlags)flags {
|
||||||
|
FIRReachabilityStatus status = kFIRReachabilityNotReachable;
|
||||||
|
// If the Reachable flag is not set, we definitely don't have connectivity.
|
||||||
|
if (flags & kSCNetworkReachabilityFlagsReachable) {
|
||||||
|
// Reachable flag is set. Check further flags.
|
||||||
|
if (!(flags & kSCNetworkReachabilityFlagsConnectionRequired)) {
|
||||||
|
// Connection required flag is not set, so we have connectivity.
|
||||||
|
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||||
|
status = (flags & kSCNetworkReachabilityFlagsIsWWAN) ? kFIRReachabilityViaCellular
|
||||||
|
: kFIRReachabilityViaWifi;
|
||||||
|
#elif TARGET_OS_OSX
|
||||||
|
status = kFIRReachabilityViaWifi;
|
||||||
|
#endif
|
||||||
|
} else if ((flags & (kSCNetworkReachabilityFlagsConnectionOnDemand |
|
||||||
|
kSCNetworkReachabilityFlagsConnectionOnTraffic)) &&
|
||||||
|
!(flags & kSCNetworkReachabilityFlagsInterventionRequired)) {
|
||||||
|
// If the connection on demand or connection on traffic flag is set, and user intervention
|
||||||
|
// is not required, we have connectivity.
|
||||||
|
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||||
|
status = (flags & kSCNetworkReachabilityFlagsIsWWAN) ? kFIRReachabilityViaCellular
|
||||||
|
: kFIRReachabilityViaWifi;
|
||||||
|
#elif TARGET_OS_OSX
|
||||||
|
status = kFIRReachabilityViaWifi;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)reachabilityFlagsChanged:(SCNetworkReachabilityFlags)flags {
|
||||||
|
FIRReachabilityStatus status = [self statusForFlags:flags];
|
||||||
|
if (reachabilityStatus_ != status) {
|
||||||
|
NSString *reachabilityStatusString;
|
||||||
|
if (status == kFIRReachabilityUnknown) {
|
||||||
|
reachabilityStatusString = kFIRReachabilityUnknownStatus;
|
||||||
|
} else {
|
||||||
|
reachabilityStatusString = (status == kFIRReachabilityNotReachable)
|
||||||
|
? kFIRReachabilityDisconnectedStatus
|
||||||
|
: kFIRReachabilityConnectedStatus;
|
||||||
|
}
|
||||||
|
[loggerDelegate_ firNetwork_logWithLevel:kFIRNetworkLogLevelDebug
|
||||||
|
messageCode:kFIRNetworkMessageCodeReachabilityChecker004
|
||||||
|
message:@"Network status has changed. Code, status"
|
||||||
|
contexts:@[ @(status), reachabilityStatusString ]];
|
||||||
|
reachabilityStatus_ = status;
|
||||||
|
[reachabilityDelegate_ reachability:self statusChanged:reachabilityStatus_];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
static void ReachabilityCallback(SCNetworkReachabilityRef reachability,
|
||||||
|
SCNetworkReachabilityFlags flags,
|
||||||
|
void *info) {
|
||||||
|
FIRReachabilityChecker *checker = (__bridge FIRReachabilityChecker *)info;
|
||||||
|
[checker reachabilityFlagsChanged:flags];
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function used to be at the top of the file, but it was moved here
|
||||||
|
// as a workaround for a suspected compiler bug. When compiled in Release mode
|
||||||
|
// and run on an iOS device with WiFi disabled, the reachability code crashed
|
||||||
|
// when calling SCNetworkReachabilityScheduleWithRunLoop, or shortly thereafter.
|
||||||
|
// After unsuccessfully trying to diagnose the cause of the crash, it was
|
||||||
|
// discovered that moving this function to the end of the file magically fixed
|
||||||
|
// the crash. If you are going to edit this file, exercise caution and make sure
|
||||||
|
// to test thoroughly with an iOS device under various network conditions.
|
||||||
|
const NSString *FIRReachabilityStatusString(FIRReachabilityStatus status) {
|
||||||
|
switch (status) {
|
||||||
|
case kFIRReachabilityUnknown:
|
||||||
|
return @"Reachability Unknown";
|
||||||
|
|
||||||
|
case kFIRReachabilityNotReachable:
|
||||||
|
return @"Not reachable";
|
||||||
|
|
||||||
|
case kFIRReachabilityViaWifi:
|
||||||
|
return @"Reachable via Wifi";
|
||||||
|
|
||||||
|
case kFIRReachabilityViaCellular:
|
||||||
|
return @"Reachable via Cellular Data";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return [NSString stringWithFormat:@"Invalid reachability status %d", (int)status];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef Firebase_VERSION
|
||||||
|
#error "Firebase_VERSION is not defined: add -DFirebase_VERSION=... to the build invocation"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FIRCore_VERSION
|
||||||
|
#error "FIRCore_VERSION is not defined: add -DFIRCore_VERSION=... to the build invocation"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// The following two macros supply the incantation so that the C
|
||||||
|
// preprocessor does not try to parse the version as a floating
|
||||||
|
// point number. See
|
||||||
|
// https://www.guyrutenberg.com/2008/12/20/expanding-macros-into-string-constants-in-c/
|
||||||
|
#define STR(x) STR_EXPAND(x)
|
||||||
|
#define STR_EXPAND(x) #x
|
||||||
|
|
||||||
|
const unsigned char *const FIRVersionString = (const unsigned char *const)STR(Firebase_VERSION);
|
||||||
|
const unsigned char *const FIRCoreVersionString = (const unsigned char *const)STR(FIRCore_VERSION);
|
49
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIRAnalyticsConfiguration+Internal.h
generated
Normal file
49
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIRAnalyticsConfiguration+Internal.h
generated
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "FIRAnalyticsConfiguration.h"
|
||||||
|
|
||||||
|
/// Values stored in analyticsEnabledState. Never alter these constants since they must match with
|
||||||
|
/// values persisted to disk.
|
||||||
|
typedef NS_ENUM(int64_t, FIRAnalyticsEnabledState) {
|
||||||
|
// 0 is the default value for keys not found stored in persisted config, so it cannot represent
|
||||||
|
// kFIRAnalyticsEnabledStateSetNo. It must represent kFIRAnalyticsEnabledStateNotSet.
|
||||||
|
kFIRAnalyticsEnabledStateNotSet = 0,
|
||||||
|
kFIRAnalyticsEnabledStateSetYes = 1,
|
||||||
|
kFIRAnalyticsEnabledStateSetNo = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The user defaults key for the persisted measurementEnabledState value. FIRAPersistedConfig reads
|
||||||
|
/// measurementEnabledState using this same key.
|
||||||
|
static NSString *const kFIRAPersistedConfigMeasurementEnabledStateKey =
|
||||||
|
@"/google/measurement/measurement_enabled_state";
|
||||||
|
|
||||||
|
static NSString *const kFIRAnalyticsConfigurationSetEnabledNotification =
|
||||||
|
@"FIRAnalyticsConfigurationSetEnabledNotification";
|
||||||
|
static NSString *const kFIRAnalyticsConfigurationSetMinimumSessionIntervalNotification =
|
||||||
|
@"FIRAnalyticsConfigurationSetMinimumSessionIntervalNotification";
|
||||||
|
static NSString *const kFIRAnalyticsConfigurationSetSessionTimeoutIntervalNotification =
|
||||||
|
@"FIRAnalyticsConfigurationSetSessionTimeoutIntervalNotification";
|
||||||
|
|
||||||
|
@interface FIRAnalyticsConfiguration (Internal)
|
||||||
|
|
||||||
|
/// Sets whether analytics collection is enabled for this app on this device, and a flag to persist
|
||||||
|
/// the value or not. The setting should not be persisted if being set by the global data collection
|
||||||
|
/// flag.
|
||||||
|
- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled
|
||||||
|
persistSetting:(BOOL)shouldPersist;
|
||||||
|
|
||||||
|
@end
|
48
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIRAppAssociationRegistration.h
generated
Normal file
48
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIRAppAssociationRegistration.h
generated
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/** @class FIRAppAssociationRegistration
|
||||||
|
@brief Manages object associations as a singleton-dependent: At most one object is
|
||||||
|
registered for any given host/key pair, and the object shall be created on-the-fly when
|
||||||
|
asked for.
|
||||||
|
*/
|
||||||
|
@interface FIRAppAssociationRegistration <ObjectType> : NSObject
|
||||||
|
|
||||||
|
/** @fn registeredObjectWithHost:key:creationBlock:
|
||||||
|
@brief Retrieves the registered object with a particular host and key.
|
||||||
|
@param host The host object.
|
||||||
|
@param key The key to specify the registered object on the host.
|
||||||
|
@param creationBlock The block to return the object to be registered if not already.
|
||||||
|
The block is executed immediately before this method returns if it is executed at all.
|
||||||
|
It can also be executed multiple times across different method invocations if previous
|
||||||
|
execution of the block returns @c nil.
|
||||||
|
@return The registered object for the host/key pair, or @c nil if no object is registered
|
||||||
|
and @c creationBlock returns @c nil.
|
||||||
|
@remarks The method is thread-safe but non-reentrant in the sense that attempting to call this
|
||||||
|
method again within the @c creationBlock with the same host/key pair raises an exception.
|
||||||
|
The registered object is retained by the host.
|
||||||
|
*/
|
||||||
|
+ (nullable ObjectType)registeredObjectWithHost:(id)host
|
||||||
|
key:(NSString *)key
|
||||||
|
creationBlock:(ObjectType _Nullable (^)(void))creationBlock;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,214 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "FIRApp.h"
|
||||||
|
#import "FIRErrors.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The internal interface to FIRApp. This is meant for first-party integrators, who need to receive
|
||||||
|
* FIRApp notifications, log info about the success or failure of their configuration, and access
|
||||||
|
* other internal functionality of FIRApp.
|
||||||
|
*
|
||||||
|
* TODO(b/28296561): Restructure this header.
|
||||||
|
*/
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSInteger, FIRConfigType) {
|
||||||
|
FIRConfigTypeCore = 1,
|
||||||
|
FIRConfigTypeSDK = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Names of services provided by Firebase.
|
||||||
|
*/
|
||||||
|
extern NSString *const kFIRServiceAdMob;
|
||||||
|
extern NSString *const kFIRServiceAuth;
|
||||||
|
extern NSString *const kFIRServiceAuthUI;
|
||||||
|
extern NSString *const kFIRServiceCrash;
|
||||||
|
extern NSString *const kFIRServiceDatabase;
|
||||||
|
extern NSString *const kFIRServiceDynamicLinks;
|
||||||
|
extern NSString *const kFIRServiceInstanceID;
|
||||||
|
extern NSString *const kFIRServiceInvites;
|
||||||
|
extern NSString *const kFIRServiceMessaging;
|
||||||
|
extern NSString *const kFIRServiceMeasurement;
|
||||||
|
extern NSString *const kFIRServiceRemoteConfig;
|
||||||
|
extern NSString *const kFIRServiceStorage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Names of services provided by the Google pod, but logged by the Firebase pod.
|
||||||
|
*/
|
||||||
|
extern NSString *const kGGLServiceAnalytics;
|
||||||
|
extern NSString *const kGGLServiceSignIn;
|
||||||
|
|
||||||
|
extern NSString *const kFIRDefaultAppName;
|
||||||
|
extern NSString *const kFIRAppReadyToConfigureSDKNotification;
|
||||||
|
extern NSString *const kFIRAppDeleteNotification;
|
||||||
|
extern NSString *const kFIRAppIsDefaultAppKey;
|
||||||
|
extern NSString *const kFIRAppNameKey;
|
||||||
|
extern NSString *const kFIRGoogleAppIDKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The format string for the User Defaults key used for storing the data collection enabled flag.
|
||||||
|
* This includes formatting to append the Firebase App's name.
|
||||||
|
*/
|
||||||
|
extern NSString *const kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The plist key used for storing the data collection enabled flag.
|
||||||
|
*/
|
||||||
|
extern NSString *const kFIRGlobalAppDataCollectionEnabledPlistKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A notification fired containing diagnostic information when SDK errors occur.
|
||||||
|
*/
|
||||||
|
extern NSString *const kFIRAppDiagnosticsNotification;
|
||||||
|
|
||||||
|
/** @var FIRAuthStateDidChangeInternalNotification
|
||||||
|
@brief The name of the @c NSNotificationCenter notification which is posted when the auth state
|
||||||
|
changes (e.g. a new token has been produced, a user logs in or out). The object parameter of
|
||||||
|
the notification is a dictionary possibly containing the key:
|
||||||
|
@c FIRAuthStateDidChangeInternalNotificationTokenKey (the new access token.) If it does not
|
||||||
|
contain this key it indicates a sign-out event took place.
|
||||||
|
*/
|
||||||
|
extern NSString *const FIRAuthStateDidChangeInternalNotification;
|
||||||
|
|
||||||
|
/** @var FIRAuthStateDidChangeInternalNotificationTokenKey
|
||||||
|
@brief A key present in the dictionary object parameter of the
|
||||||
|
@c FIRAuthStateDidChangeInternalNotification notification. The value associated with this
|
||||||
|
key will contain the new access token.
|
||||||
|
*/
|
||||||
|
extern NSString *const FIRAuthStateDidChangeInternalNotificationTokenKey;
|
||||||
|
|
||||||
|
/** @var FIRAuthStateDidChangeInternalNotificationAppKey
|
||||||
|
@brief A key present in the dictionary object parameter of the
|
||||||
|
@c FIRAuthStateDidChangeInternalNotification notification. The value associated with this
|
||||||
|
key will contain the FIRApp associated with the auth instance.
|
||||||
|
*/
|
||||||
|
extern NSString *const FIRAuthStateDidChangeInternalNotificationAppKey;
|
||||||
|
|
||||||
|
/** @var FIRAuthStateDidChangeInternalNotificationUIDKey
|
||||||
|
@brief A key present in the dictionary object parameter of the
|
||||||
|
@c FIRAuthStateDidChangeInternalNotification notification. The value associated with this
|
||||||
|
key will contain the new user's UID (or nil if there is no longer a user signed in).
|
||||||
|
*/
|
||||||
|
extern NSString *const FIRAuthStateDidChangeInternalNotificationUIDKey;
|
||||||
|
|
||||||
|
/** @typedef FIRTokenCallback
|
||||||
|
@brief The type of block which gets called when a token is ready.
|
||||||
|
*/
|
||||||
|
typedef void (^FIRTokenCallback)(NSString *_Nullable token, NSError *_Nullable error);
|
||||||
|
|
||||||
|
/** @typedef FIRAppGetTokenImplementation
|
||||||
|
@brief The type of block which can provide an implementation for the @c getTokenWithCallback:
|
||||||
|
method.
|
||||||
|
@param forceRefresh Forces the token to be refreshed.
|
||||||
|
@param callback The block which should be invoked when the async call completes.
|
||||||
|
*/
|
||||||
|
typedef void (^FIRAppGetTokenImplementation)(BOOL forceRefresh, FIRTokenCallback callback);
|
||||||
|
|
||||||
|
/** @typedef FIRAppGetUID
|
||||||
|
@brief The type of block which can provide an implementation for the @c getUID method.
|
||||||
|
*/
|
||||||
|
typedef NSString *_Nullable (^FIRAppGetUIDImplementation)(void);
|
||||||
|
|
||||||
|
@interface FIRApp ()
|
||||||
|
|
||||||
|
/** @property getTokenImplementation
|
||||||
|
@brief Gets or sets the block to use for the implementation of
|
||||||
|
@c getTokenForcingRefresh:withCallback:
|
||||||
|
*/
|
||||||
|
@property(nonatomic, copy) FIRAppGetTokenImplementation getTokenImplementation;
|
||||||
|
|
||||||
|
/** @property getUIDImplementation
|
||||||
|
@brief Gets or sets the block to use for the implementation of @c getUID.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, copy) FIRAppGetUIDImplementation getUIDImplementation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an error for failing to configure a subspec service. This method is called by each
|
||||||
|
* FIRApp notification listener.
|
||||||
|
*/
|
||||||
|
+ (NSError *)errorForSubspecConfigurationFailureWithDomain:(NSString *)domain
|
||||||
|
errorCode:(FIRErrorCode)code
|
||||||
|
service:(NSString *)service
|
||||||
|
reason:(NSString *)reason;
|
||||||
|
/**
|
||||||
|
* Checks if the default app is configured without trying to configure it.
|
||||||
|
*/
|
||||||
|
+ (BOOL)isDefaultAppConfigured;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a given third-party library with the given version number to be reported for
|
||||||
|
* analyitcs.
|
||||||
|
*
|
||||||
|
* @param library Name of the library
|
||||||
|
* @param version Version of the library
|
||||||
|
*/
|
||||||
|
// clang-format off
|
||||||
|
+ (void)registerLibrary:(NSString *)library
|
||||||
|
withVersion:(NSString *)version NS_SWIFT_NAME(registerLibrary(_:version:));
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A concatenated string representing all the third-party libraries and version numbers.
|
||||||
|
*/
|
||||||
|
+ (NSString *)firebaseUserAgent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by each SDK to send logs about SDK configuration status to Clearcut.
|
||||||
|
*/
|
||||||
|
- (void)sendLogsWithServiceName:(NSString *)serviceName
|
||||||
|
version:(NSString *)version
|
||||||
|
error:(NSError *)error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used by the unit tests in eack SDK to reset FIRApp. This method is thread unsafe.
|
||||||
|
*/
|
||||||
|
+ (void)resetApps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used by the unit tests in each SDK to set customized options.
|
||||||
|
*/
|
||||||
|
- (instancetype)initInstanceWithName:(NSString *)name options:(FIROptions *)options;
|
||||||
|
|
||||||
|
/** @fn getTokenForcingRefresh:withCallback:
|
||||||
|
@brief Retrieves the Firebase authentication token, possibly refreshing it.
|
||||||
|
@param forceRefresh Forces a token refresh. Useful if the token becomes invalid for some reason
|
||||||
|
other than an expiration.
|
||||||
|
@param callback The block to invoke when the token is available.
|
||||||
|
*/
|
||||||
|
- (void)getTokenForcingRefresh:(BOOL)forceRefresh withCallback:(FIRTokenCallback)callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose the UID of the current user for Firestore.
|
||||||
|
*/
|
||||||
|
- (nullable NSString *)getUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WARNING: THIS SETTING DOES NOT WORK YET. IT WILL BE MOVED TO THE PUBLIC HEADER ONCE ALL SDKS
|
||||||
|
* CONFORM TO THIS PREFERENCE. DO NOT RELY ON IT.
|
||||||
|
*
|
||||||
|
* Gets or sets whether automatic data collection is enabled for all products. Defaults to `YES`
|
||||||
|
* unless `FirebaseAutomaticDataCollectionEnabled` is set to `NO` in your app's Info.plist. This
|
||||||
|
* value is persisted across runs of the app so that it can be set once when users have consented to
|
||||||
|
* collection.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readwrite, getter=isAutomaticDataCollectionEnabled)
|
||||||
|
BOOL automaticDataCollectionEnabled;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides utilities for accessing resources in bundles.
|
||||||
|
*/
|
||||||
|
@interface FIRBundleUtil : NSObject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds all relevant bundles, starting with [NSBundle mainBundle].
|
||||||
|
*/
|
||||||
|
+ (NSArray *)relevantBundles;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the options dictionary from one of the provided bundles.
|
||||||
|
*
|
||||||
|
* @param resourceName The resource name, e.g. @"GoogleService-Info".
|
||||||
|
* @param fileType The file type (extension), e.g. @"plist".
|
||||||
|
* @param bundles The bundles to expect, in priority order. See also
|
||||||
|
* +[FIRBundleUtil relevantBundles].
|
||||||
|
*/
|
||||||
|
+ (NSString *)optionsDictionaryPathWithResourceName:(NSString *)resourceName
|
||||||
|
andFileType:(NSString *)fileType
|
||||||
|
inBundles:(NSArray *)bundles;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds URL schemes defined in all relevant bundles, starting with those from
|
||||||
|
* [NSBundle mainBundle].
|
||||||
|
*/
|
||||||
|
+ (NSArray *)relevantURLSchemes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the bundle identifier exists in the given bundles.
|
||||||
|
*/
|
||||||
|
+ (BOOL)hasBundleIdentifier:(NSString *)bundleIdentifier inBundles:(NSArray *)bundles;
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Error codes in Firebase error domain. */
|
||||||
|
typedef NS_ENUM(NSInteger, FIRErrorCode) {
|
||||||
|
/**
|
||||||
|
* Unknown error.
|
||||||
|
*/
|
||||||
|
FIRErrorCodeUnknown = 0,
|
||||||
|
/**
|
||||||
|
* Loading data from the GoogleService-Info.plist file failed. This is a fatal error and should
|
||||||
|
* not be ignored. Further calls to the API will fail and/or possibly cause crashes.
|
||||||
|
*/
|
||||||
|
FIRErrorCodeInvalidPlistFile = -100,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validating the Google App ID format failed.
|
||||||
|
*/
|
||||||
|
FIRErrorCodeInvalidAppID = -101,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error code for failing to configure a specific service.
|
||||||
|
*/
|
||||||
|
FIRErrorCodeAdMobFailed = -110,
|
||||||
|
FIRErrorCodeAppInviteFailed = -112,
|
||||||
|
FIRErrorCodeCloudMessagingFailed = -113,
|
||||||
|
FIRErrorCodeConfigFailed = -114,
|
||||||
|
FIRErrorCodeDatabaseFailed = -115,
|
||||||
|
FIRErrorCodeCrashReportingFailed = -118,
|
||||||
|
FIRErrorCodeDurableDeepLinkFailed = -119,
|
||||||
|
FIRErrorCodeAuthFailed = -120,
|
||||||
|
FIRErrorCodeInstanceIDFailed = -121,
|
||||||
|
FIRErrorCodeStorageFailed = -123,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error codes returned by Dynamic Links
|
||||||
|
*/
|
||||||
|
FIRErrorCodeDynamicLinksStrongMatchNotAvailable = -124,
|
||||||
|
FIRErrorCodeDynamicLinksManualRetrievalNotEnabled = -125,
|
||||||
|
FIRErrorCodeDynamicLinksPendingLinkOnlyAvailableAtFirstLaunch = -126,
|
||||||
|
FIRErrorCodeDynamicLinksPendingLinkRetrievalAlreadyRunning = -127,
|
||||||
|
};
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#include "FIRErrorCode.h"
|
||||||
|
|
||||||
|
extern NSString *const kFirebaseErrorDomain;
|
||||||
|
extern NSString *const kFirebaseAdMobErrorDomain;
|
||||||
|
extern NSString *const kFirebaseAppInviteErrorDomain;
|
||||||
|
extern NSString *const kFirebaseAuthErrorDomain;
|
||||||
|
extern NSString *const kFirebaseCloudMessagingErrorDomain;
|
||||||
|
extern NSString *const kFirebaseConfigErrorDomain;
|
||||||
|
extern NSString *const kFirebaseCoreErrorDomain;
|
||||||
|
extern NSString *const kFirebaseCrashReportingErrorDomain;
|
||||||
|
extern NSString *const kFirebaseDatabaseErrorDomain;
|
||||||
|
extern NSString *const kFirebaseDurableDeepLinkErrorDomain;
|
||||||
|
extern NSString *const kFirebaseInstanceIDErrorDomain;
|
||||||
|
extern NSString *const kFirebasePerfErrorDomain;
|
||||||
|
extern NSString *const kFirebaseStorageErrorDomain;
|
|
@ -0,0 +1,159 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import "FIRLoggerLevel.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Firebase services used in Firebase logger.
|
||||||
|
*/
|
||||||
|
typedef NSString *const FIRLoggerService;
|
||||||
|
|
||||||
|
extern FIRLoggerService kFIRLoggerABTesting;
|
||||||
|
extern FIRLoggerService kFIRLoggerAdMob;
|
||||||
|
extern FIRLoggerService kFIRLoggerAnalytics;
|
||||||
|
extern FIRLoggerService kFIRLoggerAuth;
|
||||||
|
extern FIRLoggerService kFIRLoggerCore;
|
||||||
|
extern FIRLoggerService kFIRLoggerCrash;
|
||||||
|
extern FIRLoggerService kFIRLoggerDatabase;
|
||||||
|
extern FIRLoggerService kFIRLoggerDynamicLinks;
|
||||||
|
extern FIRLoggerService kFIRLoggerFirestore;
|
||||||
|
extern FIRLoggerService kFIRLoggerInstanceID;
|
||||||
|
extern FIRLoggerService kFIRLoggerInvites;
|
||||||
|
extern FIRLoggerService kFIRLoggerMLKit;
|
||||||
|
extern FIRLoggerService kFIRLoggerMessaging;
|
||||||
|
extern FIRLoggerService kFIRLoggerPerf;
|
||||||
|
extern FIRLoggerService kFIRLoggerRemoteConfig;
|
||||||
|
extern FIRLoggerService kFIRLoggerStorage;
|
||||||
|
extern FIRLoggerService kFIRLoggerSwizzler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The key used to store the logger's error count.
|
||||||
|
*/
|
||||||
|
extern NSString *const kFIRLoggerErrorCountKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The key used to store the logger's warning count.
|
||||||
|
*/
|
||||||
|
extern NSString *const kFIRLoggerWarningCountKey;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables or disables Analytics debug mode.
|
||||||
|
* If set to YES, the logging level for Analytics will be set to FIRLoggerLevelDebug.
|
||||||
|
* Enabling the debug mode has no effect if the app is running from App Store.
|
||||||
|
* (required) analytics debug mode flag.
|
||||||
|
*/
|
||||||
|
void FIRSetAnalyticsDebugMode(BOOL analyticsDebugMode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the default logging level of FIRLoggerLevelNotice to a user-specified level.
|
||||||
|
* The default level cannot be set above FIRLoggerLevelNotice if the app is running from App Store.
|
||||||
|
* (required) log level (one of the FIRLoggerLevel enum values).
|
||||||
|
*/
|
||||||
|
void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the specified logger level is loggable given the current settings.
|
||||||
|
* (required) log level (one of the FIRLoggerLevel enum values).
|
||||||
|
* (required) whether or not this function is called from the Analytics component.
|
||||||
|
*/
|
||||||
|
BOOL FIRIsLoggableLevel(FIRLoggerLevel loggerLevel, BOOL analyticsComponent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs a message to the Xcode console and the device log. If running from AppStore, will
|
||||||
|
* not log any messages with a level higher than FIRLoggerLevelNotice to avoid log spamming.
|
||||||
|
* (required) log level (one of the FIRLoggerLevel enum values).
|
||||||
|
* (required) service name of type FIRLoggerService.
|
||||||
|
* (required) message code starting with "I-" which means iOS, followed by a capitalized
|
||||||
|
* three-character service identifier and a six digit integer message ID that is unique
|
||||||
|
* within the service.
|
||||||
|
* An example of the message code is @"I-COR000001".
|
||||||
|
* (required) message string which can be a format string.
|
||||||
|
* (optional) variable arguments list obtained from calling va_start, used when message is a format
|
||||||
|
* string.
|
||||||
|
*/
|
||||||
|
extern void FIRLogBasic(FIRLoggerLevel level,
|
||||||
|
FIRLoggerService service,
|
||||||
|
NSString *messageCode,
|
||||||
|
NSString *message,
|
||||||
|
// On 64-bit simulators, va_list is not a pointer, so cannot be marked nullable
|
||||||
|
// See: http://stackoverflow.com/q/29095469
|
||||||
|
#if __LP64__ && TARGET_OS_SIMULATOR || TARGET_OS_OSX
|
||||||
|
va_list args_ptr
|
||||||
|
#else
|
||||||
|
va_list _Nullable args_ptr
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The following functions accept the following parameters in order:
|
||||||
|
* (required) service name of type FIRLoggerService.
|
||||||
|
* (required) message code starting from "I-" which means iOS, followed by a capitalized
|
||||||
|
* three-character service identifier and a six digit integer message ID that is unique
|
||||||
|
* within the service.
|
||||||
|
* An example of the message code is @"I-COR000001".
|
||||||
|
* See go/firebase-log-proposal for details.
|
||||||
|
* (required) message string which can be a format string.
|
||||||
|
* (optional) the list of arguments to substitute into the format string.
|
||||||
|
* Example usage:
|
||||||
|
* FIRLogError(kFIRLoggerCore, @"I-COR000001", @"Configuration of %@ failed.", app.name);
|
||||||
|
*/
|
||||||
|
extern void FIRLogError(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
|
||||||
|
NS_FORMAT_FUNCTION(3, 4);
|
||||||
|
extern void FIRLogWarning(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
|
||||||
|
NS_FORMAT_FUNCTION(3, 4);
|
||||||
|
extern void FIRLogNotice(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
|
||||||
|
NS_FORMAT_FUNCTION(3, 4);
|
||||||
|
extern void FIRLogInfo(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
|
||||||
|
NS_FORMAT_FUNCTION(3, 4);
|
||||||
|
extern void FIRLogDebug(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
|
||||||
|
NS_FORMAT_FUNCTION(3, 4);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
@interface FIRLoggerWrapper : NSObject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Objective-C wrapper for FIRLogBasic to allow weak linking to FIRLogger
|
||||||
|
* (required) log level (one of the FIRLoggerLevel enum values).
|
||||||
|
* (required) service name of type FIRLoggerService.
|
||||||
|
* (required) message code starting with "I-" which means iOS, followed by a capitalized
|
||||||
|
* three-character service identifier and a six digit integer message ID that is unique
|
||||||
|
* within the service.
|
||||||
|
* An example of the message code is @"I-COR000001".
|
||||||
|
* (required) message string which can be a format string.
|
||||||
|
* (optional) variable arguments list obtained from calling va_start, used when message is a format
|
||||||
|
* string.
|
||||||
|
*/
|
||||||
|
|
||||||
|
+ (void)logWithLevel:(FIRLoggerLevel)level
|
||||||
|
withService:(FIRLoggerService)service
|
||||||
|
withCode:(NSString *)messageCode
|
||||||
|
withMessage:(NSString *)message
|
||||||
|
withArgs:(va_list)args;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
46
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIRMutableDictionary.h
generated
Normal file
46
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIRMutableDictionary.h
generated
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
/// A mutable dictionary that provides atomic accessor and mutators.
|
||||||
|
@interface FIRMutableDictionary : NSObject
|
||||||
|
|
||||||
|
/// Returns an object given a key in the dictionary or nil if not found.
|
||||||
|
- (id)objectForKey:(id)key;
|
||||||
|
|
||||||
|
/// Updates the object given its key or adds it to the dictionary if it is not in the dictionary.
|
||||||
|
- (void)setObject:(id)object forKey:(id<NSCopying>)key;
|
||||||
|
|
||||||
|
/// Removes the object given its session ID from the dictionary.
|
||||||
|
- (void)removeObjectForKey:(id)key;
|
||||||
|
|
||||||
|
/// Removes all objects.
|
||||||
|
- (void)removeAllObjects;
|
||||||
|
|
||||||
|
/// Returns the number of current objects in the dictionary.
|
||||||
|
- (NSUInteger)count;
|
||||||
|
|
||||||
|
/// Returns an object given a key in the dictionary or nil if not found.
|
||||||
|
- (id)objectForKeyedSubscript:(id<NSCopying>)key;
|
||||||
|
|
||||||
|
/// Updates the object given its key or adds it to the dictionary if it is not in the dictionary.
|
||||||
|
- (void)setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key;
|
||||||
|
|
||||||
|
/// Returns the immutable dictionary.
|
||||||
|
- (NSDictionary *)dictionary;
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import "FIRNetworkConstants.h"
|
||||||
|
#import "FIRNetworkLoggerProtocol.h"
|
||||||
|
#import "FIRNetworkURLSession.h"
|
||||||
|
|
||||||
|
/// Delegate protocol for FIRNetwork events.
|
||||||
|
@protocol FIRNetworkReachabilityDelegate
|
||||||
|
|
||||||
|
/// Tells the delegate to handle events when the network reachability changes to connected or not
|
||||||
|
/// connected.
|
||||||
|
- (void)reachabilityDidChange;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
/// The Network component that provides network status and handles network requests and responses.
|
||||||
|
/// This is not thread safe.
|
||||||
|
///
|
||||||
|
/// NOTE:
|
||||||
|
/// User must add FIRAnalytics handleEventsForBackgroundURLSessionID:completionHandler to the
|
||||||
|
/// AppDelegate application:handleEventsForBackgroundURLSession:completionHandler:
|
||||||
|
@interface FIRNetwork : NSObject
|
||||||
|
|
||||||
|
/// Indicates if network connectivity is available.
|
||||||
|
@property(nonatomic, readonly, getter=isNetworkConnected) BOOL networkConnected;
|
||||||
|
|
||||||
|
/// Indicates if there are any uploads in progress.
|
||||||
|
@property(nonatomic, readonly, getter=hasUploadInProgress) BOOL uploadInProgress;
|
||||||
|
|
||||||
|
/// An optional delegate that can be used in the event when network reachability changes.
|
||||||
|
@property(nonatomic, weak) id<FIRNetworkReachabilityDelegate> reachabilityDelegate;
|
||||||
|
|
||||||
|
/// An optional delegate that can be used to log messages, warnings or errors that occur in the
|
||||||
|
/// network operations.
|
||||||
|
@property(nonatomic, weak) id<FIRNetworkLoggerDelegate> loggerDelegate;
|
||||||
|
|
||||||
|
/// Indicates whether the logger should display debug messages.
|
||||||
|
@property(nonatomic, assign) BOOL isDebugModeEnabled;
|
||||||
|
|
||||||
|
/// The time interval in seconds for the network request to timeout.
|
||||||
|
@property(nonatomic, assign) NSTimeInterval timeoutInterval;
|
||||||
|
|
||||||
|
/// Initializes with the default reachability host.
|
||||||
|
- (instancetype)init;
|
||||||
|
|
||||||
|
/// Initializes with a custom reachability host.
|
||||||
|
- (instancetype)initWithReachabilityHost:(NSString *)reachabilityHost;
|
||||||
|
|
||||||
|
/// Handles events when background session with the given ID has finished.
|
||||||
|
+ (void)handleEventsForBackgroundURLSessionID:(NSString *)sessionID
|
||||||
|
completionHandler:(FIRNetworkSystemCompletionHandler)completionHandler;
|
||||||
|
|
||||||
|
/// Compresses and sends a POST request with the provided data to the URL. The session will be
|
||||||
|
/// background session if usingBackgroundSession is YES. Otherwise, the POST session is default
|
||||||
|
/// session. Returns a session ID or nil if an error occurs.
|
||||||
|
- (NSString *)postURL:(NSURL *)url
|
||||||
|
payload:(NSData *)payload
|
||||||
|
queue:(dispatch_queue_t)queue
|
||||||
|
usingBackgroundSession:(BOOL)usingBackgroundSession
|
||||||
|
completionHandler:(FIRNetworkCompletionHandler)handler;
|
||||||
|
|
||||||
|
/// Sends a GET request with the provided data to the URL. The session will be background session
|
||||||
|
/// if usingBackgroundSession is YES. Otherwise, the GET session is default session. Returns a
|
||||||
|
/// session ID or nil if an error occurs.
|
||||||
|
- (NSString *)getURL:(NSURL *)url
|
||||||
|
headers:(NSDictionary *)headers
|
||||||
|
queue:(dispatch_queue_t)queue
|
||||||
|
usingBackgroundSession:(BOOL)usingBackgroundSession
|
||||||
|
completionHandler:(FIRNetworkCompletionHandler)handler;
|
||||||
|
|
||||||
|
@end
|
75
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIRNetworkConstants.h
generated
Normal file
75
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIRNetworkConstants.h
generated
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
/// Error codes in Firebase Network error domain.
|
||||||
|
/// Note: these error codes should never change. It would make it harder to decode the errors if
|
||||||
|
/// we inadvertently altered any of these codes in a future SDK version.
|
||||||
|
typedef NS_ENUM(NSInteger, FIRNetworkErrorCode) {
|
||||||
|
/// Unknown error.
|
||||||
|
FIRNetworkErrorCodeUnknown = 0,
|
||||||
|
/// Error occurs when the request URL is invalid.
|
||||||
|
FIRErrorCodeNetworkInvalidURL = 1,
|
||||||
|
/// Error occurs when request cannot be constructed.
|
||||||
|
FIRErrorCodeNetworkRequestCreation = 2,
|
||||||
|
/// Error occurs when payload cannot be compressed.
|
||||||
|
FIRErrorCodeNetworkPayloadCompression = 3,
|
||||||
|
/// Error occurs when session task cannot be created.
|
||||||
|
FIRErrorCodeNetworkSessionTaskCreation = 4,
|
||||||
|
/// Error occurs when there is no response.
|
||||||
|
FIRErrorCodeNetworkInvalidResponse = 5
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma mark - Network constants
|
||||||
|
|
||||||
|
/// The prefix of the ID of the background session.
|
||||||
|
extern NSString *const kFIRNetworkBackgroundSessionConfigIDPrefix;
|
||||||
|
|
||||||
|
/// The sub directory to store the files of data that is being uploaded in the background.
|
||||||
|
extern NSString *const kFIRNetworkApplicationSupportSubdirectory;
|
||||||
|
|
||||||
|
/// Name of the temporary directory that stores files for background uploading.
|
||||||
|
extern NSString *const kFIRNetworkTempDirectoryName;
|
||||||
|
|
||||||
|
/// The period when the temporary uploading file can stay.
|
||||||
|
extern const NSTimeInterval kFIRNetworkTempFolderExpireTime;
|
||||||
|
|
||||||
|
/// The default network request timeout interval.
|
||||||
|
extern const NSTimeInterval kFIRNetworkTimeOutInterval;
|
||||||
|
|
||||||
|
/// The host to check the reachability of the network.
|
||||||
|
extern NSString *const kFIRNetworkReachabilityHost;
|
||||||
|
|
||||||
|
/// The key to get the error context of the UserInfo.
|
||||||
|
extern NSString *const kFIRNetworkErrorContext;
|
||||||
|
|
||||||
|
#pragma mark - Network Status Code
|
||||||
|
|
||||||
|
extern const int kFIRNetworkHTTPStatusOK;
|
||||||
|
extern const int kFIRNetworkHTTPStatusNoContent;
|
||||||
|
extern const int kFIRNetworkHTTPStatusCodeMultipleChoices;
|
||||||
|
extern const int kFIRNetworkHTTPStatusCodeMovedPermanently;
|
||||||
|
extern const int kFIRNetworkHTTPStatusCodeFound;
|
||||||
|
extern const int kFIRNetworkHTTPStatusCodeNotModified;
|
||||||
|
extern const int kFIRNetworkHTTPStatusCodeMovedTemporarily;
|
||||||
|
extern const int kFIRNetworkHTTPStatusCodeNotFound;
|
||||||
|
extern const int kFIRNetworkHTTPStatusCodeCannotAcceptTraffic;
|
||||||
|
extern const int kFIRNetworkHTTPStatusCodeUnavailable;
|
||||||
|
|
||||||
|
#pragma mark - Error Domain
|
||||||
|
|
||||||
|
extern NSString *const kFIRNetworkErrorDomain;
|
50
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIRNetworkLoggerProtocol.h
generated
Normal file
50
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIRNetworkLoggerProtocol.h
generated
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import "FIRLoggerLevel.h"
|
||||||
|
#import "FIRNetworkMessageCode.h"
|
||||||
|
|
||||||
|
/// The log levels used by FIRNetworkLogger.
|
||||||
|
typedef NS_ENUM(NSInteger, FIRNetworkLogLevel) {
|
||||||
|
kFIRNetworkLogLevelError = FIRLoggerLevelError,
|
||||||
|
kFIRNetworkLogLevelWarning = FIRLoggerLevelWarning,
|
||||||
|
kFIRNetworkLogLevelInfo = FIRLoggerLevelInfo,
|
||||||
|
kFIRNetworkLogLevelDebug = FIRLoggerLevelDebug,
|
||||||
|
};
|
||||||
|
|
||||||
|
@protocol FIRNetworkLoggerDelegate <NSObject>
|
||||||
|
|
||||||
|
@required
|
||||||
|
/// Tells the delegate to log a message with an array of contexts and the log level.
|
||||||
|
- (void)firNetwork_logWithLevel:(FIRNetworkLogLevel)logLevel
|
||||||
|
messageCode:(FIRNetworkMessageCode)messageCode
|
||||||
|
message:(NSString *)message
|
||||||
|
contexts:(NSArray *)contexts;
|
||||||
|
|
||||||
|
/// Tells the delegate to log a message with a context and the log level.
|
||||||
|
- (void)firNetwork_logWithLevel:(FIRNetworkLogLevel)logLevel
|
||||||
|
messageCode:(FIRNetworkMessageCode)messageCode
|
||||||
|
message:(NSString *)message
|
||||||
|
context:(id)context;
|
||||||
|
|
||||||
|
/// Tells the delegate to log a message with the log level.
|
||||||
|
- (void)firNetwork_logWithLevel:(FIRNetworkLogLevel)logLevel
|
||||||
|
messageCode:(FIRNetworkMessageCode)messageCode
|
||||||
|
message:(NSString *)message;
|
||||||
|
|
||||||
|
@end
|
52
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIRNetworkMessageCode.h
generated
Normal file
52
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIRNetworkMessageCode.h
generated
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Make sure these codes do not overlap with any contained in the FIRAMessageCode enum.
|
||||||
|
typedef NS_ENUM(NSInteger, FIRNetworkMessageCode) {
|
||||||
|
// FIRNetwork.m
|
||||||
|
kFIRNetworkMessageCodeNetwork000 = 900000, // I-NET900000
|
||||||
|
kFIRNetworkMessageCodeNetwork001 = 900001, // I-NET900001
|
||||||
|
kFIRNetworkMessageCodeNetwork002 = 900002, // I-NET900002
|
||||||
|
kFIRNetworkMessageCodeNetwork003 = 900003, // I-NET900003
|
||||||
|
// FIRNetworkURLSession.m
|
||||||
|
kFIRNetworkMessageCodeURLSession000 = 901000, // I-NET901000
|
||||||
|
kFIRNetworkMessageCodeURLSession001 = 901001, // I-NET901001
|
||||||
|
kFIRNetworkMessageCodeURLSession002 = 901002, // I-NET901002
|
||||||
|
kFIRNetworkMessageCodeURLSession003 = 901003, // I-NET901003
|
||||||
|
kFIRNetworkMessageCodeURLSession004 = 901004, // I-NET901004
|
||||||
|
kFIRNetworkMessageCodeURLSession005 = 901005, // I-NET901005
|
||||||
|
kFIRNetworkMessageCodeURLSession006 = 901006, // I-NET901006
|
||||||
|
kFIRNetworkMessageCodeURLSession007 = 901007, // I-NET901007
|
||||||
|
kFIRNetworkMessageCodeURLSession008 = 901008, // I-NET901008
|
||||||
|
kFIRNetworkMessageCodeURLSession009 = 901009, // I-NET901009
|
||||||
|
kFIRNetworkMessageCodeURLSession010 = 901010, // I-NET901010
|
||||||
|
kFIRNetworkMessageCodeURLSession011 = 901011, // I-NET901011
|
||||||
|
kFIRNetworkMessageCodeURLSession012 = 901012, // I-NET901012
|
||||||
|
kFIRNetworkMessageCodeURLSession013 = 901013, // I-NET901013
|
||||||
|
kFIRNetworkMessageCodeURLSession014 = 901014, // I-NET901014
|
||||||
|
kFIRNetworkMessageCodeURLSession015 = 901015, // I-NET901015
|
||||||
|
kFIRNetworkMessageCodeURLSession016 = 901016, // I-NET901016
|
||||||
|
kFIRNetworkMessageCodeURLSession017 = 901017, // I-NET901017
|
||||||
|
kFIRNetworkMessageCodeURLSession018 = 901018, // I-NET901018
|
||||||
|
// FIRReachabilityChecker.m
|
||||||
|
kFIRNetworkMessageCodeReachabilityChecker000 = 902000, // I-NET902000
|
||||||
|
kFIRNetworkMessageCodeReachabilityChecker001 = 902001, // I-NET902001
|
||||||
|
kFIRNetworkMessageCodeReachabilityChecker002 = 902002, // I-NET902002
|
||||||
|
kFIRNetworkMessageCodeReachabilityChecker003 = 902003, // I-NET902003
|
||||||
|
kFIRNetworkMessageCodeReachabilityChecker004 = 902004, // I-NET902004
|
||||||
|
kFIRNetworkMessageCodeReachabilityChecker005 = 902005, // I-NET902005
|
||||||
|
kFIRNetworkMessageCodeReachabilityChecker006 = 902006, // I-NET902006
|
||||||
|
};
|
60
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIRNetworkURLSession.h
generated
Normal file
60
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIRNetworkURLSession.h
generated
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import "FIRNetworkLoggerProtocol.h"
|
||||||
|
|
||||||
|
typedef void (^FIRNetworkCompletionHandler)(NSHTTPURLResponse *response,
|
||||||
|
NSData *data,
|
||||||
|
NSError *error);
|
||||||
|
typedef void (^FIRNetworkURLSessionCompletionHandler)(NSHTTPURLResponse *response,
|
||||||
|
NSData *data,
|
||||||
|
NSString *sessionID,
|
||||||
|
NSError *error);
|
||||||
|
typedef void (^FIRNetworkSystemCompletionHandler)(void);
|
||||||
|
|
||||||
|
/// The protocol that uses NSURLSession for iOS >= 7.0 to handle requests and responses.
|
||||||
|
@interface FIRNetworkURLSession
|
||||||
|
: NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDownloadDelegate>
|
||||||
|
|
||||||
|
/// Indicates whether the background network is enabled. Default value is NO.
|
||||||
|
@property(nonatomic, getter=isBackgroundNetworkEnabled) BOOL backgroundNetworkEnabled;
|
||||||
|
|
||||||
|
/// The logger delegate to log message, errors or warnings that occur during the network operations.
|
||||||
|
@property(nonatomic, weak) id<FIRNetworkLoggerDelegate> loggerDelegate;
|
||||||
|
|
||||||
|
/// Calls the system provided completion handler after the background session is finished.
|
||||||
|
+ (void)handleEventsForBackgroundURLSessionID:(NSString *)sessionID
|
||||||
|
completionHandler:(FIRNetworkSystemCompletionHandler)completionHandler;
|
||||||
|
|
||||||
|
/// Initializes with logger delegate.
|
||||||
|
- (instancetype)initWithNetworkLoggerDelegate:(id<FIRNetworkLoggerDelegate>)networkLoggerDelegate
|
||||||
|
NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
- (instancetype)init NS_UNAVAILABLE;
|
||||||
|
|
||||||
|
/// Sends an asynchronous POST request and calls the provided completion handler when the request
|
||||||
|
/// completes or when errors occur, and returns an ID of the session/connection.
|
||||||
|
- (NSString *)sessionIDFromAsyncPOSTRequest:(NSURLRequest *)request
|
||||||
|
completionHandler:(FIRNetworkURLSessionCompletionHandler)handler;
|
||||||
|
|
||||||
|
/// Sends an asynchronous GET request and calls the provided completion handler when the request
|
||||||
|
/// completes or when errors occur, and returns an ID of the session.
|
||||||
|
- (NSString *)sessionIDFromAsyncGETRequest:(NSURLRequest *)request
|
||||||
|
completionHandler:(FIRNetworkURLSessionCompletionHandler)handler;
|
||||||
|
|
||||||
|
@end
|
114
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIROptionsInternal.h
generated
Normal file
114
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIROptionsInternal.h
generated
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "FIROptions.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keys for the strings in the plist file.
|
||||||
|
*/
|
||||||
|
extern NSString *const kFIRAPIKey;
|
||||||
|
extern NSString *const kFIRTrackingID;
|
||||||
|
extern NSString *const kFIRGoogleAppID;
|
||||||
|
extern NSString *const kFIRClientID;
|
||||||
|
extern NSString *const kFIRGCMSenderID;
|
||||||
|
extern NSString *const kFIRAndroidClientID;
|
||||||
|
extern NSString *const kFIRDatabaseURL;
|
||||||
|
extern NSString *const kFIRStorageBucket;
|
||||||
|
extern NSString *const kFIRBundleID;
|
||||||
|
extern NSString *const kFIRProjectID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keys for the plist file name
|
||||||
|
*/
|
||||||
|
extern NSString *const kServiceInfoFileName;
|
||||||
|
|
||||||
|
extern NSString *const kServiceInfoFileType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This header file exposes the initialization of FIROptions to internal use.
|
||||||
|
*/
|
||||||
|
@interface FIROptions ()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* resetDefaultOptions and initInternalWithOptionsDictionary: are exposed only for unit tests.
|
||||||
|
*/
|
||||||
|
+ (void)resetDefaultOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the options with dictionary. The above strings are the keys of the dictionary.
|
||||||
|
* This is the designated initializer.
|
||||||
|
*/
|
||||||
|
- (instancetype)initInternalWithOptionsDictionary:(NSDictionary *)serviceInfoDictionary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* defaultOptions and defaultOptionsDictionary are exposed in order to be used in FIRApp and
|
||||||
|
* other first party services.
|
||||||
|
*/
|
||||||
|
+ (FIROptions *)defaultOptions;
|
||||||
|
|
||||||
|
+ (NSDictionary *)defaultOptionsDictionary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether or not Analytics collection was explicitly enabled via a plist flag or at
|
||||||
|
* runtime.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) BOOL isAnalyticsCollectionExpicitlySet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not Analytics Collection was enabled. Analytics Collection is enabled unless
|
||||||
|
* explicitly disabled in GoogleService-Info.plist.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) BOOL isAnalyticsCollectionEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not Analytics Collection was completely disabled. If YES, then
|
||||||
|
* isAnalyticsCollectionEnabled will be NO.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) BOOL isAnalyticsCollectionDeactivated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The version ID of the client library, e.g. @"1100000".
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly, copy) NSString *libraryVersionID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The flag indicating whether this object was constructed with the values in the default plist
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
@property(nonatomic) BOOL usingOptionsFromDefaultPlist;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not Measurement was enabled. Measurement is enabled unless explicitly disabled in
|
||||||
|
* GoogleService-Info.plist.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) BOOL isMeasurementEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not Analytics was enabled in the developer console.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) BOOL isAnalyticsEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not SignIn was enabled in the developer console.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) BOOL isSignInEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not editing is locked. This should occur after FIROptions has been set on a FIRApp.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, getter=isEditingLocked) BOOL editingLocked;
|
||||||
|
|
||||||
|
@end
|
47
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIRReachabilityChecker+Internal.h
generated
Normal file
47
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIRReachabilityChecker+Internal.h
generated
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "FIRReachabilityChecker.h"
|
||||||
|
|
||||||
|
typedef SCNetworkReachabilityRef (*FIRReachabilityCreateWithNameFn)(CFAllocatorRef allocator,
|
||||||
|
const char *host);
|
||||||
|
|
||||||
|
typedef Boolean (*FIRReachabilitySetCallbackFn)(SCNetworkReachabilityRef target,
|
||||||
|
SCNetworkReachabilityCallBack callback,
|
||||||
|
SCNetworkReachabilityContext *context);
|
||||||
|
typedef Boolean (*FIRReachabilityScheduleWithRunLoopFn)(SCNetworkReachabilityRef target,
|
||||||
|
CFRunLoopRef runLoop,
|
||||||
|
CFStringRef runLoopMode);
|
||||||
|
typedef Boolean (*FIRReachabilityUnscheduleFromRunLoopFn)(SCNetworkReachabilityRef target,
|
||||||
|
CFRunLoopRef runLoop,
|
||||||
|
CFStringRef runLoopMode);
|
||||||
|
|
||||||
|
typedef void (*FIRReachabilityReleaseFn)(CFTypeRef cf);
|
||||||
|
|
||||||
|
struct FIRReachabilityApi {
|
||||||
|
FIRReachabilityCreateWithNameFn createWithNameFn;
|
||||||
|
FIRReachabilitySetCallbackFn setCallbackFn;
|
||||||
|
FIRReachabilityScheduleWithRunLoopFn scheduleWithRunLoopFn;
|
||||||
|
FIRReachabilityUnscheduleFromRunLoopFn unscheduleFromRunLoopFn;
|
||||||
|
FIRReachabilityReleaseFn releaseFn;
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface FIRReachabilityChecker (Internal)
|
||||||
|
|
||||||
|
- (const struct FIRReachabilityApi *)reachabilityApi;
|
||||||
|
- (void)setReachabilityApi:(const struct FIRReachabilityApi *)reachabilityApi;
|
||||||
|
|
||||||
|
@end
|
83
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIRReachabilityChecker.h
generated
Normal file
83
Verdnaturaventas/Pods/FirebaseCore/Firebase/Core/Private/FIRReachabilityChecker.h
generated
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <SystemConfiguration/SystemConfiguration.h>
|
||||||
|
|
||||||
|
/// Reachability Status
|
||||||
|
typedef enum {
|
||||||
|
kFIRReachabilityUnknown, ///< Have not yet checked or been notified whether host is reachable.
|
||||||
|
kFIRReachabilityNotReachable, ///< Host is not reachable.
|
||||||
|
kFIRReachabilityViaWifi, ///< Host is reachable via Wifi.
|
||||||
|
kFIRReachabilityViaCellular, ///< Host is reachable via cellular.
|
||||||
|
} FIRReachabilityStatus;
|
||||||
|
|
||||||
|
const NSString *FIRReachabilityStatusString(FIRReachabilityStatus status);
|
||||||
|
|
||||||
|
@class FIRReachabilityChecker;
|
||||||
|
@protocol FIRNetworkLoggerDelegate;
|
||||||
|
|
||||||
|
/// Google Analytics iOS Reachability Checker.
|
||||||
|
@protocol FIRReachabilityDelegate
|
||||||
|
@required
|
||||||
|
/// Called when network status has changed.
|
||||||
|
- (void)reachability:(FIRReachabilityChecker *)reachability
|
||||||
|
statusChanged:(FIRReachabilityStatus)status;
|
||||||
|
@end
|
||||||
|
|
||||||
|
/// Google Analytics iOS Network Status Checker.
|
||||||
|
@interface FIRReachabilityChecker : NSObject
|
||||||
|
|
||||||
|
/// The last known reachability status, or FIRReachabilityStatusUnknown if the
|
||||||
|
/// checker is not active.
|
||||||
|
@property(nonatomic, readonly) FIRReachabilityStatus reachabilityStatus;
|
||||||
|
/// The host to which reachability status is to be checked.
|
||||||
|
@property(nonatomic, copy, readonly) NSString *host;
|
||||||
|
/// The delegate to be notified of reachability status changes.
|
||||||
|
@property(nonatomic, weak) id<FIRReachabilityDelegate> reachabilityDelegate;
|
||||||
|
/// The delegate to be notified to log messages.
|
||||||
|
@property(nonatomic, weak) id<FIRNetworkLoggerDelegate> loggerDelegate;
|
||||||
|
/// `YES` if the reachability checker is active, `NO` otherwise.
|
||||||
|
@property(nonatomic, readonly) BOOL isActive;
|
||||||
|
|
||||||
|
/// Initialize the reachability checker. Note that you must call start to begin checking for and
|
||||||
|
/// receiving notifications about network status changes.
|
||||||
|
///
|
||||||
|
/// @param reachabilityDelegate The delegate to be notified when reachability status to host
|
||||||
|
/// changes.
|
||||||
|
///
|
||||||
|
/// @param loggerDelegate The delegate to send log messages to.
|
||||||
|
///
|
||||||
|
/// @param host The name of the host.
|
||||||
|
///
|
||||||
|
- (instancetype)initWithReachabilityDelegate:(id<FIRReachabilityDelegate>)reachabilityDelegate
|
||||||
|
loggerDelegate:(id<FIRNetworkLoggerDelegate>)loggerDelegate
|
||||||
|
withHost:(NSString *)host;
|
||||||
|
|
||||||
|
- (instancetype)init NS_UNAVAILABLE;
|
||||||
|
|
||||||
|
/// Start checking for reachability to the specified host. This has no effect if the status
|
||||||
|
/// checker is already checking for connectivity.
|
||||||
|
///
|
||||||
|
/// @return `YES` if initiating status checking was successful or the status checking has already
|
||||||
|
/// been initiated, `NO` otherwise.
|
||||||
|
- (BOOL)start;
|
||||||
|
|
||||||
|
/// Stop checking for reachability to the specified host. This has no effect if the status
|
||||||
|
/// checker is not checking for connectivity.
|
||||||
|
- (void)stop;
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017 Google
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
/** The version of the Firebase SDK. */
|
||||||
|
FOUNDATION_EXPORT const unsigned char *const FIRVersionString;
|
||||||
|
|
||||||
|
/** The version of the FirebaseCore Component. */
|
||||||
|
FOUNDATION_EXPORT const unsigned char *const FIRCoreVersionString;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue