From e392f832f42897da41a7c0106709b55427efcea4 Mon Sep 17 00:00:00 2001 From: Adlai Holler Date: Tue, 6 Nov 2018 08:31:09 -0800 Subject: [PATCH] Rework ASTraitCollection to Fix Warnings and Remove Boilerplate (#1211) * Clean up trait collection to fix a ton of warnings and remove code * Support Xcode 9 (iOS 11 SDK) * Hack harder --- AsyncDisplayKit.xcodeproj/project.pbxproj | 4 - Source/Base/ASAssert.h | 1 + Source/Base/ASBaseDefines.h | 12 + Source/Details/ASTraitCollection.h | 107 +---- Source/Details/ASTraitCollection.mm | 468 +++++----------------- Tests/ASTraitCollectionTests.mm | 30 -- 6 files changed, 123 insertions(+), 499 deletions(-) delete mode 100644 Tests/ASTraitCollectionTests.mm diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 2cfec88415..7566729fb7 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -101,7 +101,6 @@ 3917EBD41E9C2FC400D04A01 /* _ASCollectionReusableView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3917EBD21E9C2FC400D04A01 /* _ASCollectionReusableView.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3917EBD51E9C2FC400D04A01 /* _ASCollectionReusableView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3917EBD31E9C2FC400D04A01 /* _ASCollectionReusableView.mm */; }; 3C9C128519E616EF00E942A0 /* ASTableViewTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3C9C128419E616EF00E942A0 /* ASTableViewTests.mm */; }; - 4496D0731FA9EA6B001CC8D5 /* ASTraitCollectionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4496D0721FA9EA6B001CC8D5 /* ASTraitCollectionTests.mm */; }; 4E9127691F64157600499623 /* ASRunLoopQueueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4E9127681F64157600499623 /* ASRunLoopQueueTests.mm */; }; 509E68601B3AED8E009B9150 /* ASScrollDirection.mm in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E111B371BD7007741D0 /* ASScrollDirection.mm */; }; 509E68611B3AEDA0009B9150 /* ASAbstractLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E171B37339C007741D0 /* ASAbstractLayoutController.h */; }; @@ -672,7 +671,6 @@ 3917EBD21E9C2FC400D04A01 /* _ASCollectionReusableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASCollectionReusableView.h; sourceTree = ""; }; 3917EBD31E9C2FC400D04A01 /* _ASCollectionReusableView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = _ASCollectionReusableView.mm; sourceTree = ""; }; 3C9C128419E616EF00E942A0 /* ASTableViewTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASTableViewTests.mm; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - 4496D0721FA9EA6B001CC8D5 /* ASTraitCollectionTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ASTraitCollectionTests.mm; sourceTree = ""; }; 464052191A3F83C40061C0BA /* ASDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ASDataController.h; sourceTree = ""; }; 4640521A1A3F83C40061C0BA /* ASDataController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASDataController.mm; sourceTree = ""; }; 4640521B1A3F83C40061C0BA /* ASTableLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTableLayoutController.h; sourceTree = ""; }; @@ -1359,7 +1357,6 @@ 058D0A37195D057000B7D73C /* ASTextNodeWordKernerTests.mm */, CCE4F9BC1F0ECE5200062E4E /* ASTLayoutFixture.h */, CCE4F9BD1F0ECE5200062E4E /* ASTLayoutFixture.mm */, - 4496D0721FA9EA6B001CC8D5 /* ASTraitCollectionTests.mm */, CC0AEEA31D66316E005D1C78 /* ASUICollectionViewTests.mm */, AEEC47E31C21D3D200EC1693 /* ASVideoNodeTests.mm */, CCA221D21D6FA7EF00AF6A0F /* ASViewControllerTests.mm */, @@ -2268,7 +2265,6 @@ CCEDDDD9200C518800FFCD0A /* ASConfigurationTests.mm in Sources */, AE440175210FB7CF00B36DA2 /* ASTextKitFontSizeAdjusterTests.mm in Sources */, E51B78BF1F028ABF00E32604 /* ASLayoutFlatteningTests.mm in Sources */, - 4496D0731FA9EA6B001CC8D5 /* ASTraitCollectionTests.mm in Sources */, 29CDC2E21AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.mm in Sources */, CC583AD71EF9BDC100134156 /* NSInvocation+ASTestHelpers.mm in Sources */, CC051F1F1D7A286A006434CB /* ASCALayerTests.mm in Sources */, diff --git a/Source/Base/ASAssert.h b/Source/Base/ASAssert.h index e838c86dd1..f00ce967d6 100644 --- a/Source/Base/ASAssert.h +++ b/Source/Base/ASAssert.h @@ -63,6 +63,7 @@ #define ASDisplayNodeCAssertPositiveReal(description, num) ASDisplayNodeCAssert(num >= 0 && num <= CGFLOAT_MAX, @"%@ must be a real positive integer: %f.", description, (CGFloat)num) #define ASDisplayNodeCAssertInfOrPositiveReal(description, num) ASDisplayNodeCAssert(isinf(num) || (num >= 0 && num <= CGFLOAT_MAX), @"%@ must be infinite or a real positive integer: %f.", description, (CGFloat)num) +#define ASDisplayNodeCAssertPermanent(object) ASDisplayNodeCAssert(CFGetRetainCount((__bridge CFTypeRef)(object)) == CFGetRetainCount(kCFNull), @"Expected %s to be a permanent object.", #object) #define ASDisplayNodeErrorDomain @"ASDisplayNodeErrorDomain" #define ASDisplayNodeNonFatalErrorCode 1 diff --git a/Source/Base/ASBaseDefines.h b/Source/Base/ASBaseDefines.h index 39c82b6748..65335e275e 100644 --- a/Source/Base/ASBaseDefines.h +++ b/Source/Base/ASBaseDefines.h @@ -21,6 +21,18 @@ # define let const __auto_type #endif +/** + * Hack to support building for iOS with Xcode 9. UIUserInterfaceStyle was previously tvOS-only, + * and it was added to iOS 12. Xcode 9 (iOS 11 SDK) will flat-out refuse to build anything that + * references this enum targeting iOS, even if it's guarded with the right availability macros, + * because it thinks the entire platform isn't compatible with the enum. + */ +#if TARGET_OS_TV || (defined(__IPHONE_12_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_12_0) +#define AS_BUILD_UIUSERINTERFACESTYLE 1 +#else +#define AS_BUILD_UIUSERINTERFACESTYLE 0 +#endif + #ifdef __GNUC__ # define ASDISPLAYNODE_GNUC(major, minor) \ (__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))) diff --git a/Source/Details/ASTraitCollection.h b/Source/Details/ASTraitCollection.h index f1aa8ab187..9ecf433ecc 100644 --- a/Source/Details/ASTraitCollection.h +++ b/Source/Details/ASTraitCollection.h @@ -18,34 +18,6 @@ NS_ASSUME_NONNULL_BEGIN -#pragma mark - ASPrimitiveContentSizeCategory - -/** - * ASPrimitiveContentSizeCategory is a UIContentSizeCategory that can be used inside a struct. - * - * We need an unretained pointer because ARC can't manage struct memory. - * - * WARNING: DO NOT cast UIContentSizeCategory values to ASPrimitiveContentSizeCategory directly. - * Use ASPrimitiveContentSizeCategoryMake(UIContentSizeCategory) instead. - * This is because we make some assumptions about the lifetime of the object it points to. - * Also note that cast from ASPrimitiveContentSizeCategory to UIContentSizeCategory is always safe. - */ -typedef __unsafe_unretained UIContentSizeCategory ASPrimitiveContentSizeCategory; - -/** - * Safely casts from UIContentSizeCategory to ASPrimitiveContentSizeCategory. - * - * The UIKit documentation doesn't specify if we can receive a copy of the UIContentSizeCategory constant. While getting - * copies is fine with ARC, usage of unretained pointers requires us to ensure the lifetime of the object it points to. - * Manual retain&release of the UIContentSizeCategory object is not an option because it would require us to do that - * everywhere ASPrimitiveTraitCollection is used. This is error-prone and can lead to crashes and memory leaks. So, we - * explicitly limit possible values of ASPrimitiveContentSizeCategory to the predetermined set of global constants with - * known lifetime. - * - * @return a pointer to one of the UIContentSizeCategory constants. - */ -AS_EXTERN ASPrimitiveContentSizeCategory ASPrimitiveContentSizeCategoryMake(UIContentSizeCategory sizeCategory); - #pragma mark - ASPrimitiveTraitCollection /** @@ -56,7 +28,9 @@ AS_EXTERN ASPrimitiveContentSizeCategory ASPrimitiveContentSizeCategoryMake(UICo * If you use ASPrimitiveTraitCollection, please do make sure to initialize it with ASPrimitiveTraitCollectionMakeDefault() * or ASPrimitiveTraitCollectionFromUITraitCollection(UITraitCollection*). */ -typedef struct ASPrimitiveTraitCollection { +#pragma clang diagnostic push +#pragma clang diagnostic warning "-Wpadded" +typedef struct { UIUserInterfaceSizeClass horizontalSizeClass; UIUserInterfaceSizeClass verticalSizeClass; @@ -66,14 +40,16 @@ typedef struct ASPrimitiveTraitCollection { UIUserInterfaceIdiom userInterfaceIdiom; UIForceTouchCapability forceTouchCapability; UITraitEnvironmentLayoutDirection layoutDirection API_AVAILABLE(ios(10.0)); -#if TARGET_OS_TV - UIUserInterfaceStyle userInterfaceStyle; +#if AS_BUILD_UIUSERINTERFACESTYLE + UIUserInterfaceStyle userInterfaceStyle API_AVAILABLE(tvos(10.0), ios(12.0)); #endif - ASPrimitiveContentSizeCategory preferredContentSizeCategory; + // NOTE: This must be a constant. We will assert. + unowned UIContentSizeCategory preferredContentSizeCategory API_AVAILABLE(ios(10.0)); CGSize containerSize; } ASPrimitiveTraitCollection; +#pragma clang diagnostic pop /** * Creates ASPrimitiveTraitCollection with default values. @@ -164,52 +140,21 @@ AS_EXTERN void ASTraitCollectionPropagateDown(id element, ASPri AS_SUBCLASSING_RESTRICTED @interface ASTraitCollection : NSObject -@property (nonatomic, readonly) UIUserInterfaceSizeClass horizontalSizeClass; -@property (nonatomic, readonly) UIUserInterfaceSizeClass verticalSizeClass; +@property (readonly) UIUserInterfaceSizeClass horizontalSizeClass; +@property (readonly) UIUserInterfaceSizeClass verticalSizeClass; -@property (nonatomic, readonly) CGFloat displayScale; -@property (nonatomic, readonly) UIDisplayGamut displayGamut API_AVAILABLE(ios(10.0)); +@property (readonly) CGFloat displayScale; +@property (readonly) UIDisplayGamut displayGamut API_AVAILABLE(ios(10.0)); -@property (nonatomic, readonly) UIUserInterfaceIdiom userInterfaceIdiom; -@property (nonatomic, readonly) UIForceTouchCapability forceTouchCapability; -@property (nonatomic, readonly) UITraitEnvironmentLayoutDirection layoutDirection API_AVAILABLE(ios(10.0)); -#if TARGET_OS_TV -@property (nonatomic, readonly) UIUserInterfaceStyle userInterfaceStyle; +@property (readonly) UIUserInterfaceIdiom userInterfaceIdiom; +@property (readonly) UIForceTouchCapability forceTouchCapability; +@property (readonly) UITraitEnvironmentLayoutDirection layoutDirection API_AVAILABLE(ios(10.0)); +#if AS_BUILD_UIUSERINTERFACESTYLE +@property (readonly) UIUserInterfaceStyle userInterfaceStyle API_AVAILABLE(tvos(10.0), ios(12.0)); #endif +@property (readonly) UIContentSizeCategory preferredContentSizeCategory API_AVAILABLE(ios(10.0)); -@property (nonatomic, readonly) UIContentSizeCategory preferredContentSizeCategory; - -@property (nonatomic, readonly) CGSize containerSize; - -+ (ASTraitCollection *)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection - containerSize:(CGSize)windowSize NS_RETURNS_RETAINED; - -+ (ASTraitCollection *)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection - containerSize:(CGSize)windowSize - fallbackContentSizeCategory:(UIContentSizeCategory)fallbackContentSizeCategory NS_RETURNS_RETAINED; - -#if TARGET_OS_TV -+ (ASTraitCollection *)traitCollectionWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass - verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass - displayScale:(CGFloat)displayScale - displayGamut:(UIDisplayGamut)displayGamut - userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom - forceTouchCapability:(UIForceTouchCapability)forceTouchCapability - layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection - userInterfaceStyle:(UIUserInterfaceStyle)userInterfaceStyle - preferredContentSizeCategory:(UIContentSizeCategory)preferredContentSizeCategory - containerSize:(CGSize)windowSize NS_RETURNS_RETAINED; -#else -+ (ASTraitCollection *)traitCollectionWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass - verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass - displayScale:(CGFloat)displayScale - displayGamut:(UIDisplayGamut)displayGamut - userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom - forceTouchCapability:(UIForceTouchCapability)forceTouchCapability - layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection - preferredContentSizeCategory:(UIContentSizeCategory)preferredContentSizeCategory - containerSize:(CGSize)windowSize NS_RETURNS_RETAINED; -#endif +@property (readonly) CGSize containerSize; - (BOOL)isEqualToTraitCollection:(ASTraitCollection *)traitCollection; @@ -226,18 +171,4 @@ AS_SUBCLASSING_RESTRICTED @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 - verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass - forceTouchCapability:(UIForceTouchCapability)forceTouchCapability - containerSize:(CGSize)windowSize - NS_RETURNS_RETAINED ASDISPLAYNODE_DEPRECATED_MSG("Use full version of this method instead."); - -@end - NS_ASSUME_NONNULL_END diff --git a/Source/Details/ASTraitCollection.mm b/Source/Details/ASTraitCollection.mm index 8e49221782..7058501aac 100644 --- a/Source/Details/ASTraitCollection.mm +++ b/Source/Details/ASTraitCollection.mm @@ -8,68 +8,11 @@ // #import +#import #import #import #import -#pragma mark - ASPrimitiveContentSizeCategory - -// UIContentSizeCategoryUnspecified is available only in iOS 10.0 and later. -// This is used for compatibility with older iOS versions. -ASDISPLAYNODE_INLINE UIContentSizeCategory AS_UIContentSizeCategoryUnspecified() { - if (AS_AVAILABLE_IOS(10)) { - return UIContentSizeCategoryUnspecified; - } else { - return @"_UICTContentSizeCategoryUnspecified"; - } -} - -ASDISPLAYNODE_INLINE UIContentSizeCategory _Nonnull AS_safeContentSizeCategory(UIContentSizeCategory _Nullable sizeCategory) { - return sizeCategory ? sizeCategory : AS_UIContentSizeCategoryUnspecified(); -} - -ASPrimitiveContentSizeCategory ASPrimitiveContentSizeCategoryMake(UIContentSizeCategory sizeCategory) { - if ([sizeCategory isEqualToString:UIContentSizeCategoryExtraSmall]) { - return UIContentSizeCategoryExtraSmall; - } - if ([sizeCategory isEqualToString:UIContentSizeCategorySmall]) { - return UIContentSizeCategorySmall; - } - if ([sizeCategory isEqualToString:UIContentSizeCategoryMedium]) { - return UIContentSizeCategoryMedium; - } - if ([sizeCategory isEqualToString:UIContentSizeCategoryLarge]) { - return UIContentSizeCategoryLarge; - } - if ([sizeCategory isEqualToString:UIContentSizeCategoryExtraLarge]) { - return UIContentSizeCategoryExtraLarge; - } - if ([sizeCategory isEqualToString:UIContentSizeCategoryExtraExtraLarge]) { - return UIContentSizeCategoryExtraExtraLarge; - } - if ([sizeCategory isEqualToString:UIContentSizeCategoryExtraExtraExtraLarge]) { - return UIContentSizeCategoryExtraExtraExtraLarge; - } - - if ([sizeCategory isEqualToString:UIContentSizeCategoryAccessibilityMedium]) { - return UIContentSizeCategoryAccessibilityMedium; - } - if ([sizeCategory isEqualToString:UIContentSizeCategoryAccessibilityLarge]) { - return UIContentSizeCategoryAccessibilityLarge; - } - if ([sizeCategory isEqualToString:UIContentSizeCategoryAccessibilityExtraLarge]) { - return UIContentSizeCategoryAccessibilityExtraLarge; - } - if ([sizeCategory isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraLarge]) { - return UIContentSizeCategoryAccessibilityExtraExtraLarge; - } - if ([sizeCategory isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraExtraLarge]) { - return UIContentSizeCategoryAccessibilityExtraExtraExtraLarge; - } - - return AS_UIContentSizeCategoryUnspecified(); -} - #pragma mark - ASPrimitiveTraitCollection void ASTraitCollectionPropagateDown(id element, ASPrimitiveTraitCollection traitCollection) { @@ -83,14 +26,21 @@ void ASTraitCollectionPropagateDown(id element, ASPrimitiveTrai } ASPrimitiveTraitCollection ASPrimitiveTraitCollectionMakeDefault() { - return (ASPrimitiveTraitCollection) { - // Default values can be defined in here - .displayGamut = UIDisplayGamutUnspecified, - .userInterfaceIdiom = UIUserInterfaceIdiomUnspecified, - .layoutDirection = UITraitEnvironmentLayoutDirectionUnspecified, - .preferredContentSizeCategory = ASPrimitiveContentSizeCategoryMake(AS_UIContentSizeCategoryUnspecified()), - .containerSize = CGSizeZero, - }; + ASPrimitiveTraitCollection tc; + tc.horizontalSizeClass = UIUserInterfaceSizeClassUnspecified; + tc.verticalSizeClass = UIUserInterfaceSizeClassUnspecified; + tc.containerSize = CGSizeZero; + if (AS_AVAILABLE_IOS(10)) { + tc.displayGamut = UIDisplayGamutUnspecified; + tc.preferredContentSizeCategory = UIContentSizeCategoryUnspecified; + tc.layoutDirection = UITraitEnvironmentLayoutDirectionUnspecified; + } +#if AS_BUILD_UIUSERINTERFACESTYLE + if (AS_AVAILABLE_IOS_TVOS(12, 10)) { + tc.userInterfaceStyle = UIUserInterfaceStyleUnspecified; + } +#endif + return tc; } ASPrimitiveTraitCollection ASPrimitiveTraitCollectionFromUITraitCollection(UITraitCollection *traitCollection) { @@ -104,38 +54,19 @@ ASPrimitiveTraitCollection ASPrimitiveTraitCollectionFromUITraitCollection(UITra environmentTraitCollection.displayGamut = traitCollection.displayGamut; environmentTraitCollection.layoutDirection = traitCollection.layoutDirection; - // preferredContentSizeCategory is also available on older iOS versions, but only via UIApplication class. - // It should be noted that [UIApplication sharedApplication] is unavailable because Texture is built with only extension-safe API. - environmentTraitCollection.preferredContentSizeCategory = ASPrimitiveContentSizeCategoryMake(traitCollection.preferredContentSizeCategory); - - #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. + ASDisplayNodeCAssertPermanent(traitCollection.preferredContentSizeCategory); + environmentTraitCollection.preferredContentSizeCategory = traitCollection.preferredContentSizeCategory; } +#if AS_BUILD_UIUSERINTERFACESTYLE + if (AS_AVAILABLE_IOS_TVOS(12, 10)) { + environmentTraitCollection.userInterfaceStyle = traitCollection.userInterfaceStyle; + } +#endif return environmentTraitCollection; } BOOL ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(ASPrimitiveTraitCollection lhs, ASPrimitiveTraitCollection rhs) { - UIContentSizeCategory leftSizeCategory = AS_safeContentSizeCategory(lhs.preferredContentSizeCategory); - UIContentSizeCategory rightSizeCategory = AS_safeContentSizeCategory(rhs.preferredContentSizeCategory); - - return - lhs.verticalSizeClass == rhs.verticalSizeClass && - lhs.horizontalSizeClass == rhs.horizontalSizeClass && - lhs.displayScale == rhs.displayScale && - lhs.displayGamut == rhs.displayGamut && - lhs.userInterfaceIdiom == rhs.userInterfaceIdiom && - lhs.forceTouchCapability == rhs.forceTouchCapability && - lhs.layoutDirection == rhs.layoutDirection && - #if TARGET_OS_TV - lhs.userInterfaceStyle == rhs.userInterfaceStyle && - #endif - - [leftSizeCategory isEqualToString:rightSizeCategory] && // Simple pointer comparison should be sufficient here - - CGSizeEqualToSize(lhs.containerSize, rhs.containerSize); + return !memcmp(&lhs, &rhs, sizeof(ASPrimitiveTraitCollection)); } // Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline @@ -179,6 +110,7 @@ ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIUserInterfaceSizeClass(UIUserInt } // Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline +API_AVAILABLE(ios(10)) ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIDisplayGamut(UIDisplayGamut displayGamut) { switch (displayGamut) { case UIDisplayGamutSRGB: @@ -191,6 +123,7 @@ ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIDisplayGamut(UIDisplayGamut disp } // Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline +API_AVAILABLE(ios(10)) ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUITraitEnvironmentLayoutDirection(UITraitEnvironmentLayoutDirection layoutDirection) { switch (layoutDirection) { case UITraitEnvironmentLayoutDirectionLeftToRight: @@ -202,8 +135,9 @@ ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUITraitEnvironmentLayoutDirection( } } -#if TARGET_OS_TV // Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline +#if AS_BUILD_UIUSERINTERFACESTYLE +API_AVAILABLE(tvos(10.0), ios(12.0)) ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIUserInterfaceStyle(UIUserInterfaceStyle userInterfaceStyle) { switch (userInterfaceStyle) { case UIUserInterfaceStyleLight: @@ -221,181 +155,83 @@ NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection trai [props addObject:@{ @"verticalSizeClass": AS_NSStringFromUIUserInterfaceSizeClass(traits.verticalSizeClass) }]; [props addObject:@{ @"horizontalSizeClass": AS_NSStringFromUIUserInterfaceSizeClass(traits.horizontalSizeClass) }]; [props addObject:@{ @"displayScale": [NSString stringWithFormat: @"%.0lf", (double)traits.displayScale] }]; - [props addObject:@{ @"displayGamut": AS_NSStringFromUIDisplayGamut(traits.displayGamut) }]; [props addObject:@{ @"userInterfaceIdiom": AS_NSStringFromUIUserInterfaceIdiom(traits.userInterfaceIdiom) }]; [props addObject:@{ @"forceTouchCapability": AS_NSStringFromUIForceTouchCapability(traits.forceTouchCapability) }]; - [props addObject:@{ @"layoutDirection": AS_NSStringFromUITraitEnvironmentLayoutDirection(traits.layoutDirection) }]; - #if TARGET_OS_TV +#if AS_BUILD_UIUSERINTERFACESTYLE + if (AS_AVAILABLE_IOS_TVOS(12, 10)) { [props addObject:@{ @"userInterfaceStyle": AS_NSStringFromUIUserInterfaceStyle(traits.userInterfaceStyle) }]; - #endif - [props addObject:@{ @"preferredContentSizeCategory": AS_safeContentSizeCategory(traits.preferredContentSizeCategory) }]; + } +#endif + if (AS_AVAILABLE_IOS(10)) { + [props addObject:@{ @"layoutDirection": AS_NSStringFromUITraitEnvironmentLayoutDirection(traits.layoutDirection) }]; + [props addObject:@{ @"preferredContentSizeCategory": traits.preferredContentSizeCategory }]; + [props addObject:@{ @"displayGamut": AS_NSStringFromUIDisplayGamut(traits.displayGamut) }]; + } [props addObject:@{ @"containerSize": NSStringFromCGSize(traits.containerSize) }]; return ASObjectDescriptionMakeWithoutObject(props); } #pragma mark - ASTraitCollection -@implementation ASTraitCollection - -#if TARGET_OS_TV - -- (instancetype)initWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass - verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass - displayScale:(CGFloat)displayScale - displayGamut:(UIDisplayGamut)displayGamut - userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom - forceTouchCapability:(UIForceTouchCapability)forceTouchCapability - layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection - userInterfaceStyle:(UIUserInterfaceStyle)userInterfaceStyle - preferredContentSizeCategory:(UIContentSizeCategory _Nonnull)preferredContentSizeCategory - containerSize:(CGSize)windowSize -{ - self = [super init]; - if (self) { - _horizontalSizeClass = horizontalSizeClass; - _verticalSizeClass = verticalSizeClass; - _displayScale = displayScale; - _displayGamut = displayGamut; - _userInterfaceIdiom = userInterfaceIdiom; - _forceTouchCapability = forceTouchCapability; - _layoutDirection = layoutDirection; - _userInterfaceStyle = userInterfaceStyle; - _preferredContentSizeCategory = AS_safeContentSizeCategory(preferredContentSizeCategory); // guard against misuse - _containerSize = windowSize; - } - return self; +@implementation ASTraitCollection { + ASPrimitiveTraitCollection _prim; } -+ (instancetype)traitCollectionWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass - verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass - displayScale:(CGFloat)displayScale - displayGamut:(UIDisplayGamut)displayGamut - userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom - forceTouchCapability:(UIForceTouchCapability)forceTouchCapability - layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection - userInterfaceStyle:(UIUserInterfaceStyle)userInterfaceStyle - preferredContentSizeCategory:(UIContentSizeCategory _Nonnull)preferredContentSizeCategory - containerSize:(CGSize)windowSize NS_RETURNS_RETAINED -{ - return [[self alloc] initWithHorizontalSizeClass:horizontalSizeClass - verticalSizeClass:verticalSizeClass - displayScale:displayScale - displayGamut:displayGamut - userInterfaceIdiom:userInterfaceIdiom - forceTouchCapability:forceTouchCapability - layoutDirection:layoutDirection - userInterfaceStyle:userInterfaceStyle - preferredContentSizeCategory:preferredContentSizeCategory - containerSize:windowSize]; -} - -#else - -- (instancetype)initWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass - verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass - displayScale:(CGFloat)displayScale - displayGamut:(UIDisplayGamut)displayGamut - userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom - forceTouchCapability:(UIForceTouchCapability)forceTouchCapability - layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection - preferredContentSizeCategory:(UIContentSizeCategory _Nonnull)preferredContentSizeCategory - containerSize:(CGSize)windowSize -{ - self = [super init]; - if (self) { - _horizontalSizeClass = horizontalSizeClass; - _verticalSizeClass = verticalSizeClass; - _displayScale = displayScale; - _displayGamut = displayGamut; - _userInterfaceIdiom = userInterfaceIdiom; - _forceTouchCapability = forceTouchCapability; - _layoutDirection = layoutDirection; - _preferredContentSizeCategory = AS_safeContentSizeCategory(preferredContentSizeCategory); // guard against misuse - _containerSize = windowSize; - } - return self; -} - -+ (instancetype)traitCollectionWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass - verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass - displayScale:(CGFloat)displayScale - displayGamut:(UIDisplayGamut)displayGamut - userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom - forceTouchCapability:(UIForceTouchCapability)forceTouchCapability - layoutDirection:(UITraitEnvironmentLayoutDirection)layoutDirection - preferredContentSizeCategory:(UIContentSizeCategory _Nonnull)preferredContentSizeCategory - containerSize:(CGSize)windowSize NS_RETURNS_RETAINED -{ - return [[self alloc] initWithHorizontalSizeClass:horizontalSizeClass - verticalSizeClass:verticalSizeClass - displayScale:displayScale - displayGamut:displayGamut - userInterfaceIdiom:userInterfaceIdiom - forceTouchCapability:forceTouchCapability - layoutDirection:layoutDirection - preferredContentSizeCategory:preferredContentSizeCategory - containerSize:windowSize]; -} - -#endif - -+ (instancetype)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection - containerSize:(CGSize)windowSize NS_RETURNS_RETAINED -{ - return [self traitCollectionWithUITraitCollection:traitCollection - containerSize:windowSize - fallbackContentSizeCategory:AS_UIContentSizeCategoryUnspecified()]; -} - - -+ (instancetype)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection - containerSize:(CGSize)windowSize - fallbackContentSizeCategory:(UIContentSizeCategory _Nonnull)fallbackContentSizeCategory NS_RETURNS_RETAINED -{ - UIDisplayGamut displayGamut; - UITraitEnvironmentLayoutDirection layoutDirection; - UIContentSizeCategory sizeCategory; - #if TARGET_OS_TV - UIUserInterfaceStyle userInterfaceStyle; - #endif ++ (ASTraitCollection *)traitCollectionWithASPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traits NS_RETURNS_RETAINED { + ASTraitCollection *tc = [[ASTraitCollection alloc] init]; 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 + ASDisplayNodeCAssertPermanent(traits.preferredContentSizeCategory); } + tc->_prim = traits; + return tc; +} -#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]; +- (ASPrimitiveTraitCollection)primitiveTraitCollection { + return _prim; +} +- (UIUserInterfaceSizeClass)horizontalSizeClass +{ + return _prim.horizontalSizeClass; +} +-(UIUserInterfaceSizeClass)verticalSizeClass +{ + return _prim.verticalSizeClass; +} +- (CGFloat)displayScale +{ + return _prim.displayScale; +} +- (UIDisplayGamut)displayGamut +{ + return _prim.displayGamut; +} +- (UIForceTouchCapability)forceTouchCapability +{ + return _prim.forceTouchCapability; +} +- (UITraitEnvironmentLayoutDirection)layoutDirection +{ + return _prim.layoutDirection; +} +#if AS_BUILD_UIUSERINTERFACESTYLE +- (UIUserInterfaceStyle)userInterfaceStyle +{ + return _prim.userInterfaceStyle; +} #endif +- (UIContentSizeCategory)preferredContentSizeCategory +{ + return _prim.preferredContentSizeCategory; +} +- (NSUInteger)hash { + return ASHashBytes(&_prim, sizeof(ASPrimitiveTraitCollection)); +} + +- (BOOL)isEqual:(id)object { + if (!object || ![object isKindOfClass:ASTraitCollection.class]) { + return NO; + } + return [self isEqualToTraitCollection:object]; } - (BOOL)isEqualToTraitCollection:(ASTraitCollection *)traitCollection @@ -407,129 +243,7 @@ NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection trai 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 NS_RETURNS_RETAINED -{ -#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 - verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass - forceTouchCapability:(UIForceTouchCapability)forceTouchCapability - containerSize:(CGSize)windowSize NS_RETURNS_RETAINED -{ -#if TARGET_OS_TV - return [self traitCollectionWithHorizontalSizeClass:horizontalSizeClass - verticalSizeClass:verticalSizeClass - displayScale:displayScale - displayGamut:UIDisplayGamutUnspecified - userInterfaceIdiom:userInterfaceIdiom - forceTouchCapability:forceTouchCapability - layoutDirection:UITraitEnvironmentLayoutDirectionUnspecified - userInterfaceStyle:UIUserInterfaceStyleUnspecified - preferredContentSizeCategory:AS_UIContentSizeCategoryUnspecified() - containerSize:windowSize]; -#else - return [self traitCollectionWithHorizontalSizeClass:horizontalSizeClass - verticalSizeClass:verticalSizeClass - displayScale:displayScale - displayGamut:UIDisplayGamutUnspecified - userInterfaceIdiom:userInterfaceIdiom - forceTouchCapability:forceTouchCapability - layoutDirection:UITraitEnvironmentLayoutDirectionUnspecified - preferredContentSizeCategory:AS_UIContentSizeCategoryUnspecified() - containerSize:windowSize]; -#endif + return ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(_prim, traitCollection->_prim); } @end diff --git a/Tests/ASTraitCollectionTests.mm b/Tests/ASTraitCollectionTests.mm deleted file mode 100644 index e6141d6c3a..0000000000 --- a/Tests/ASTraitCollectionTests.mm +++ /dev/null @@ -1,30 +0,0 @@ -// -// ASTraitCollectionTests.mm -// Texture -// -// Copyright (c) Pinterest, Inc. All rights reserved. -// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0 -// - -#import -#import - -@interface ASTraitCollectionTests : XCTestCase - -@end - -@implementation ASTraitCollectionTests - -- (void)testPrimitiveContentSizeCategoryLifetime -{ - ASPrimitiveContentSizeCategory primitiveContentSize; - @autoreleasepool { - // Make sure the compiler won't optimize string alloc/dealloc - NSString *contentSizeCategory = [NSString stringWithCString:"UICTContentSizeCategoryL" encoding:NSUTF8StringEncoding]; - primitiveContentSize = ASPrimitiveContentSizeCategoryMake(contentSizeCategory); - } - - XCTAssertEqual(primitiveContentSize, UIContentSizeCategoryLarge); -} - -@end