Versión estable 2018
This commit is contained in:
parent
e2649847ef
commit
459227bf4d
|
@ -7,7 +7,7 @@
|
|||
<key>BaseLibrary.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
<integer>6</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
|
|
|
@ -2,5 +2,8 @@ use_frameworks!
|
|||
|
||||
platform :ios, '8.0'
|
||||
pod 'FSCalendar'
|
||||
target "Verdnaturaventas" do
|
||||
pod 'Firebase/Core'
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +1,51 @@
|
|||
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:
|
||||
- Firebase/Core
|
||||
- FSCalendar
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
FSCalendar: 18117308f5876a8f6f0fe572085d543580c2c5e5
|
||||
SPEC REPOS:
|
||||
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
|
||||
//
|
||||
// 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 "FSCalendarAppearance.h"
|
||||
#import "FSCalendarConstance.h"
|
||||
|
||||
/**
|
||||
* FSCalendar is a superior awesome calendar control with high performance, high customizablility and very simple usage.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
#import "FSCalendarConstants.h"
|
||||
#import "FSCalendarCell.h"
|
||||
#import "FSCalendarWeekdayView.h"
|
||||
#import "FSCalendarHeaderView.h"
|
||||
|
||||
//! Project version number for FSCalendar.
|
||||
FOUNDATION_EXPORT double FSCalendarVersionNumber;
|
||||
|
@ -40,10 +38,18 @@ typedef NS_ENUM(NSUInteger, FSCalendarScrollDirection) {
|
|||
FSCalendarScrollDirectionHorizontal
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, FSCalendarUnit) {
|
||||
FSCalendarUnitMonth = NSCalendarUnitMonth,
|
||||
FSCalendarUnitWeekOfYear = NSCalendarUnitWeekOfYear,
|
||||
FSCalendarUnitDay = NSCalendarUnitDay
|
||||
typedef NS_ENUM(NSUInteger, FSCalendarPlaceholderType) {
|
||||
FSCalendarPlaceholderTypeNone = 0,
|
||||
FSCalendarPlaceholderTypeFillHeadTail = 1,
|
||||
FSCalendarPlaceholderTypeFillSixRows = 2
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, FSCalendarMonthPosition) {
|
||||
FSCalendarMonthPositionPrevious,
|
||||
FSCalendarMonthPositionCurrent,
|
||||
FSCalendarMonthPositionNext,
|
||||
|
||||
FSCalendarMonthPositionNotFound = NSNotFound
|
||||
};
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
@ -51,12 +57,17 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@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>
|
||||
|
||||
@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.
|
||||
*/
|
||||
|
@ -77,11 +88,15 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
*/
|
||||
- (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.
|
||||
*
|
||||
* @see
|
||||
*
|
||||
* - (UIColor *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance eventColorForDate:(NSDate *)date;
|
||||
* - (NSArray *)calendar:(FSCalendar *)calendar appearance:(FSCalendarAppearance *)appearance eventColorsForDate:(NSDate *)date;
|
||||
*/
|
||||
|
@ -103,56 +118,70 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* These functions are deprecated
|
||||
These functions are deprecated
|
||||
*/
|
||||
- (void)calendarCurrentScopeWillChange:(FSCalendar *)calendar animated:(BOOL)animated FSCalendarDeprecated(-calendar:boundingRectWillChange:animated:);
|
||||
- (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
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@protocol FSCalendarDelegateAppearance <NSObject>
|
||||
@protocol FSCalendarDelegateAppearance <FSCalendarDelegate>
|
||||
|
||||
@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.
|
||||
*/
|
||||
- (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.
|
||||
|
@ -175,14 +204,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
- (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.
|
||||
|
@ -195,15 +224,39 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
- (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
|
||||
|
||||
#pragma mark - Primary
|
||||
|
@ -222,9 +275,9 @@ IB_DESIGNABLE
|
|||
@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
|
||||
|
@ -240,16 +293,7 @@ IB_DESIGNABLE
|
|||
*
|
||||
* calendar.locale = [NSLocale localeWithLocaleIdentifier:@"zh-CN"];
|
||||
*/
|
||||
@property (strong, 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;
|
||||
@property (copy, nonatomic) NSLocale *locale;
|
||||
|
||||
/**
|
||||
* The scroll direction of FSCalendar.
|
||||
|
@ -268,330 +312,319 @@ IB_DESIGNABLE
|
|||
@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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* The height of weekday header of the calendar.
|
||||
The height of weekday header of the calendar.
|
||||
*/
|
||||
@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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Change the scope of the calendar. Make sure `-calendar:boundingRectWillChange:animated` is correctly adopted.
|
||||
*
|
||||
* @param scope The target scope to change.
|
||||
* @param animated YES if you want to animate the scoping; NO if the change should be immediate.
|
||||
Change the scope of the calendar. Make sure `-calendar:boundingRectWillChange:animated` is correctly adopted.
|
||||
|
||||
@param scope The target scope to change.
|
||||
@param animated YES if you want to animate the scoping; NO if the change should be immediate.
|
||||
*/
|
||||
- (void)setScope:(FSCalendarScope)scope animated:(BOOL)animated;
|
||||
|
||||
/**
|
||||
* Selects a given date in the calendar.
|
||||
*
|
||||
* @param date A date in the calendar.
|
||||
Selects a given 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.
|
||||
*
|
||||
* @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.
|
||||
Selects a given date in the calendar, optionally scrolling the date to visible area.
|
||||
|
||||
@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.
|
||||
*/
|
||||
- (void)selectDate:(NSDate *)date scrollToDate:(BOOL)scrollToDate;
|
||||
- (void)selectDate:(nullable NSDate *)date scrollToDate:(BOOL)scrollToDate;
|
||||
|
||||
/**
|
||||
* Deselects a given date of the calendar.
|
||||
* @param date A date in the calendar.
|
||||
Deselects a given date of the calendar.
|
||||
|
||||
@param date A date in the calendar.
|
||||
*/
|
||||
- (void)deselectDate:(NSDate *)date;
|
||||
|
||||
/**
|
||||
* Change the current page of the calendar.
|
||||
*
|
||||
* @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.
|
||||
Changes the current page of the calendar.
|
||||
|
||||
@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.
|
||||
*/
|
||||
- (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
|
||||
|
||||
#pragma mark - DateTools
|
||||
|
||||
/**
|
||||
* Job for this category:
|
||||
*
|
||||
* 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)
|
||||
IB_DESIGNABLE
|
||||
@interface FSCalendar (IBExtension)
|
||||
|
||||
/**
|
||||
* Returns the number of year of the given date
|
||||
*/
|
||||
- (NSInteger)yearOfDate:(NSDate *)date;
|
||||
#if TARGET_INTERFACE_BUILDER
|
||||
|
||||
/**
|
||||
* Returns the number of month of the given date
|
||||
*/
|
||||
- (NSInteger)monthOfDate:(NSDate *)date;
|
||||
@property (assign, nonatomic) IBInspectable CGFloat titleTextSize;
|
||||
@property (assign, nonatomic) IBInspectable CGFloat subtitleTextSize;
|
||||
@property (assign, nonatomic) IBInspectable CGFloat weekdayTextSize;
|
||||
@property (assign, nonatomic) IBInspectable CGFloat headerTitleTextSize;
|
||||
|
||||
/**
|
||||
* Returns the number of day of the given date
|
||||
*/
|
||||
- (NSInteger)dayOfDate:(NSDate *)date;
|
||||
@property (strong, nonatomic) IBInspectable UIColor *eventDefaultColor;
|
||||
@property (strong, nonatomic) IBInspectable UIColor *eventSelectionColor;
|
||||
@property (strong, nonatomic) IBInspectable UIColor *weekdayTextColor;
|
||||
|
||||
/**
|
||||
* Returns the number of weekday of the given date
|
||||
*/
|
||||
- (NSInteger)weekdayOfDate:(NSDate *)date;
|
||||
@property (strong, nonatomic) IBInspectable UIColor *headerTitleColor;
|
||||
@property (strong, nonatomic) IBInspectable NSString *headerDateFormat;
|
||||
@property (assign, nonatomic) IBInspectable CGFloat headerMinimumDissolvedAlpha;
|
||||
|
||||
/**
|
||||
* Returns the number of weekOfYear of the given date
|
||||
*/
|
||||
- (NSInteger)weekOfDate:(NSDate *)date;
|
||||
@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;
|
||||
|
||||
/**
|
||||
* Returns the number of hour of the given date
|
||||
*/
|
||||
- (NSInteger)hourOfDate:(NSDate *)date;
|
||||
@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;
|
||||
|
||||
/**
|
||||
* Returns the number of minite of the given date
|
||||
*/
|
||||
- (NSInteger)miniuteOfDate:(NSDate *)date;
|
||||
@property (strong, nonatomic) IBInspectable UIColor *selectionColor;
|
||||
@property (strong, nonatomic) IBInspectable UIColor *todayColor;
|
||||
@property (strong, nonatomic) IBInspectable UIColor *todaySelectionColor;
|
||||
|
||||
/**
|
||||
* Returns the number of seconds of the given date
|
||||
*/
|
||||
- (NSInteger)secondOfDate:(NSDate *)date;
|
||||
@property (strong, nonatomic) IBInspectable UIColor *borderDefaultColor;
|
||||
@property (strong, nonatomic) IBInspectable UIColor *borderSelectionColor;
|
||||
|
||||
/**
|
||||
* Returns the number of rows of the given month
|
||||
*/
|
||||
- (NSInteger)numberOfRowsInMonth:(NSDate *)month;
|
||||
@property (assign, nonatomic) IBInspectable CGFloat borderRadius;
|
||||
@property (assign, nonatomic) IBInspectable BOOL useVeryShortWeekdaySymbols;
|
||||
|
||||
/**
|
||||
* Zeronizing hour、minute and second components of the given date
|
||||
*/
|
||||
- (NSDate *)dateByIgnoringTimeComponentsOfDate:(NSDate *)date;
|
||||
@property (assign, nonatomic) IBInspectable BOOL fakeSubtitles;
|
||||
@property (assign, nonatomic) IBInspectable BOOL fakeEventDots;
|
||||
@property (assign, nonatomic) IBInspectable NSInteger fakedSelectedDay;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark - Deprecate
|
||||
|
||||
/**
|
||||
* These attributes and functions are deprecated.
|
||||
*/
|
||||
@interface FSCalendar (Deprecated)
|
||||
@property (strong, nonatomic) NSDate *currentMonth FSCalendarDeprecated('currentPage');
|
||||
@property (assign, nonatomic) FSCalendarFlow flow FSCalendarDeprecated('scrollDirection');
|
||||
- (void)setSelectedDate:(NSDate *)selectedDate FSCalendarDeprecated(-selectDate:);
|
||||
- (void)setSelectedDate:(NSDate *)selectedDate animate:(BOOL)animate FSCalendarDeprecated(-selectDate:scrollToDate:);
|
||||
- (BOOL)date:(NSDate *)date sharesSameMonthWithDate:(NSDate *)anotherDate FSCalendarDeprecated(-isDate:equalToDate:toCalendarUnit);
|
||||
- (BOOL)date:(NSDate *)date sharesSameWeekWithDate:(NSDate *)anotherDate FSCalendarDeprecated(-isDate:equalToDate:toCalendarUnit);
|
||||
- (BOOL)date:(NSDate *)date sharesSameDayWithDate:(NSDate *)anotherDate FSCalendarDeprecated(-isDate:equalToDate:toCalendarUnit);
|
||||
@property (assign, nonatomic) CGFloat lineHeightMultiplier FSCalendarDeprecated(rowHeight);
|
||||
@property (assign, nonatomic) IBInspectable BOOL showsPlaceholders FSCalendarDeprecated('placeholderType');
|
||||
@property (strong, nonatomic) NSString *identifier DEPRECATED_MSG_ATTRIBUTE("Changing calendar identifier is NOT RECOMMENDED. ");
|
||||
|
||||
// Use NSCalendar.
|
||||
- (NSDate *)dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day FSCalendarDeprecated([NSDateFormatter dateFromString:]);
|
||||
- (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
|
||||
|
||||
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
|
||||
//
|
||||
|
||||
#import "FSCalendarConstance.h"
|
||||
#import "FSCalendarConstants.h"
|
||||
|
||||
@class FSCalendar;
|
||||
|
||||
|
@ -22,9 +22,9 @@ typedef NS_ENUM(NSInteger, FSCalendarCellState) {
|
|||
FSCalendarCellStateTodaySelected = FSCalendarCellStateToday|FSCalendarCellStateSelected
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, FSCalendarCellShape) {
|
||||
FSCalendarCellShapeCircle = 0,
|
||||
FSCalendarCellShapeRectangle = 1
|
||||
typedef NS_ENUM(NSUInteger, FSCalendarSeparators) {
|
||||
FSCalendarSeparatorNone = 0,
|
||||
FSCalendarSeparatorInterRows = 1
|
||||
};
|
||||
|
||||
typedef NS_OPTIONS(NSUInteger, FSCalendarCaseOptions) {
|
||||
|
@ -45,46 +45,53 @@ typedef NS_OPTIONS(NSUInteger, FSCalendarCaseOptions) {
|
|||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@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.
|
||||
|
@ -182,11 +189,9 @@ typedef NS_OPTIONS(NSUInteger, FSCalendarCaseOptions) {
|
|||
@property (strong, nonatomic) UIColor *borderSelectionColor;
|
||||
|
||||
/**
|
||||
* The shape appears when a day is selected or today.
|
||||
*
|
||||
* @see FSCalendarCellShape
|
||||
* The border radius, while 1 means a circle, 0 means a rectangle, and the middle value will give it a corner radius.
|
||||
*/
|
||||
@property (assign, nonatomic) FSCalendarCellShape cellShape;
|
||||
@property (assign, nonatomic) CGFloat borderRadius;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
// For preview only
|
||||
@property (assign, nonatomic) BOOL fakeSubtitles;
|
||||
@property (assign, nonatomic) BOOL fakeEventDots;
|
||||
@property (assign, nonatomic) NSInteger fakedSelectedDay;
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Triggers an appearance update.
|
||||
*/
|
||||
- (void)invalidateAppearance;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
|
@ -225,15 +222,13 @@ typedef NS_OPTIONS(NSUInteger, FSCalendarCaseOptions) {
|
|||
*/
|
||||
@interface FSCalendarAppearance (Deprecated)
|
||||
|
||||
@property (assign, nonatomic) FSCalendarCellStyle cellStyle FSCalendarDeprecated('cellShape');
|
||||
@property (assign, nonatomic) BOOL useVeryShortWeekdaySymbols FSCalendarDeprecated('caseOptions');
|
||||
@property (assign, nonatomic) BOOL autoAdjustTitleSize FSCalendarDeprecated('adjustFontSizeToFitContentSize');
|
||||
@property (assign, nonatomic) BOOL adjustsFontSizeToFitCellSize FSCalendarDeprecated('adjustFontSizeToFitContentSize');
|
||||
|
||||
@property (assign, nonatomic) CGFloat titleTextSize FSCalendarDeprecated('titleFont');
|
||||
@property (assign, nonatomic) CGFloat subtitleTextSize FSCalendarDeprecated('subtitleFont');
|
||||
@property (assign, nonatomic) CGFloat weekdayTextSize FSCalendarDeprecated('weekdayFont');
|
||||
@property (assign, nonatomic) CGFloat headerTitleTextSize FSCalendarDeprecated('headerTitleFont');
|
||||
@property (assign, nonatomic) CGFloat titleVerticalOffset FSCalendarDeprecated('titleOffset');
|
||||
@property (assign, nonatomic) CGFloat subtitleVerticalOffset FSCalendarDeprecated('subtitleOffset');
|
||||
@property (strong, nonatomic) UIColor *eventColor FSCalendarDeprecated('eventDefaultColor');
|
||||
@property (assign, nonatomic) FSCalendarCellShape cellShape FSCalendarDeprecated('borderRadius');
|
||||
@property (assign, nonatomic) BOOL adjustsFontSizeToFitContentSize DEPRECATED_MSG_ATTRIBUTE("The attribute \'adjustsFontSizeToFitContentSize\' is not neccesary anymore.");
|
||||
- (void)invalidateAppearance FSCalendarDeprecated('FSCalendar setNeedsConfigureAppearance');
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
#import "FSCalendarAppearance.h"
|
||||
#import "FSCalendarDynamicHeader.h"
|
||||
#import "UIView+FSExtension.h"
|
||||
#import "FSCalendarExtensions.h"
|
||||
|
||||
@interface FSCalendarAppearance ()
|
||||
|
||||
|
@ -21,44 +21,6 @@
|
|||
@property (strong, nonatomic) NSMutableDictionary *subtitleColors;
|
||||
@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
|
||||
|
||||
@implementation FSCalendarAppearance
|
||||
|
@ -68,17 +30,10 @@
|
|||
self = [super init];
|
||||
if (self) {
|
||||
|
||||
_adjustsFontSizeToFitContentSize = YES;
|
||||
|
||||
_titleFontSize = _preferredTitleFontSize = FSCalendarStandardTitleTextSize;
|
||||
_subtitleFontSize = _preferredSubtitleFontSize = FSCalendarStandardSubtitleTextSize;
|
||||
_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;
|
||||
_titleFont = [UIFont systemFontOfSize:FSCalendarStandardTitleTextSize];
|
||||
_subtitleFont = [UIFont systemFontOfSize:FSCalendarStandardSubtitleTextSize];
|
||||
_weekdayFont = [UIFont systemFontOfSize:FSCalendarStandardWeekdayTextSize];
|
||||
_headerTitleFont = [UIFont systemFontOfSize:FSCalendarStandardHeaderTextSize];
|
||||
|
||||
_headerTitleColor = FSCalendarStandardTitleTextColor;
|
||||
_headerDateFormat = @"MMMM yyyy";
|
||||
|
@ -110,113 +65,82 @@
|
|||
_borderColors[@(FSCalendarCellStateSelected)] = [UIColor clearColor];
|
||||
_borderColors[@(FSCalendarCellStateNormal)] = [UIColor clearColor];
|
||||
|
||||
_cellShape = FSCalendarCellShapeCircle;
|
||||
_eventColor = FSCalendarStandardEventDotColor;
|
||||
_borderRadius = 1.0;
|
||||
_eventDefaultColor = FSCalendarStandardEventDotColor;
|
||||
_eventSelectionColor = FSCalendarStandardEventDotColor;
|
||||
|
||||
_borderColors = [NSMutableDictionary dictionaryWithCapacity:2];
|
||||
|
||||
#if TARGET_INTERFACE_BUILDER
|
||||
_fakeEventDots = YES;
|
||||
#endif
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setTitleFont:(UIFont *)titleFont
|
||||
{
|
||||
BOOL needsInvalidating = NO;
|
||||
if (![_titleFontName isEqualToString:titleFont.fontName]) {
|
||||
_titleFontName = titleFont.fontName;
|
||||
needsInvalidating = YES;
|
||||
if (![_titleFont isEqual:titleFont]) {
|
||||
_titleFont = titleFont;
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
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
|
||||
{
|
||||
BOOL needsInvalidating = NO;
|
||||
if (![_subtitleFontName isEqualToString:subtitleFont.fontName]) {
|
||||
_subtitleFontName = subtitleFont.fontName;
|
||||
needsInvalidating = YES;
|
||||
if (![_subtitleFont isEqual:subtitleFont]) {
|
||||
_subtitleFont = subtitleFont;
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
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
|
||||
{
|
||||
BOOL needsInvalidating = NO;
|
||||
if (![_weekdayFontName isEqualToString:weekdayFont.fontName]) {
|
||||
_weekdayFontName = weekdayFont.fontName;
|
||||
needsInvalidating = YES;
|
||||
if (![_weekdayFont isEqual:weekdayFont]) {
|
||||
_weekdayFont = weekdayFont;
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
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
|
||||
{
|
||||
BOOL needsInvalidating = NO;
|
||||
if (![_headerTitleFontName isEqualToString:headerTitleFont.fontName]) {
|
||||
_headerTitleFontName = headerTitleFont.fontName;
|
||||
needsInvalidating = YES;
|
||||
}
|
||||
if (_headerTitleFontSize != headerTitleFont.pointSize) {
|
||||
_headerTitleFontSize = headerTitleFont.pointSize;
|
||||
needsInvalidating = YES;
|
||||
}
|
||||
if (needsInvalidating) {
|
||||
[self invalidateHeaderFont];
|
||||
if (![_headerTitleFont isEqual:headerTitleFont]) {
|
||||
_headerTitleFont = headerTitleFont;
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setTitleVerticalOffset:(CGFloat)titleVerticalOffset
|
||||
- (void)setTitleOffset:(CGPoint)titleOffset
|
||||
{
|
||||
if (_titleVerticalOffset != titleVerticalOffset) {
|
||||
_titleVerticalOffset = titleVerticalOffset;
|
||||
[_calendar.collectionView.visibleCells makeObjectsPerformSelector:@selector(setNeedsLayout)];
|
||||
if (!CGPointEqualToPoint(_titleOffset, titleOffset)) {
|
||||
_titleOffset = titleOffset;
|
||||
[_calendar.visibleCells makeObjectsPerformSelector:@selector(setNeedsLayout)];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setSubtitleVerticalOffset:(CGFloat)subtitleVerticalOffset
|
||||
- (void)setSubtitleOffset:(CGPoint)subtitleOffset
|
||||
{
|
||||
if (_subtitleVerticalOffset != subtitleVerticalOffset) {
|
||||
_subtitleVerticalOffset = subtitleVerticalOffset;
|
||||
[_calendar.collectionView.visibleCells makeObjectsPerformSelector:@selector(setNeedsLayout)];
|
||||
if (!CGPointEqualToPoint(_subtitleOffset, subtitleOffset)) {
|
||||
_subtitleOffset = subtitleOffset;
|
||||
[_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
|
||||
|
@ -226,7 +150,7 @@
|
|||
} else {
|
||||
[_titleColors removeObjectForKey:@(FSCalendarCellStateNormal)];
|
||||
}
|
||||
[self invalidateTitleTextColor];
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
|
||||
- (UIColor *)titleDefaultColor
|
||||
|
@ -241,7 +165,7 @@
|
|||
} else {
|
||||
[_titleColors removeObjectForKey:@(FSCalendarCellStateSelected)];
|
||||
}
|
||||
[self invalidateTitleTextColor];
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
|
||||
- (UIColor *)titleSelectionColor
|
||||
|
@ -256,7 +180,7 @@
|
|||
} else {
|
||||
[_titleColors removeObjectForKey:@(FSCalendarCellStateToday)];
|
||||
}
|
||||
[self invalidateTitleTextColor];
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
|
||||
- (UIColor *)titleTodayColor
|
||||
|
@ -271,7 +195,7 @@
|
|||
} else {
|
||||
[_titleColors removeObjectForKey:@(FSCalendarCellStatePlaceholder)];
|
||||
}
|
||||
[self invalidateTitleTextColor];
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
|
||||
- (UIColor *)titlePlaceholderColor
|
||||
|
@ -286,7 +210,7 @@
|
|||
} else {
|
||||
[_titleColors removeObjectForKey:@(FSCalendarCellStateWeekend)];
|
||||
}
|
||||
[self invalidateTitleTextColor];
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
|
||||
- (UIColor *)titleWeekendColor
|
||||
|
@ -301,7 +225,7 @@
|
|||
} else {
|
||||
[_subtitleColors removeObjectForKey:@(FSCalendarCellStateNormal)];
|
||||
}
|
||||
[self invalidateSubtitleTextColor];
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
|
||||
-(UIColor *)subtitleDefaultColor
|
||||
|
@ -316,7 +240,7 @@
|
|||
} else {
|
||||
[_subtitleColors removeObjectForKey:@(FSCalendarCellStateSelected)];
|
||||
}
|
||||
[self invalidateSubtitleTextColor];
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
|
||||
- (UIColor *)subtitleSelectionColor
|
||||
|
@ -331,7 +255,7 @@
|
|||
} else {
|
||||
[_subtitleColors removeObjectForKey:@(FSCalendarCellStateToday)];
|
||||
}
|
||||
[self invalidateSubtitleTextColor];
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
|
||||
- (UIColor *)subtitleTodayColor
|
||||
|
@ -346,7 +270,7 @@
|
|||
} else {
|
||||
[_subtitleColors removeObjectForKey:@(FSCalendarCellStatePlaceholder)];
|
||||
}
|
||||
[self invalidateSubtitleTextColor];
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
|
||||
- (UIColor *)subtitlePlaceholderColor
|
||||
|
@ -361,7 +285,7 @@
|
|||
} else {
|
||||
[_subtitleColors removeObjectForKey:@(FSCalendarCellStateWeekend)];
|
||||
}
|
||||
[self invalidateSubtitleTextColor];
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
|
||||
- (UIColor *)subtitleWeekendColor
|
||||
|
@ -376,7 +300,7 @@
|
|||
} else {
|
||||
[_backgroundColors removeObjectForKey:@(FSCalendarCellStateSelected)];
|
||||
}
|
||||
[self invalidateBackgroundColors];
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
|
||||
- (UIColor *)selectionColor
|
||||
|
@ -391,7 +315,7 @@
|
|||
} else {
|
||||
[_backgroundColors removeObjectForKey:@(FSCalendarCellStateToday)];
|
||||
}
|
||||
[self invalidateBackgroundColors];
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
|
||||
- (UIColor *)todayColor
|
||||
|
@ -406,7 +330,7 @@
|
|||
} else {
|
||||
[_backgroundColors removeObjectForKey:@(FSCalendarCellStateToday|FSCalendarCellStateSelected)];
|
||||
}
|
||||
[self invalidateBackgroundColors];
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
|
||||
- (UIColor *)todaySelectionColor
|
||||
|
@ -414,11 +338,11 @@
|
|||
return _backgroundColors[@(FSCalendarCellStateToday|FSCalendarCellStateSelected)];
|
||||
}
|
||||
|
||||
- (void)setEventColor:(UIColor *)eventColor
|
||||
- (void)setEventDefaultColor:(UIColor *)eventDefaultColor
|
||||
{
|
||||
if (![_eventColor isEqual:eventColor]) {
|
||||
_eventColor = eventColor;
|
||||
[self invalidateEventColors];
|
||||
if (![_eventDefaultColor isEqual:eventDefaultColor]) {
|
||||
_eventDefaultColor = eventDefaultColor;
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -429,7 +353,7 @@
|
|||
} else {
|
||||
[_borderColors removeObjectForKey:@(FSCalendarCellStateNormal)];
|
||||
}
|
||||
[self invalidateBorderColors];
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
|
||||
- (UIColor *)borderDefaultColor
|
||||
|
@ -444,7 +368,7 @@
|
|||
} else {
|
||||
[_borderColors removeObjectForKey:@(FSCalendarCellStateSelected)];
|
||||
}
|
||||
[self invalidateBorderColors];
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
|
||||
- (UIColor *)borderSelectionColor
|
||||
|
@ -452,11 +376,13 @@
|
|||
return _borderColors[@(FSCalendarCellStateSelected)];
|
||||
}
|
||||
|
||||
- (void)setCellShape:(FSCalendarCellShape)cellShape
|
||||
- (void)setBorderRadius:(CGFloat)borderRadius
|
||||
{
|
||||
if (_cellShape != cellShape) {
|
||||
_cellShape = cellShape;
|
||||
[self invalidateCellShapes];
|
||||
borderRadius = MAX(0.0, borderRadius);
|
||||
borderRadius = MIN(1.0, borderRadius);
|
||||
if (_borderRadius != borderRadius) {
|
||||
_borderRadius = borderRadius;
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -464,7 +390,7 @@
|
|||
{
|
||||
if (![_weekdayTextColor isEqual:weekdayTextColor]) {
|
||||
_weekdayTextColor = weekdayTextColor;
|
||||
[self invalidateWeekdayTextColor];
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -472,7 +398,7 @@
|
|||
{
|
||||
if (![_headerTitleColor isEqual:color]) {
|
||||
_headerTitleColor = color;
|
||||
[self invalidateHeaderTextColor];
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -480,8 +406,7 @@
|
|||
{
|
||||
if (_headerMinimumDissolvedAlpha != headerMinimumDissolvedAlpha) {
|
||||
_headerMinimumDissolvedAlpha = headerMinimumDissolvedAlpha;
|
||||
[_calendar.header.collectionView.visibleCells makeObjectsPerformSelector:@selector(setNeedsLayout)];
|
||||
[_calendar.visibleStickyHeaders makeObjectsPerformSelector:@selector(setNeedsLayout)];
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -489,169 +414,24 @@
|
|||
{
|
||||
if (![_headerDateFormat isEqual: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
|
||||
{
|
||||
if (_caseOptions != caseOptions) {
|
||||
_caseOptions = caseOptions;
|
||||
[_calendar invalidateWeekdaySymbols];
|
||||
[_calendar invalidateHeaders];
|
||||
[self.calendar configureAppearance];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)invalidateAppearance
|
||||
- (void)setSeparators:(FSCalendarSeparators)separators
|
||||
{
|
||||
[self invalidateFonts];
|
||||
[self invalidateTextColors];
|
||||
[self invalidateBorderColors];
|
||||
[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];
|
||||
if (_separators != separators) {
|
||||
_separators = separators;
|
||||
[_calendar.collectionView.collectionViewLayout invalidateLayout];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -659,16 +439,6 @@
|
|||
|
||||
@implementation FSCalendarAppearance (Deprecated)
|
||||
|
||||
- (void)setCellStyle:(FSCalendarCellStyle)cellStyle
|
||||
{
|
||||
self.cellShape = (FSCalendarCellShape)cellStyle;
|
||||
}
|
||||
|
||||
- (FSCalendarCellStyle)cellStyle
|
||||
{
|
||||
return (FSCalendarCellStyle)self.cellShape;
|
||||
}
|
||||
|
||||
- (void)setUseVeryShortWeekdaySymbols:(BOOL)useVeryShortWeekdaySymbols
|
||||
{
|
||||
_caseOptions &= 15;
|
||||
|
@ -680,65 +450,73 @@
|
|||
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
|
||||
{
|
||||
self.titleFont = [UIFont fontWithName:_titleFontName size:titleTextSize];
|
||||
}
|
||||
|
||||
- (CGFloat)titleTextSize
|
||||
{
|
||||
return _titleFontSize;
|
||||
self.titleFont = [UIFont fontWithName:self.titleFont.fontName size:titleTextSize];
|
||||
}
|
||||
|
||||
- (void)setSubtitleTextSize:(CGFloat)subtitleTextSize
|
||||
{
|
||||
self.subtitleFont = [UIFont fontWithName:_subtitleFontName size:subtitleTextSize];
|
||||
}
|
||||
|
||||
- (CGFloat)subtitleTextSize
|
||||
{
|
||||
return _subtitleFontSize;
|
||||
self.subtitleFont = [UIFont fontWithName:self.subtitleFont.fontName size:subtitleTextSize];
|
||||
}
|
||||
|
||||
- (void)setWeekdayTextSize:(CGFloat)weekdayTextSize
|
||||
{
|
||||
self.weekdayFont = [UIFont fontWithName:_weekdayFontName size:weekdayTextSize];
|
||||
}
|
||||
|
||||
- (CGFloat)weekdayTextSize
|
||||
{
|
||||
return _weekdayFontSize;
|
||||
self.weekdayFont = [UIFont fontWithName:self.weekdayFont.fontName size:weekdayTextSize];
|
||||
}
|
||||
|
||||
- (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
|
||||
{
|
||||
self.adjustsFontSizeToFitContentSize = adjustsFontSizeToFitCellSize;
|
||||
}
|
||||
|
||||
- (BOOL)adjustsFontSizeToFitCellSize
|
||||
{
|
||||
return self.adjustsFontSizeToFitContentSize;
|
||||
}
|
||||
- (void)setAdjustsFontSizeToFitContentSize:(BOOL)adjustsFontSizeToFitContentSize {}
|
||||
- (BOOL)adjustsFontSizeToFitContentSize { return YES; }
|
||||
|
||||
@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 "FSCalendar.h"
|
||||
#import "FSCalendarEventIndicator.h"
|
||||
|
||||
@class FSCalendar, FSCalendarAppearance, FSCalendarEventIndicator;
|
||||
|
||||
typedef NS_ENUM(NSUInteger, FSCalendarMonthPosition);
|
||||
|
||||
@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) 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) UIImage *image;
|
||||
@property (assign, nonatomic) FSCalendarMonthPosition monthPosition;
|
||||
|
||||
@property (assign, nonatomic) BOOL needsAdjustingViewFrame;
|
||||
@property (assign, nonatomic) NSInteger numberOfEvents;
|
||||
|
||||
@property (assign, nonatomic) BOOL dateIsPlaceholder;
|
||||
@property (assign, nonatomic) BOOL dateIsSelected;
|
||||
@property (assign, nonatomic) BOOL dateIsToday;
|
||||
@property (assign, nonatomic) BOOL weekend;
|
||||
|
||||
@property (readonly, nonatomic) BOOL weekend;
|
||||
|
||||
@property (strong, nonatomic) UIColor *preferredSelectionColor;
|
||||
@property (strong, nonatomic) UIColor *preferredFillDefaultColor;
|
||||
@property (strong, nonatomic) UIColor *preferredFillSelectionColor;
|
||||
@property (strong, nonatomic) UIColor *preferredTitleDefaultColor;
|
||||
@property (strong, nonatomic) UIColor *preferredTitleSelectionColor;
|
||||
@property (strong, nonatomic) UIColor *preferredSubtitleDefaultColor;
|
||||
@property (strong, nonatomic) UIColor *preferredSubtitleSelectionColor;
|
||||
@property (strong, nonatomic) UIColor *preferredBorderDefaultColor;
|
||||
@property (strong, nonatomic) UIColor *preferredBorderSelectionColor;
|
||||
@property (strong, nonatomic) id preferredEventColor;
|
||||
@property (assign, nonatomic) FSCalendarCellShape preferredCellShape;
|
||||
@property (assign, nonatomic) CGPoint preferredTitleOffset;
|
||||
@property (assign, nonatomic) CGPoint preferredSubtitleOffset;
|
||||
@property (assign, nonatomic) CGPoint preferredImageOffset;
|
||||
@property (assign, nonatomic) CGPoint preferredEventOffset;
|
||||
|
||||
- (void)invalidateTitleFont;
|
||||
- (void)invalidateSubtitleFont;
|
||||
- (void)invalidateTitleTextColor;
|
||||
- (void)invalidateSubtitleTextColor;
|
||||
@property (strong, nonatomic) NSArray<UIColor *> *preferredEventDefaultColors;
|
||||
@property (strong, nonatomic) NSArray<UIColor *> *preferredEventSelectionColors;
|
||||
@property (assign, nonatomic) CGFloat preferredBorderRadius;
|
||||
|
||||
- (void)invalidateBorderColors;
|
||||
- (void)invalidateBackgroundColors;
|
||||
- (void)invalidateEventColors;
|
||||
- (void)invalidateCellShapes;
|
||||
// Add subviews to self.contentView and set up constraints
|
||||
- (instancetype)initWithFrame:(CGRect)frame NS_REQUIRES_SUPER;
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_REQUIRES_SUPER;
|
||||
|
||||
- (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;
|
||||
- (void)performSelecting;
|
||||
|
||||
@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 "FSCalendar.h"
|
||||
#import "UIView+FSExtension.h"
|
||||
#import "FSCalendarExtensions.h"
|
||||
#import "FSCalendarDynamicHeader.h"
|
||||
#import "FSCalendarConstance.h"
|
||||
#import "FSCalendarConstants.h"
|
||||
|
||||
@interface FSCalendarCell ()
|
||||
|
||||
@property (readonly, nonatomic) UIColor *colorForBackgroundLayer;
|
||||
@property (readonly, nonatomic) UIColor *colorForCellFill;
|
||||
@property (readonly, nonatomic) UIColor *colorForTitleLabel;
|
||||
@property (readonly, nonatomic) UIColor *colorForSubtitleLabel;
|
||||
@property (readonly, nonatomic) UIColor *colorForCellBorder;
|
||||
@property (readonly, nonatomic) FSCalendarCellShape cellShape;
|
||||
@property (readonly, nonatomic) NSArray<UIColor *> *colorsForEvents;
|
||||
@property (readonly, nonatomic) CGFloat borderRadius;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -30,79 +31,137 @@
|
|||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
|
||||
_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;
|
||||
|
||||
[self commonInit];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setBounds:(CGRect)bounds
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
[super setBounds:bounds];
|
||||
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;
|
||||
_backgroundLayer.frame = CGRectMake((self.bounds.size.width-diameter)/2,
|
||||
(titleHeight-diameter)/2,
|
||||
diameter,
|
||||
diameter);
|
||||
_backgroundLayer.borderWidth = 1.0;
|
||||
_backgroundLayer.borderColor = [UIColor clearColor].CGColor;
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self) {
|
||||
[self commonInit];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)commonInit
|
||||
{
|
||||
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
|
||||
{
|
||||
[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
|
||||
{
|
||||
[super prepareForReuse];
|
||||
[CATransaction setDisableActions:YES];
|
||||
_backgroundLayer.hidden = YES;
|
||||
if (self.window) { // Avoid interrupt of navigation transition somehow
|
||||
[CATransaction setDisableActions:YES]; // Avoid blink of shape layer.
|
||||
}
|
||||
self.shapeLayer.opacity = 0;
|
||||
[self.contentView.layer removeAnimationForKey:@"opacity"];
|
||||
}
|
||||
|
||||
|
@ -110,7 +169,7 @@
|
|||
|
||||
- (void)performSelecting
|
||||
{
|
||||
_backgroundLayer.hidden = NO;
|
||||
_shapeLayer.opacity = 1;
|
||||
|
||||
#define kAnimationDuration FSCalendarDefaultBounceAnimationDuration
|
||||
|
||||
|
@ -126,8 +185,8 @@
|
|||
zoomIn.duration = kAnimationDuration/4;
|
||||
group.duration = kAnimationDuration;
|
||||
group.animations = @[zoomOut, zoomIn];
|
||||
[_backgroundLayer addAnimation:group forKey:@"bounce"];
|
||||
[self configureCell];
|
||||
[_shapeLayer addAnimation:group forKey:@"bounce"];
|
||||
[self configureAppearance];
|
||||
|
||||
#undef kAnimationDuration
|
||||
|
||||
|
@ -135,103 +194,72 @@
|
|||
|
||||
#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;
|
||||
if (![textColor isEqual:_titleLabel.textColor]) {
|
||||
_titleLabel.textColor = textColor;
|
||||
}
|
||||
UIFont *titleFont = self.calendar.appearance.titleFont;
|
||||
if (![titleFont isEqual:_titleLabel.font]) {
|
||||
_titleLabel.font = titleFont;
|
||||
}
|
||||
if (_subtitle) {
|
||||
textColor = self.colorForSubtitleLabel;
|
||||
if (![textColor isEqual:_subtitleLabel.textColor]) {
|
||||
_subtitleLabel.textColor = textColor;
|
||||
}
|
||||
titleFont = self.calendar.appearance.subtitleFont;
|
||||
if (![titleFont isEqual:_subtitleLabel.font]) {
|
||||
_subtitleLabel.font = titleFont;
|
||||
}
|
||||
}
|
||||
|
||||
UIColor *borderColor = self.colorForCellBorder;
|
||||
BOOL shouldHiddenBackgroundLayer = !self.selected && !self.dateIsToday && !self.dateIsSelected && !borderColor;
|
||||
UIColor *fillColor = self.colorForCellFill;
|
||||
|
||||
if (_backgroundLayer.hidden != shouldHiddenBackgroundLayer) {
|
||||
_backgroundLayer.hidden = shouldHiddenBackgroundLayer;
|
||||
BOOL shouldHideShapeLayer = !self.selected && !self.dateIsToday && !borderColor && !fillColor;
|
||||
|
||||
if (_shapeLayer.opacity == shouldHideShapeLayer) {
|
||||
_shapeLayer.opacity = !shouldHideShapeLayer;
|
||||
}
|
||||
if (!shouldHiddenBackgroundLayer) {
|
||||
if (!shouldHideShapeLayer) {
|
||||
|
||||
CGPathRef path = self.cellShape == FSCalendarCellShapeCircle ?
|
||||
[UIBezierPath bezierPathWithOvalInRect:_backgroundLayer.bounds].CGPath :
|
||||
[UIBezierPath bezierPathWithRect:_backgroundLayer.bounds].CGPath;
|
||||
if (!CGPathEqualToPath(_backgroundLayer.path,path)) {
|
||||
_backgroundLayer.path = path;
|
||||
CGColorRef cellFillColor = self.colorForCellFill.CGColor;
|
||||
if (!CGColorEqualToColor(_shapeLayer.fillColor, cellFillColor)) {
|
||||
_shapeLayer.fillColor = cellFillColor;
|
||||
}
|
||||
|
||||
CGColorRef backgroundColor = self.colorForBackgroundLayer.CGColor;
|
||||
if (!CGColorEqualToColor(_backgroundLayer.fillColor, backgroundColor)) {
|
||||
_backgroundLayer.fillColor = backgroundColor;
|
||||
CGColorRef cellBorderColor = self.colorForCellBorder.CGColor;
|
||||
if (!CGColorEqualToColor(_shapeLayer.strokeColor, cellBorderColor)) {
|
||||
_shapeLayer.strokeColor = cellBorderColor;
|
||||
}
|
||||
|
||||
CGColorRef borderColor = self.colorForCellBorder.CGColor;
|
||||
if (!CGColorEqualToColor(_backgroundLayer.strokeColor, borderColor)) {
|
||||
_backgroundLayer.strokeColor = borderColor;
|
||||
CGPathRef path = [UIBezierPath bezierPathWithRoundedRect:_shapeLayer.bounds
|
||||
cornerRadius:CGRectGetWidth(_shapeLayer.bounds)*0.5*self.borderRadius].CGPath;
|
||||
if (!CGPathEqualToPath(_shapeLayer.path, path)) {
|
||||
_shapeLayer.path = path;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (![_image isEqual:_imageView.image]) {
|
||||
[self invalidateImage];
|
||||
_imageView.image = _image;
|
||||
_imageView.hidden = !_image;
|
||||
}
|
||||
|
||||
if (_eventIndicator.hidden == (_numberOfEvents > 0)) {
|
||||
_eventIndicator.hidden = !_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
|
||||
{
|
||||
if (self.isSelected || self.dateIsSelected) {
|
||||
if (self.isSelected) {
|
||||
if (self.dateIsToday) {
|
||||
return dictionary[@(FSCalendarCellStateSelected|FSCalendarCellStateToday)] ?: dictionary[@(FSCalendarCellStateSelected)];
|
||||
}
|
||||
|
@ -240,77 +268,28 @@
|
|||
if (self.dateIsToday && [[dictionary allKeys] containsObject:@(FSCalendarCellStateToday)]) {
|
||||
return dictionary[@(FSCalendarCellStateToday)];
|
||||
}
|
||||
if (self.dateIsPlaceholder && [[dictionary allKeys] containsObject:@(FSCalendarCellStatePlaceholder)]) {
|
||||
if (self.placeholder && [[dictionary allKeys] containsObject:@(FSCalendarCellStatePlaceholder)]) {
|
||||
return dictionary[@(FSCalendarCellStatePlaceholder)];
|
||||
}
|
||||
if (self.isWeekend && [[dictionary allKeys] containsObject:@(FSCalendarCellStateWeekend)]) {
|
||||
if (self.weekend && [[dictionary allKeys] containsObject:@(FSCalendarCellStateWeekend)]) {
|
||||
return dictionary[@(FSCalendarCellStateWeekend)];
|
||||
}
|
||||
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
|
||||
|
||||
- (UIColor *)colorForBackgroundLayer
|
||||
- (UIColor *)colorForCellFill
|
||||
{
|
||||
if (self.dateIsSelected || self.isSelected) {
|
||||
return self.preferredSelectionColor ?: [self colorForCurrentStateInDictionary:_appearance.backgroundColors];
|
||||
if (self.selected) {
|
||||
return self.preferredFillSelectionColor ?: [self colorForCurrentStateInDictionary:_appearance.backgroundColors];
|
||||
}
|
||||
return [self colorForCurrentStateInDictionary:_appearance.backgroundColors];
|
||||
return self.preferredFillDefaultColor ?: [self colorForCurrentStateInDictionary:_appearance.backgroundColors];
|
||||
}
|
||||
|
||||
- (UIColor *)colorForTitleLabel
|
||||
{
|
||||
if (self.dateIsSelected || self.isSelected) {
|
||||
if (self.selected) {
|
||||
return self.preferredTitleSelectionColor ?: [self colorForCurrentStateInDictionary:_appearance.titleColors];
|
||||
}
|
||||
return self.preferredTitleDefaultColor ?: [self colorForCurrentStateInDictionary:_appearance.titleColors];
|
||||
|
@ -318,7 +297,7 @@
|
|||
|
||||
- (UIColor *)colorForSubtitleLabel
|
||||
{
|
||||
if (self.dateIsSelected || self.isSelected) {
|
||||
if (self.selected) {
|
||||
return self.preferredSubtitleSelectionColor ?: [self colorForCurrentStateInDictionary:_appearance.subtitleColors];
|
||||
}
|
||||
return self.preferredSubtitleDefaultColor ?: [self colorForCurrentStateInDictionary:_appearance.subtitleColors];
|
||||
|
@ -326,51 +305,168 @@
|
|||
|
||||
- (UIColor *)colorForCellBorder
|
||||
{
|
||||
if (self.dateIsSelected || self.isSelected) {
|
||||
if (self.selected) {
|
||||
return _preferredBorderSelectionColor ?: _appearance.borderSelectionColor;
|
||||
}
|
||||
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
|
||||
{
|
||||
if (![_calendar isEqual:calendar]) {
|
||||
_calendar = calendar;
|
||||
}
|
||||
if (![_appearance isEqual:calendar.appearance]) {
|
||||
_appearance = calendar.appearance;
|
||||
[self invalidateTitleFont];
|
||||
[self invalidateSubtitleFont];
|
||||
[self invalidateTitleTextColor];
|
||||
[self invalidateSubtitleTextColor];
|
||||
[self invalidateEventColors];
|
||||
[self configureAppearance];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setSubtitle:(NSString *)subtitle
|
||||
{
|
||||
if (![_subtitle isEqualToString:subtitle]) {
|
||||
_needsAdjustingViewFrame = !(_subtitle.length && subtitle.length);
|
||||
BOOL diff = (subtitle.length && !_subtitle.length) || (_subtitle.length && !subtitle.length);
|
||||
_subtitle = subtitle;
|
||||
if (_needsAdjustingViewFrame) {
|
||||
if (diff) {
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setNeedsAdjustingViewFrame:(BOOL)needsAdjustingViewFrame
|
||||
{
|
||||
if (_needsAdjustingViewFrame != needsAdjustingViewFrame) {
|
||||
_needsAdjustingViewFrame = needsAdjustingViewFrame;
|
||||
_eventIndicator.needsAdjustingViewFrame = needsAdjustingViewFrame;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface FSCalendarEventIndicator ()
|
||||
|
||||
@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
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// FSCalendar
|
||||
//
|
||||
// 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>
|
||||
|
@ -11,3 +11,8 @@
|
|||
@interface FSCalendarCollectionView : UICollectionView
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface FSCalendarSeparator : UICollectionReusableView
|
||||
|
||||
@end
|
||||
|
|
|
@ -3,11 +3,14 @@
|
|||
// FSCalendar
|
||||
//
|
||||
// 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 "FSCalendarCell.h"
|
||||
#import "FSCalendarExtensions.h"
|
||||
#import "FSCalendarConstants.h"
|
||||
|
||||
@interface FSCalendarCollectionView ()
|
||||
|
||||
|
@ -41,16 +44,27 @@
|
|||
{
|
||||
self.scrollsToTop = NO;
|
||||
self.contentInset = UIEdgeInsetsZero;
|
||||
|
||||
#ifdef __IPHONE_9_0
|
||||
if ([self respondsToSelector:@selector(setSemanticContentAttribute:)]) {
|
||||
self.semanticContentAttribute = UISemanticContentAttributeForceLeftToRight;
|
||||
}
|
||||
#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
|
||||
{
|
||||
[super setContentInset:UIEdgeInsetsZero];
|
||||
if (contentInset.top) {
|
||||
self.contentOffset = CGPointMake(self.contentOffset.x, self.contentOffset.y+contentInset.top);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setScrollsToTop:(BOOL)scrollsToTop
|
||||
|
@ -60,3 +74,24 @@
|
|||
|
||||
@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
|
||||
//
|
||||
|
||||
#import "FSCalendarConstance.h"
|
||||
#import "FSCalendarConstants.h"
|
||||
|
||||
CGFloat const FSCalendarStandardHeaderHeight = 40;
|
||||
CGFloat const FSCalendarStandardWeekdayHeight = 25;
|
||||
CGFloat const FSCalendarStandardMonthlyPageHeight = 300.0;
|
||||
CGFloat const FSCalendarStandardWeeklyPageHeight = 108+1/3.0;
|
||||
CGFloat const FSCalendarStandardCellDiameter = 100/3.0;
|
||||
CGFloat const FSCalendarStandardSeparatorThickness = 0.5;
|
||||
CGFloat const FSCalendarAutomaticDimension = -1;
|
||||
CGFloat const FSCalendarDefaultBounceAnimationDuration = 0.15;
|
||||
CGFloat const FSCalendarStandardRowHeight = 38+1.0/3;
|
||||
CGFloat const FSCalendarStandardRowHeight = 38;
|
||||
CGFloat const FSCalendarStandardTitleTextSize = 13.5;
|
||||
CGFloat const FSCalendarStandardSubtitleTextSize = 10;
|
||||
CGFloat const FSCalendarStandardWeekdayTextSize = 14;
|
||||
CGFloat const FSCalendarStandardHeaderTextSize = 16.5;
|
||||
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 "FSCalendarCell.h"
|
||||
#import "FSCalendarHeader.h"
|
||||
#import "FSCalendarHeaderView.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)
|
||||
|
||||
@property (readonly, nonatomic) CAShapeLayer *maskLayer;
|
||||
@property (readonly, nonatomic) FSCalendarHeader *header;
|
||||
@property (readonly, nonatomic) UICollectionView *collectionView;
|
||||
@property (readonly, nonatomic) UICollectionViewFlowLayout *collectionViewLayout;
|
||||
@property (readonly, nonatomic) NSArray *weekdays;
|
||||
@property (readonly, nonatomic) BOOL ibEditing;
|
||||
@property (readonly, nonatomic) FSCalendarCollectionView *collectionView;
|
||||
@property (readonly, nonatomic) FSCalendarScopeHandle *scopeHandle;
|
||||
@property (readonly, nonatomic) FSCalendarCollectionViewLayout *collectionViewLayout;
|
||||
@property (readonly, nonatomic) FSCalendarTransitionCoordinator *transitionCoordinator;
|
||||
@property (readonly, nonatomic) FSCalendarCalculator *calculator;
|
||||
@property (readonly, nonatomic) BOOL floatingMode;
|
||||
@property (readonly, nonatomic) NSArray *visibleStickyHeaders;
|
||||
@property (readonly, nonatomic) CGFloat preferredHeaderHeight;
|
||||
@property (readonly, nonatomic) CGFloat preferredWeekdayHeight;
|
||||
@property (readonly, nonatomic) CGFloat preferredRowHeight;
|
||||
@property (readonly, nonatomic) UIView *bottomBorder;
|
||||
|
||||
@property (readonly, nonatomic) NSCalendar *calendar;
|
||||
@property (readonly, nonatomic) NSCalendar *gregorian;
|
||||
@property (readonly, nonatomic) NSDateComponents *components;
|
||||
@property (readonly, nonatomic) NSDateFormatter *formatter;
|
||||
|
||||
@property (readonly, nonatomic) UIView *contentView;
|
||||
@property (readonly, nonatomic) UIView *daysContainer;
|
||||
|
||||
@property (assign, nonatomic) BOOL needsAdjustingMonthPosition;
|
||||
@property (assign, nonatomic) BOOL needsAdjustingViewFrame;
|
||||
|
||||
- (void)invalidateWeekdayFont;
|
||||
- (void)invalidateWeekdayTextColor;
|
||||
|
||||
- (void)invalidateHeaders;
|
||||
- (void)invalidateWeekdaySymbols;
|
||||
- (void)invalidateAppearanceForCell:(FSCalendarCell *)cell;
|
||||
- (void)adjustMonthPosition;
|
||||
- (void)configureAppearance;
|
||||
|
||||
- (NSDate *)dateForIndexPath:(NSIndexPath *)indexPath;
|
||||
- (NSDate *)dateForIndexPath:(NSIndexPath *)indexPath scope:(FSCalendarScope)scope;
|
||||
- (NSIndexPath *)indexPathForDate:(NSDate *)date;
|
||||
- (NSIndexPath *)indexPathForDate:(NSDate *)date scope:(FSCalendarScope)scope;
|
||||
|
||||
- (NSInteger)numberOfHeadPlaceholdersForMonth:(NSDate *)month;
|
||||
- (BOOL)isPageInRange:(NSDate *)page;
|
||||
- (BOOL)isDateInRange:(NSDate *)date;
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size scope:(FSCalendarScope)scope;
|
||||
|
||||
|
@ -68,21 +64,21 @@
|
|||
@property (readonly, nonatomic) NSDictionary *subtitleColors;
|
||||
@property (readonly, nonatomic) NSDictionary *borderColors;
|
||||
|
||||
@property (readonly, nonatomic) UIFont *preferredTitleFont;
|
||||
@property (readonly, nonatomic) UIFont *preferredSubtitleFont;
|
||||
@property (readonly, nonatomic) UIFont *preferredWeekdayFont;
|
||||
@property (readonly, nonatomic) UIFont *preferredHeaderTitleFont;
|
||||
@end
|
||||
|
||||
- (void)adjustTitleIfNecessary;
|
||||
- (void)invalidateFonts;
|
||||
@interface FSCalendarWeekdayView (Dynamic)
|
||||
|
||||
@property (readwrite, nonatomic) FSCalendar *calendar;
|
||||
|
||||
@end
|
||||
|
||||
@interface FSCalendarCollectionViewLayout (Dynamic)
|
||||
|
||||
@interface FSCalendarHeader (Dynamic)
|
||||
|
||||
@property (readonly, nonatomic) UICollectionView *collectionView;
|
||||
@property (readonly, nonatomic) CGSize estimatedItemSize;
|
||||
|
||||
@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 "FSCalendarCollectionView.h"
|
||||
|
||||
@class FSCalendar,FSCalendarAppearance;
|
||||
|
||||
@interface FSCalendarHeader : UIView
|
||||
@class FSCalendar, FSCalendarAppearance, FSCalendarHeaderLayout, FSCalendarCollectionView;
|
||||
|
||||
@interface FSCalendarHeaderView : UIView
|
||||
|
||||
@property (weak, nonatomic) FSCalendarCollectionView *collectionView;
|
||||
@property (weak, nonatomic) FSCalendarHeaderLayout *collectionViewLayout;
|
||||
@property (weak, nonatomic) FSCalendar *calendar;
|
||||
@property (weak, nonatomic) FSCalendarAppearance *appearance;
|
||||
|
||||
@property (assign, nonatomic) CGFloat scrollOffset;
|
||||
@property (assign, nonatomic) UICollectionViewScrollDirection scrollDirection;
|
||||
@property (assign, nonatomic) BOOL scrollEnabled;
|
||||
@property (assign, nonatomic) BOOL needsAdjustingViewFrame;
|
||||
@property (assign, nonatomic) BOOL needsAdjustingMonthPosition;
|
||||
|
||||
- (void)setScrollOffset:(CGFloat)scrollOffset animated:(BOOL)animated;
|
||||
- (void)reloadData;
|
||||
- (void)configureAppearance;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -30,17 +33,17 @@
|
|||
@interface FSCalendarHeaderCell : UICollectionViewCell
|
||||
|
||||
@property (weak, nonatomic) UILabel *titleLabel;
|
||||
@property (weak, nonatomic) FSCalendarHeader *header;
|
||||
|
||||
- (void)invalidateHeaderFont;
|
||||
- (void)invalidateHeaderTextColor;
|
||||
@property (weak, nonatomic) FSCalendarHeaderView *header;
|
||||
|
||||
@end
|
||||
|
||||
@interface FSCalendarHeaderLayout : UICollectionViewFlowLayout
|
||||
|
||||
@end
|
||||
|
||||
@interface FSCalendarHeaderTouchDeliver : UIView
|
||||
|
||||
@property (weak, nonatomic) FSCalendar *calendar;
|
||||
@property (weak, nonatomic) FSCalendarHeader *header;
|
||||
@property (weak, nonatomic) FSCalendarHeaderView *header;
|
||||
|
||||
@end
|
|
@ -7,20 +7,19 @@
|
|||
//
|
||||
|
||||
#import "FSCalendar.h"
|
||||
#import "UIView+FSExtension.h"
|
||||
#import "FSCalendarHeader.h"
|
||||
#import "FSCalendarExtensions.h"
|
||||
#import "FSCalendarHeaderView.h"
|
||||
#import "FSCalendarCollectionView.h"
|
||||
#import "FSCalendarDynamicHeader.h"
|
||||
|
||||
@interface FSCalendarHeader ()<UICollectionViewDataSource,UICollectionViewDelegate>
|
||||
@interface FSCalendarHeaderView ()<UICollectionViewDataSource,UICollectionViewDelegate>
|
||||
|
||||
@property (weak, nonatomic) UICollectionViewFlowLayout *collectionViewLayout;
|
||||
|
||||
@property (assign, nonatomic) BOOL needsAdjustingMonthPosition;
|
||||
- (void)scrollToOffset:(CGFloat)scrollOffset animated:(BOOL)animated;
|
||||
- (void)configureCell:(FSCalendarHeaderCell *)cell atIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FSCalendarHeader
|
||||
@implementation FSCalendarHeaderView
|
||||
|
||||
#pragma mark - Life cycle
|
||||
|
||||
|
@ -44,17 +43,12 @@
|
|||
|
||||
- (void)initialize
|
||||
{
|
||||
_needsAdjustingViewFrame = YES;
|
||||
_needsAdjustingMonthPosition = YES;
|
||||
_scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||
_scrollEnabled = YES;
|
||||
_needsAdjustingMonthPosition = YES;
|
||||
_needsAdjustingViewFrame = YES;
|
||||
|
||||
UICollectionViewFlowLayout *collectionViewLayout = [[UICollectionViewFlowLayout alloc] init];
|
||||
collectionViewLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||
collectionViewLayout.minimumInteritemSpacing = 0;
|
||||
collectionViewLayout.minimumLineSpacing = 0;
|
||||
collectionViewLayout.sectionInset = UIEdgeInsetsZero;
|
||||
collectionViewLayout.itemSize = CGSizeMake(1, 1);
|
||||
FSCalendarHeaderLayout *collectionViewLayout = [[FSCalendarHeaderLayout alloc] init];
|
||||
self.collectionViewLayout = collectionViewLayout;
|
||||
|
||||
FSCalendarCollectionView *collectionView = [[FSCalendarCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:collectionViewLayout];
|
||||
|
@ -77,21 +71,14 @@
|
|||
if (_needsAdjustingViewFrame) {
|
||||
_needsAdjustingViewFrame = NO;
|
||||
_collectionViewLayout.itemSize = CGSizeMake(1, 1);
|
||||
[_collectionViewLayout invalidateLayout];
|
||||
_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) {
|
||||
_needsAdjustingMonthPosition = NO;
|
||||
if (self.scrollDirection == UICollectionViewScrollDirectionHorizontal) {
|
||||
[_collectionView setContentOffset:CGPointMake((_scrollOffset+0.5)*_collectionViewLayout.itemSize.width, 0) animated:NO];
|
||||
} else {
|
||||
[_collectionView setContentOffset:CGPointMake(0, _scrollOffset * _collectionViewLayout.itemSize.height) animated:NO];
|
||||
}
|
||||
};
|
||||
[self scrollToOffset:_scrollOffset animated:NO];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -110,81 +97,18 @@
|
|||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
|
||||
{
|
||||
switch (self.calendar.scope) {
|
||||
case FSCalendarScopeMonth: {
|
||||
switch (_scrollDirection) {
|
||||
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;
|
||||
}
|
||||
NSInteger numberOfSections = self.calendar.collectionView.numberOfSections;
|
||||
if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
|
||||
return numberOfSections;
|
||||
}
|
||||
return 0;
|
||||
return numberOfSections + 2;
|
||||
}
|
||||
|
||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
FSCalendarHeaderCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
|
||||
cell.header = self;
|
||||
cell.titleLabel.font = _appearance.preferredHeaderTitleFont;
|
||||
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];
|
||||
[self configureCell:cell atIndexPath:indexPath];
|
||||
return cell;
|
||||
}
|
||||
|
||||
|
@ -200,23 +124,34 @@
|
|||
|
||||
#pragma mark - Properties
|
||||
|
||||
|
||||
- (void)setCalendar:(FSCalendar *)calendar
|
||||
{
|
||||
if (![_calendar isEqual:calendar]) {
|
||||
_calendar = calendar;
|
||||
_appearance = calendar.appearance;
|
||||
}
|
||||
_calendar = calendar;
|
||||
[self configureAppearance];
|
||||
}
|
||||
|
||||
|
||||
- (void)setScrollOffset:(CGFloat)scrollOffset
|
||||
{
|
||||
[self setScrollOffset:scrollOffset animated:NO];
|
||||
}
|
||||
|
||||
- (void)setScrollOffset:(CGFloat)scrollOffset animated:(BOOL)animated
|
||||
{
|
||||
if (_scrollOffset != scrollOffset) {
|
||||
_scrollOffset = scrollOffset;
|
||||
}
|
||||
_needsAdjustingMonthPosition = YES;
|
||||
[self setNeedsLayout];
|
||||
[self scrollToOffset:scrollOffset animated:NO];
|
||||
}
|
||||
|
||||
- (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
|
||||
|
@ -225,7 +160,6 @@
|
|||
_scrollDirection = scrollDirection;
|
||||
_collectionViewLayout.scrollDirection = scrollDirection;
|
||||
_needsAdjustingMonthPosition = YES;
|
||||
_needsAdjustingViewFrame = YES;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
}
|
||||
|
@ -245,6 +179,56 @@
|
|||
[_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
|
||||
|
||||
|
||||
|
@ -280,31 +264,49 @@
|
|||
CGFloat position = [self.contentView convertPoint:CGPointMake(CGRectGetMidX(self.contentView.bounds), CGRectGetMidY(self.contentView.bounds)) toView:self.header].x;
|
||||
CGFloat center = CGRectGetMidX(self.header.bounds);
|
||||
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 {
|
||||
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) {
|
||||
CGFloat position = [self.contentView convertPoint:CGPointMake(CGRectGetMidX(self.contentView.bounds), CGRectGetMidY(self.contentView.bounds)) toView:self.header].y;
|
||||
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
|
||||
|
||||
|
||||
@implementation FSCalendarHeaderTouchDeliver
|
||||
|
||||
- (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
|
||||
//
|
||||
// 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>
|
||||
|
@ -13,18 +13,11 @@
|
|||
@interface FSCalendarStickyHeader : UICollectionReusableView
|
||||
|
||||
@property (weak, nonatomic) FSCalendar *calendar;
|
||||
@property (weak, nonatomic) FSCalendarAppearance *appearance;
|
||||
|
||||
@property (weak, nonatomic) UILabel *titleLabel;
|
||||
|
||||
@property (strong, nonatomic) NSArray *weekdayLabels;
|
||||
@property (strong, nonatomic) NSDate *month;
|
||||
|
||||
- (void)invalidateHeaderFont;
|
||||
- (void)invalidateHeaderTextColor;
|
||||
- (void)invalidateWeekdayFont;
|
||||
- (void)invalidateWeekdayTextColor;
|
||||
- (void)configureAppearance;
|
||||
|
||||
- (void)invalidateWeekdaySymbols;
|
||||
|
||||
@end
|
||||
@end
|
||||
|
|
|
@ -3,21 +3,21 @@
|
|||
// FSCalendar
|
||||
//
|
||||
// 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 "FSCalendar.h"
|
||||
#import "UIView+FSExtension.h"
|
||||
#import "FSCalendarConstance.h"
|
||||
#import "FSCalendarWeekdayView.h"
|
||||
#import "FSCalendarExtensions.h"
|
||||
#import "FSCalendarConstants.h"
|
||||
#import "FSCalendarDynamicHeader.h"
|
||||
|
||||
@interface FSCalendarStickyHeader ()
|
||||
|
||||
@property (weak, nonatomic) UIView *contentView;
|
||||
@property (weak, nonatomic) UIView *separator;
|
||||
|
||||
@property (assign, nonatomic) BOOL needsAdjustingViewFrame;
|
||||
@property (weak , nonatomic) UIView *contentView;
|
||||
@property (weak , nonatomic) UIView *bottomBorder;
|
||||
@property (weak , nonatomic) FSCalendarWeekdayView *weekdayView;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -28,8 +28,6 @@
|
|||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
|
||||
_needsAdjustingViewFrame = YES;
|
||||
|
||||
UIView *view;
|
||||
UILabel *label;
|
||||
|
||||
|
@ -45,18 +43,13 @@
|
|||
self.titleLabel = label;
|
||||
|
||||
view = [[UIView alloc] initWithFrame:CGRectZero];
|
||||
view.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.25];
|
||||
view.backgroundColor = FSCalendarStandardLineColor;
|
||||
[_contentView addSubview:view];
|
||||
self.separator = view;
|
||||
self.bottomBorder = view;
|
||||
|
||||
NSMutableArray *weekdayLabels = [NSMutableArray arrayWithCapacity:7];
|
||||
for (int i = 0; i < 7; i++) {
|
||||
label = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||
label.textAlignment = NSTextAlignmentCenter;
|
||||
[_contentView addSubview:label];
|
||||
[weekdayLabels addObject:label];
|
||||
}
|
||||
self.weekdayLabels = weekdayLabels.copy;
|
||||
FSCalendarWeekdayView *weekdayView = [[FSCalendarWeekdayView alloc] init];
|
||||
[self.contentView addSubview:weekdayView];
|
||||
self.weekdayView = weekdayView;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -65,28 +58,19 @@
|
|||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
if (_needsAdjustingViewFrame) {
|
||||
|
||||
_needsAdjustingViewFrame = NO;
|
||||
_contentView.frame = self.bounds;
|
||||
|
||||
CGFloat weekdayWidth = self.fs_width / 7.0;
|
||||
CGFloat weekdayHeight = _calendar.preferredWeekdayHeight;
|
||||
CGFloat weekdayMargin = weekdayHeight * 0.1;
|
||||
CGFloat titleWidth = _contentView.fs_width;
|
||||
|
||||
[_weekdayLabels enumerateObjectsUsingBlock:^(UILabel *label, NSUInteger index, BOOL *stop) { \
|
||||
label.frame = CGRectMake(index*weekdayWidth, _contentView.fs_height-weekdayHeight-weekdayMargin, weekdayWidth, weekdayHeight);
|
||||
}];
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
_contentView.frame = self.bounds;
|
||||
|
||||
CGFloat weekdayHeight = _calendar.preferredWeekdayHeight;
|
||||
CGFloat weekdayMargin = weekdayHeight * 0.1;
|
||||
CGFloat titleWidth = _contentView.fs_width;
|
||||
|
||||
self.weekdayView.frame = CGRectMake(0, _contentView.fs_height-weekdayHeight-weekdayMargin, self.contentView.fs_width, weekdayHeight);
|
||||
|
||||
CGFloat titleHeight = [@"1" sizeWithAttributes:@{NSFontAttributeName:self.calendar.appearance.headerTitleFont}].height*1.5 + weekdayMargin*3;
|
||||
|
||||
_bottomBorder.frame = CGRectMake(0, _contentView.fs_height-weekdayHeight-weekdayMargin*2, _contentView.fs_width, 1.0);
|
||||
_titleLabel.frame = CGRectMake(0, _bottomBorder.fs_bottom-titleHeight-weekdayMargin, titleWidth,titleHeight);
|
||||
|
||||
[self reloadData];
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
@ -95,63 +79,30 @@
|
|||
{
|
||||
if (![_calendar isEqual: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
|
||||
|
||||
- (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
|
||||
|
||||
|
||||
|
|
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
|
||||
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)
|
||||
[![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)
|
||||
[![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)
|
||||
[![License](https://img.shields.io/cocoapods/l/FSCalendar.svg?style=flat)](http://cocoadocs.org/docsets/FSCalendar)
|
||||
[![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)
|
||||
<br>
|
||||
[![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)
|
||||
|
||||
## iPad
|
||||
### iPad
|
||||
![fscalendar-ipad](https://cloud.githubusercontent.com/assets/5186464/10927681/d2448cb6-82dc-11e5-9d11-f664a06698a7.jpg)
|
||||
|
||||
## Working with AutoLayout and Orientation
|
||||
![fscalendar-scope-orientation-autolayout](https://cloud.githubusercontent.com/assets/5186464/13728798/59855e3e-e95e-11e5-84db-60f843427ef3.gif)
|
||||
### Safe Orientation
|
||||
![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:
|
||||
|
||||
* For iOS8+: 👍
|
||||
|
||||
```ruby
|
||||
use_frameworks!
|
||||
pod 'FSCalendar'
|
||||
target '<Your Target Name>' do
|
||||
pod 'FSCalendar'
|
||||
end
|
||||
```
|
||||
|
||||
* For iOS7+:
|
||||
|
||||
```ruby
|
||||
pod 'FSCalendar'
|
||||
target '<Your Target Name>' do
|
||||
pod 'FSCalendar'
|
||||
end
|
||||
```
|
||||
|
||||
* Alternatively to give it a test run, run the command:
|
||||
```ruby
|
||||
pod try FSCalendar
|
||||
```
|
||||
> [NSCalendarExtension](https://github.com/WenchaoD/NSCalendarExtension) is required to get iOS7 compatibility.
|
||||
|
||||
## Carthage:
|
||||
* For iOS8+
|
||||
|
||||
```ruby
|
||||
github "WenchaoD/FSCalendar"
|
||||
```
|
||||
|
@ -47,20 +92,21 @@ github "WenchaoD/FSCalendar"
|
|||
## Manually:
|
||||
* Drag all files under `FSCalendar` folder into your project. 👍
|
||||
|
||||
## Support IBInspectable / IBDesignable
|
||||
Only the methods marked "👍" support IBInspectable / IBDesignable feature. [Have fun with Interface builder](#roll_with_interface_builder)
|
||||
> 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)
|
||||
|
||||
|
||||
# Setup
|
||||
|
||||
## Use Interface Builder
|
||||
|
||||
1. Drag an UIView object to ViewController Scene
|
||||
2. Change the `Custom Class` to `FSCalendar`<br/>
|
||||
3. Link `dataSource` and `delegate` to the ViewController <br/>
|
||||
1、 Drag an UIView object to ViewController Scene
|
||||
2、 Change the `Custom Class` to `FSCalendar`<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)
|
||||
|
||||
4. Finally, you should implement `FSCalendarDataSource` and `FSCalendarDelegate` in ViewController.m
|
||||
4、 Finally, implement `FSCalendarDataSource` and `FSCalendarDelegate` in your `ViewController`
|
||||
|
||||
## Or use code
|
||||
|
||||
|
@ -83,7 +129,7 @@ self.calendar = calendar;
|
|||
|
||||
|
||||
```swift
|
||||
private weak var calendar: FSCalendar!
|
||||
fileprivate weak var calendar: FSCalendar!
|
||||
```
|
||||
```swift
|
||||
// In loadView or viewDidLoad
|
||||
|
@ -93,57 +139,190 @@ calendar.delegate = self
|
|||
view.addSubview(calendar)
|
||||
self.calendar = calendar
|
||||
```
|
||||
<br/>
|
||||
|
||||
> To use **FSCalendar** in Swift3, see `Example-Swift` for details.
|
||||
|
||||
|
||||
## Hide placeholder dates
|
||||
![fscalendar-showsplaceholder](https://cloud.githubusercontent.com/assets/5186464/13727902/21a90042-e940-11e5-9b9f-392f38cf007d.gif)
|
||||
## <a id='adjusts_frame_dynamicly' /></a>Warning
|
||||
`FSCalendar` ***doesn't*** update frame by itself, Please implement
|
||||
|
||||
1. Set `calendar.showsPlaceholders = NO`;
|
||||
2. <a id="implement_bounding_rect_will_change"></a> Implement `-calendar:boundingRectWillChange:animated:`
|
||||
* For ***AutoLayout***
|
||||
|
||||
```objc
|
||||
// For autoLayout
|
||||
- (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];
|
||||
}
|
||||
```
|
||||
|
||||
* For ***Manual Layout***
|
||||
|
||||
```objc
|
||||
// For manual layout
|
||||
- (void)calendar:(FSCalendar *)calendar boundingRectWillChange:(CGRect)bounds animated:(BOOL)animated
|
||||
{
|
||||
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
|
||||
![fscalendar - ibdesignable](https://cloud.githubusercontent.com/assets/5186464/9301716/2e76a2ca-4503-11e5-8450-1fa7aa93e9fd.gif)
|
||||
|
||||
## More Usage
|
||||
* 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/)
|
||||
# <a id="pre-knowledge"></a>Pre-knowledge
|
||||
|
||||
# If you like this repo
|
||||
* ***Star*** this repo.
|
||||
* Send your calendar screenshot or `itunes link address` [here](https://github.com/WenchaoD/FSCalendar/issues/2).
|
||||
> In `Swift3`, `NSDate` and `NSDateFormatter` have been renamed to ***Date*** and ***DateFormatter*** , see `Example-Swift` for details.
|
||||
|
||||
## 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
|
||||
FSCalendar is available under the MIT license. See the LICENSE file for more info.
|
||||
|
||||
# Contributions
|
||||
* 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`
|
||||
|
||||
### [Documentation](http://cocoadocs.org/docsets/FSCalendar/) | [More Usage](https://github.com/WenchaoD/FSCalendar/blob/master/MOREUSAGE.md) | [简书](http://www.jianshu.com/notebooks/4276521/latest)
|
||||
|
|
|
@ -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