diff --git a/Source/ASViewController.h b/Source/ASViewController.h index bdd343b30d..28fd134f4b 100644 --- a/Source/ASViewController.h +++ b/Source/ASViewController.h @@ -63,7 +63,7 @@ NS_ASSUME_NONNULL_BEGIN /** * Set this block to customize the ASDisplayTraits returned when the VC transitions to the given window size. */ -@property (nonatomic, copy) ASDisplayTraitsForTraitWindowSizeBlock overrideDisplayTraitsWithWindowSize; +@property (nonatomic, copy) ASDisplayTraitsForTraitWindowSizeBlock overrideDisplayTraitsWithWindowSize ASDISPLAYNODE_DEPRECATED_MSG("This property is actually never accessed inside the framework"); /** * @abstract Passthrough property to the the .interfaceState of the node. diff --git a/Source/Details/ASTraitCollection.h b/Source/Details/ASTraitCollection.h index 7f8ae3a476..1932b487a0 100644 --- a/Source/Details/ASTraitCollection.h +++ b/Source/Details/ASTraitCollection.h @@ -58,6 +58,14 @@ extern ASPrimitiveContentSizeCategory ASPrimitiveContentSizeCategoryMake(UIConte #pragma mark - ASPrimitiveTraitCollection +/** + * @abstract This is an internal struct-representation of ASTraitCollection. + * + * @discussion This struct is for internal use only. Framework users should always use ASTraitCollection. + * + * If you use ASPrimitiveTraitCollection, please do make sure to initialize it with ASPrimitiveTraitCollectionMakeDefault() + * or ASPrimitiveTraitCollectionFromUITraitCollection(UITraitCollection*). + */ typedef struct ASPrimitiveTraitCollection { UIUserInterfaceSizeClass horizontalSizeClass; UIUserInterfaceSizeClass verticalSizeClass; @@ -112,17 +120,23 @@ ASDISPLAYNODE_EXTERN_C_END @protocol ASTraitEnvironment /** - * Returns a struct-representation of the environment's ASEnvironmentDisplayTraits. This only exists as a internal - * convenience method. Users should access the trait collections through the NSObject based asyncTraitCollection API + * @abstract Returns a struct-representation of the environment's ASEnvironmentDisplayTraits. + * + * @discussion This only exists as an internal convenience method. Users should access the trait collections through + * the NSObject based asyncTraitCollection API */ - (ASPrimitiveTraitCollection)primitiveTraitCollection; /** - * Sets a trait collection on this environment state. + * @abstract Sets a trait collection on this environment state. + * + * @discussion This only exists as an internal convenience method. Users should not override trait collection using it. + * Use [ASViewController overrideDisplayTraitsWithTraitCollection] block instead. */ - (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection; /** + * @abstract Returns the thread-safe UITraitCollection equivalent. */ - (ASTraitCollection *)asyncTraitCollection; @@ -179,8 +193,6 @@ AS_SUBCLASSING_RESTRICTED @property (nonatomic, assign, readonly) CGSize containerSize; -+ (ASTraitCollection *)traitCollectionWithASPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traits; - + (ASTraitCollection *)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection containerSize:(CGSize)windowSize; @@ -211,13 +223,25 @@ AS_SUBCLASSING_RESTRICTED containerSize:(CGSize)windowSize; #endif -- (ASPrimitiveTraitCollection)primitiveTraitCollection; - (BOOL)isEqualToTraitCollection:(ASTraitCollection *)traitCollection; @end +/** + * These are internal helper methods. Should never be called by the framework users. + */ +@interface ASTraitCollection (PrimitiveTraits) + ++ (ASTraitCollection *)traitCollectionWithASPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traits; + +- (ASPrimitiveTraitCollection)primitiveTraitCollection; + +@end + @interface ASTraitCollection (Deprecated) +- (instancetype)init ASDISPLAYNODE_DEPRECATED_MSG("The default constructor of this class is going to become unavailable. Use other constructors instead."); + + (ASTraitCollection *)traitCollectionWithDisplayScale:(CGFloat)displayScale userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass diff --git a/Source/Details/ASTraitCollection.m b/Source/Details/ASTraitCollection.m index 8755fc57c9..194fe82788 100644 --- a/Source/Details/ASTraitCollection.m +++ b/Source/Details/ASTraitCollection.m @@ -32,6 +32,10 @@ ASDISPLAYNODE_INLINE UIContentSizeCategory AS_UIContentSizeCategoryUnspecified() } } +ASDISPLAYNODE_INLINE UIContentSizeCategory _Nonnull AS_safeContentSizeCategory(UIContentSizeCategory _Nullable sizeCategory) { + return sizeCategory ? sizeCategory : AS_UIContentSizeCategoryUnspecified(); +} + ASPrimitiveContentSizeCategory ASPrimitiveContentSizeCategoryMake(UIContentSizeCategory sizeCategory) { if ([sizeCategory isEqualToString:UIContentSizeCategoryExtraSmall]) { return UIContentSizeCategoryExtraSmall; @@ -115,13 +119,15 @@ ASPrimitiveTraitCollection ASPrimitiveTraitCollectionFromUITraitCollection(UITra #if TARGET_OS_TV environmentTraitCollection.userInterfaceStyle = traitCollection.userInterfaceStyle; #endif + } else { + environmentTraitCollection.displayGamut = UIDisplayGamutSRGB; // We're on iOS 9 or lower, so this is not a P3 device. } return environmentTraitCollection; } BOOL ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(ASPrimitiveTraitCollection lhs, ASPrimitiveTraitCollection rhs) { - UIContentSizeCategory leftSizeCategory = (UIContentSizeCategory)lhs.preferredContentSizeCategory; - UIContentSizeCategory rightSizeCategory = (UIContentSizeCategory)rhs.preferredContentSizeCategory; + UIContentSizeCategory leftSizeCategory = AS_safeContentSizeCategory(lhs.preferredContentSizeCategory); + UIContentSizeCategory rightSizeCategory = AS_safeContentSizeCategory(rhs.preferredContentSizeCategory); return lhs.verticalSizeClass == rhs.verticalSizeClass && @@ -230,7 +236,7 @@ NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection trai #if TARGET_OS_TV [props addObject:@{ @"userInterfaceStyle": AS_NSStringFromUIUserInterfaceStyle(traits.userInterfaceStyle) }]; #endif - [props addObject:@{ @"preferredContentSizeCategory": (UIContentSizeCategory)traits.preferredContentSizeCategory }]; + [props addObject:@{ @"preferredContentSizeCategory": AS_safeContentSizeCategory(traits.preferredContentSizeCategory) }]; [props addObject:@{ @"containerSize": NSStringFromCGSize(traits.containerSize) }]; return ASObjectDescriptionMakeWithoutObject(props); } @@ -249,7 +255,7 @@ NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection trai forceTouchCapability:(UIForceTouchCapability)forceTouchCapability layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection userInterfaceStyle:(UIUserInterfaceStyle)userInterfaceStyle - preferredContentSizeCategory:(UIContentSizeCategory)preferredContentSizeCategory + preferredContentSizeCategory:(UIContentSizeCategory _Nonnull)preferredContentSizeCategory containerSize:(CGSize)windowSize { self = [super init]; @@ -262,7 +268,7 @@ NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection trai _forceTouchCapability = forceTouchCapability; _layoutDirection = layoutDirection; _userInterfaceStyle = userInterfaceStyle; - _preferredContentSizeCategory = preferredContentSizeCategory; + _preferredContentSizeCategory = AS_safeContentSizeCategory(preferredContentSizeCategory); // guard against misuse _containerSize = windowSize; } return self; @@ -276,7 +282,7 @@ NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection trai forceTouchCapability:(UIForceTouchCapability)forceTouchCapability layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection userInterfaceStyle:(UIUserInterfaceStyle)userInterfaceStyle - preferredContentSizeCategory:(UIContentSizeCategory)preferredContentSizeCategory + preferredContentSizeCategory:(UIContentSizeCategory _Nonnull)preferredContentSizeCategory containerSize:(CGSize)windowSize { return [[self alloc] initWithHorizontalSizeClass:horizontalSizeClass @@ -300,7 +306,7 @@ NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection trai userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom forceTouchCapability:(UIForceTouchCapability)forceTouchCapability layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection - preferredContentSizeCategory:(UIContentSizeCategory)preferredContentSizeCategory + preferredContentSizeCategory:(UIContentSizeCategory _Nonnull)preferredContentSizeCategory containerSize:(CGSize)windowSize { self = [super init]; @@ -312,7 +318,7 @@ NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection trai _userInterfaceIdiom = userInterfaceIdiom; _forceTouchCapability = forceTouchCapability; _layoutDirection = layoutDirection; - _preferredContentSizeCategory = preferredContentSizeCategory; + _preferredContentSizeCategory = AS_safeContentSizeCategory(preferredContentSizeCategory); // guard against misuse _containerSize = windowSize; } return self; @@ -325,7 +331,7 @@ NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection trai userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom forceTouchCapability:(UIForceTouchCapability)forceTouchCapability layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection - preferredContentSizeCategory:(UIContentSizeCategory)preferredContentSizeCategory + preferredContentSizeCategory:(UIContentSizeCategory _Nonnull)preferredContentSizeCategory containerSize:(CGSize)windowSize { return [[self alloc] initWithHorizontalSizeClass:horizontalSizeClass @@ -341,6 +347,168 @@ NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection trai #endif ++ (instancetype)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection + containerSize:(CGSize)windowSize +{ + return [self traitCollectionWithUITraitCollection:traitCollection + containerSize:windowSize + fallbackContentSizeCategory:AS_UIContentSizeCategoryUnspecified()]; +} + + ++ (instancetype)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection + containerSize:(CGSize)windowSize + fallbackContentSizeCategory:(UIContentSizeCategory _Nonnull)fallbackContentSizeCategory +{ + UIDisplayGamut displayGamut; + UITraitEnvironmentLayoutDirection layoutDirection; + UIContentSizeCategory sizeCategory; + #if TARGET_OS_TV + UIUserInterfaceStyle userInterfaceStyle; + #endif + if (AS_AVAILABLE_IOS(10)) { + displayGamut = traitCollection.displayGamut; + layoutDirection = traitCollection.layoutDirection; + sizeCategory = traitCollection.preferredContentSizeCategory; + #if TARGET_OS_TV + userInterfaceStyle = traitCollection.userInterfaceStyle; + #endif + } else { + displayGamut = UIDisplayGamutSRGB; // We're on iOS 9 or lower, so this is not a P3 device. + layoutDirection = UITraitEnvironmentLayoutDirectionUnspecified; + sizeCategory = fallbackContentSizeCategory; + #if TARGET_OS_TV + userInterfaceStyle = UIUserInterfaceStyleUnspecified; + #endif + } + +#if TARGET_OS_TV + return [self traitCollectionWithHorizontalSizeClass:traitCollection.horizontalSizeClass + verticalSizeClass:traitCollection.verticalSizeClass + displayScale:traitCollection.displayScale + displayGamut:displayGamut + userInterfaceIdiom:traitCollection.userInterfaceIdiom + forceTouchCapability:traitCollection.forceTouchCapability + layoutDirection:layoutDirection + userInterfaceStyle:userInterfaceStyle + preferredContentSizeCategory:sizeCategory + containerSize:windowSize]; +#else + return [self traitCollectionWithHorizontalSizeClass:traitCollection.horizontalSizeClass + verticalSizeClass:traitCollection.verticalSizeClass + displayScale:traitCollection.displayScale + displayGamut:displayGamut + userInterfaceIdiom:traitCollection.userInterfaceIdiom + forceTouchCapability:traitCollection.forceTouchCapability + layoutDirection:layoutDirection + preferredContentSizeCategory:sizeCategory + containerSize:windowSize]; +#endif +} + +- (BOOL)isEqualToTraitCollection:(ASTraitCollection *)traitCollection +{ + if (traitCollection == nil) { + return NO; + } + + if (self == traitCollection) { + return YES; + } + + return + self.horizontalSizeClass == traitCollection.horizontalSizeClass && + self.verticalSizeClass == traitCollection.verticalSizeClass && + self.displayScale == traitCollection.displayScale && + self.displayGamut == traitCollection.displayGamut && + self.userInterfaceIdiom == traitCollection.userInterfaceIdiom && + self.forceTouchCapability == traitCollection.forceTouchCapability && + self.layoutDirection == traitCollection.layoutDirection && + #if TARGET_OS_TV + self.userInterfaceStyle == traitCollection.userInterfaceStyle && + #endif + [self.preferredContentSizeCategory isEqualToString:traitCollection.preferredContentSizeCategory] && + CGSizeEqualToSize(self.containerSize, traitCollection.containerSize); +} + +@end + +@implementation ASTraitCollection (PrimitiveTraits) + ++ (instancetype)traitCollectionWithASPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traits +{ +#if TARGET_OS_TV + return [self traitCollectionWithHorizontalSizeClass:traits.horizontalSizeClass + verticalSizeClass:traits.verticalSizeClass + displayScale:traits.displayScale + displayGamut:traits.displayGamut + userInterfaceIdiom:traits.userInterfaceIdiom + forceTouchCapability:traits.forceTouchCapability + layoutDirection:traits.layoutDirection + userInterfaceStyle:traits.userInterfaceStyle + preferredContentSizeCategory:AS_safeContentSizeCategory(traits.preferredContentSizeCategory) + containerSize:traits.containerSize]; +#else + return [self traitCollectionWithHorizontalSizeClass:traits.horizontalSizeClass + verticalSizeClass:traits.verticalSizeClass + displayScale:traits.displayScale + displayGamut:traits.displayGamut + userInterfaceIdiom:traits.userInterfaceIdiom + forceTouchCapability:traits.forceTouchCapability + layoutDirection:traits.layoutDirection + preferredContentSizeCategory:AS_safeContentSizeCategory(traits.preferredContentSizeCategory) + containerSize:traits.containerSize]; +#endif +} + +- (ASPrimitiveTraitCollection)primitiveTraitCollection +{ + return (ASPrimitiveTraitCollection) { + .horizontalSizeClass = self.horizontalSizeClass, + .verticalSizeClass = self.verticalSizeClass, + .displayScale = self.displayScale, + .displayGamut = self.displayGamut, + .userInterfaceIdiom = self.userInterfaceIdiom, + .forceTouchCapability = self.forceTouchCapability, + .layoutDirection = self.layoutDirection, +#if TARGET_OS_TV + .userInterfaceStyle = self.userInterfaceStyle, +#endif + .preferredContentSizeCategory = ASPrimitiveContentSizeCategoryMake(self.preferredContentSizeCategory), + .containerSize = self.containerSize, + }; +} + +@end + +@implementation ASTraitCollection (Deprecated) + +- (instancetype)init +{ +#if TARGET_OS_TV + return [self initWithHorizontalSizeClass:UIUserInterfaceSizeClassUnspecified + verticalSizeClass:UIUserInterfaceSizeClassUnspecified + displayScale:0 + displayGamut:UIDisplayGamutUnspecified + userInterfaceIdiom:UIUserInterfaceIdiomUnspecified + forceTouchCapability:UIForceTouchCapabilityUnknown + layoutDirection:UITraitEnvironmentLayoutDirectionUnspecified + userInterfaceStyle:UIUserInterfaceStyleUnspecified + preferredContentSizeCategory:AS_UIContentSizeCategoryUnspecified() + containerSize:CGSizeZero]; +#else + return [self initWithHorizontalSizeClass:UIUserInterfaceSizeClassUnspecified + verticalSizeClass:UIUserInterfaceSizeClassUnspecified + displayScale:0 + displayGamut:UIDisplayGamutUnspecified + userInterfaceIdiom:UIUserInterfaceIdiomUnspecified + forceTouchCapability:UIForceTouchCapabilityUnknown + layoutDirection:UITraitEnvironmentLayoutDirectionUnspecified + preferredContentSizeCategory:AS_UIContentSizeCategoryUnspecified() + containerSize:CGSizeZero]; +#endif +} + + (ASTraitCollection *)traitCollectionWithDisplayScale:(CGFloat)displayScale userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass @@ -372,128 +540,4 @@ NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection trai #endif } -+ (instancetype)traitCollectionWithASPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traits -{ -#if TARGET_OS_TV - return [self traitCollectionWithHorizontalSizeClass:traits.horizontalSizeClass - verticalSizeClass:traits.verticalSizeClass - displayScale:traits.displayScale - displayGamut:traits.displayGamut - userInterfaceIdiom:traits.userInterfaceIdiom - forceTouchCapability:traits.forceTouchCapability - layoutDirection:traits.layoutDirection - userInterfaceStyle:traits.userInterfaceStyle - preferredContentSizeCategory:(UIContentSizeCategory)traits.preferredContentSizeCategory - containerSize:traits.containerSize]; -#else - return [self traitCollectionWithHorizontalSizeClass:traits.horizontalSizeClass - verticalSizeClass:traits.verticalSizeClass - displayScale:traits.displayScale - displayGamut:traits.displayGamut - userInterfaceIdiom:traits.userInterfaceIdiom - forceTouchCapability:traits.forceTouchCapability - layoutDirection:traits.layoutDirection - preferredContentSizeCategory:(UIContentSizeCategory)traits.preferredContentSizeCategory - containerSize:traits.containerSize]; -#endif -} - -+ (instancetype)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection - containerSize:(CGSize)windowSize -{ - return [self traitCollectionWithUITraitCollection:traitCollection - containerSize:windowSize - fallbackContentSizeCategory:AS_UIContentSizeCategoryUnspecified()]; -} - - -+ (instancetype)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection - containerSize:(CGSize)windowSize - fallbackContentSizeCategory:(UIContentSizeCategory)fallbackContentSizeCategory -{ - UIDisplayGamut displayGamut; - UITraitEnvironmentLayoutDirection layoutDirection; - UIContentSizeCategory sizeCategory; - #if TARGET_OS_TV - UIUserInterfaceStyle userInterfaceStyle; - #endif - if (AS_AVAILABLE_IOS(10)) { - displayGamut = traitCollection.displayGamut; - layoutDirection = traitCollection.layoutDirection; - sizeCategory = traitCollection.preferredContentSizeCategory; - #if TARGET_OS_TV - userInterfaceStyle = traitCollection.userInterfaceStyle; - #endif - } else { - displayGamut = UIDisplayGamutUnspecified; - layoutDirection = UITraitEnvironmentLayoutDirectionUnspecified; - sizeCategory = fallbackContentSizeCategory; - #if TARGET_OS_TV - userInterfaceStyle = UIUserInterfaceStyleUnspecified; - #endif - } - -#if TARGET_OS_TV - return [self traitCollectionWithHorizontalSizeClass:traitCollection.horizontalSizeClass - verticalSizeClass:traitCollection.verticalSizeClass - displayScale:traitCollection.displayScale - displayGamut:displayGamut - userInterfaceIdiom:traitCollection.userInterfaceIdiom - forceTouchCapability:traitCollection.forceTouchCapability - layoutDirection:layoutDirection - userInterfaceStyle:userInterfaceStyle - preferredContentSizeCategory:sizeCategory - containerSize:windowSize]; -#else - return [self traitCollectionWithHorizontalSizeClass:traitCollection.horizontalSizeClass - verticalSizeClass:traitCollection.verticalSizeClass - displayScale:traitCollection.displayScale - displayGamut:displayGamut - userInterfaceIdiom:traitCollection.userInterfaceIdiom - forceTouchCapability:traitCollection.forceTouchCapability - layoutDirection:layoutDirection - preferredContentSizeCategory:sizeCategory - containerSize:windowSize]; -#endif -} - -- (ASPrimitiveTraitCollection)primitiveTraitCollection -{ - return (ASPrimitiveTraitCollection) { - .horizontalSizeClass = self.horizontalSizeClass, - .verticalSizeClass = self.verticalSizeClass, - .displayScale = self.displayScale, - .displayGamut = self.displayGamut, - .userInterfaceIdiom = self.userInterfaceIdiom, - .forceTouchCapability = self.forceTouchCapability, - .layoutDirection = self.layoutDirection, - #if TARGET_OS_TV - .userInterfaceStyle = self.userInterfaceStyle, - #endif - .preferredContentSizeCategory = ASPrimitiveContentSizeCategoryMake(self.preferredContentSizeCategory), - .containerSize = self.containerSize, - }; -} - -- (BOOL)isEqualToTraitCollection:(ASTraitCollection *)traitCollection -{ - if (self == traitCollection) { - return YES; - } - - return - self.horizontalSizeClass == traitCollection.horizontalSizeClass && - self.verticalSizeClass == traitCollection.verticalSizeClass && - self.displayScale == traitCollection.displayScale && - self.displayGamut == traitCollection.displayGamut && - self.userInterfaceIdiom == traitCollection.userInterfaceIdiom && - self.forceTouchCapability == traitCollection.forceTouchCapability && - self.layoutDirection == traitCollection.layoutDirection && - #if TARGET_OS_TV - self.userInterfaceStyle == traitCollection.userInterfaceStyle && - #endif - [self.preferredContentSizeCategory isEqualToString:traitCollection.preferredContentSizeCategory] && - CGSizeEqualToSize(self.containerSize, traitCollection.containerSize); -} - @end