[ASTraitCollection] Add missing properties to ASTraitCollection (#625)

* [ASTraitCollection] Add missing properties to ASTraitCollection

* ASTraitCollection now completely reflects UITraitCollection

* Add ASContentSizeCategory enum that corresponds to
  UIContentSizeCategory and can be used inside a struct.

* * Remove enum ASContentSizeCategory.
* Use __unsafe_unretained UIContentSizeCategory instead of the enum.

* Added ASPrimitiveTraitCollection lifetime test

* Changes requested at code review:
* Restore one of the ASTraitCollection constructors with a deprecation notice.
* Clean up API by the separation of tvOS-specific interfaces.
* Use [NSString -isEqualToString:] for ASPrimitiveContentSizeCategory equality tests for better readability.
* Encapsulate fallback logic for UIContentSizeCategoryUnspecified.

* Fix failing test
This commit is contained in:
Yevgen Pogribnyi 2018-01-16 20:08:29 +02:00 committed by Huy Nguyen
parent 193be32bcf
commit a3136b0225
6 changed files with 479 additions and 67 deletions

View File

@ -99,6 +99,7 @@
3917EBD41E9C2FC400D04A01 /* _ASCollectionReusableView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3917EBD21E9C2FC400D04A01 /* _ASCollectionReusableView.h */; settings = {ATTRIBUTES = (Private, ); }; };
3917EBD51E9C2FC400D04A01 /* _ASCollectionReusableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3917EBD31E9C2FC400D04A01 /* _ASCollectionReusableView.m */; };
3C9C128519E616EF00E942A0 /* ASTableViewTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3C9C128419E616EF00E942A0 /* ASTableViewTests.mm */; };
4496D0731FA9EA6B001CC8D5 /* ASTraitCollectionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4496D0721FA9EA6B001CC8D5 /* ASTraitCollectionTests.m */; };
4E9127691F64157600499623 /* ASRunLoopQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E9127681F64157600499623 /* ASRunLoopQueueTests.m */; };
509E68601B3AED8E009B9150 /* ASScrollDirection.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E111B371BD7007741D0 /* ASScrollDirection.m */; };
509E68611B3AEDA0009B9150 /* ASAbstractLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E171B37339C007741D0 /* ASAbstractLayoutController.h */; };
@ -634,6 +635,7 @@
3917EBD21E9C2FC400D04A01 /* _ASCollectionReusableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASCollectionReusableView.h; sourceTree = "<group>"; };
3917EBD31E9C2FC400D04A01 /* _ASCollectionReusableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _ASCollectionReusableView.m; sourceTree = "<group>"; };
3C9C128419E616EF00E942A0 /* ASTableViewTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASTableViewTests.mm; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
4496D0721FA9EA6B001CC8D5 /* ASTraitCollectionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASTraitCollectionTests.m; sourceTree = "<group>"; };
464052191A3F83C40061C0BA /* ASDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ASDataController.h; sourceTree = "<group>"; };
4640521A1A3F83C40061C0BA /* ASDataController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASDataController.mm; sourceTree = "<group>"; };
4640521B1A3F83C40061C0BA /* ASTableLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTableLayoutController.h; sourceTree = "<group>"; };
@ -1256,6 +1258,7 @@
695BE2541DC1245C008E6EA5 /* ASWrapperSpecSnapshotTests.mm */,
699B83501E3C1BA500433FA4 /* ASLayoutSpecTests.m */,
4E9127681F64157600499623 /* ASRunLoopQueueTests.m */,
4496D0721FA9EA6B001CC8D5 /* ASTraitCollectionTests.m */,
);
path = Tests;
sourceTree = "<group>";
@ -2163,6 +2166,7 @@
buildActionMask = 2147483647;
files = (
E51B78BF1F028ABF00E32604 /* ASLayoutFlatteningTests.m in Sources */,
4496D0731FA9EA6B001CC8D5 /* ASTraitCollectionTests.m in Sources */,
29CDC2E21AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.m in Sources */,
CC583AD71EF9BDC100134156 /* NSInvocation+ASTestHelpers.m in Sources */,
CC051F1F1D7A286A006434CB /* ASCALayerTests.m in Sources */,

View File

@ -1,5 +1,6 @@
## master
* Add your own contributions to the next release on the line below this with your name.
- [ASTraitCollection] Add new properties of UITraitCollection to ASTraitCollection. [Yevgen Pogribnyi](https://github.com/ypogribnyi)
- [ASRectMap] Replace implementation of ASRectTable with a simpler one based on unordered_map.[Scott Goodson](https://github.com/appleguy) [#719](https://github.com/TextureGroup/Texture/pull/719)
- [ASCollectionView] Add missing flags for ASCollectionDelegate [Ilya Zheleznikov](https://github.com/ilyailya) [#718](https://github.com/TextureGroup/Texture/pull/718)
- [ASNetworkImageNode] Deprecates .URLs in favor of .URL [Garrett Moon](https://github.com/garrettmoon) [#699](https://github.com/TextureGroup/Texture/pull/699)

View File

@ -17,6 +17,7 @@
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
@class ASTraitCollection;
@ -27,14 +28,51 @@ NS_ASSUME_NONNULL_BEGIN
ASDISPLAYNODE_EXTERN_C_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.
*/
extern ASPrimitiveContentSizeCategory ASPrimitiveContentSizeCategoryMake(UIContentSizeCategory sizeCategory);
#pragma mark - ASPrimitiveTraitCollection
typedef struct ASPrimitiveTraitCollection {
CGFloat displayScale;
UIUserInterfaceSizeClass horizontalSizeClass;
UIUserInterfaceIdiom userInterfaceIdiom;
UIUserInterfaceSizeClass verticalSizeClass;
CGFloat displayScale;
UIDisplayGamut displayGamut;
UIUserInterfaceIdiom userInterfaceIdiom;
UIForceTouchCapability forceTouchCapability;
UITraitEnvironmentLayoutDirection layoutDirection;
#if TARGET_OS_TV
UIUserInterfaceStyle userInterfaceStyle;
#endif
ASPrimitiveContentSizeCategory preferredContentSizeCategory;
CGSize containerSize;
} ASPrimitiveTraitCollection;
@ -124,11 +162,21 @@ ASDISPLAYNODE_EXTERN_C_END
AS_SUBCLASSING_RESTRICTED
@interface ASTraitCollection : NSObject
@property (nonatomic, assign, readonly) CGFloat displayScale;
@property (nonatomic, assign, readonly) UIUserInterfaceSizeClass horizontalSizeClass;
@property (nonatomic, assign, readonly) UIUserInterfaceIdiom userInterfaceIdiom;
@property (nonatomic, assign, readonly) UIUserInterfaceSizeClass verticalSizeClass;
@property (nonatomic, assign, readonly) CGFloat displayScale;
@property (nonatomic, assign, readonly) UIDisplayGamut displayGamut;
@property (nonatomic, assign, readonly) UIUserInterfaceIdiom userInterfaceIdiom;
@property (nonatomic, assign, readonly) UIForceTouchCapability forceTouchCapability;
@property (nonatomic, assign, readonly) UITraitEnvironmentLayoutDirection layoutDirection;
#if TARGET_OS_TV
@property (nonatomic, assign, readonly) UIUserInterfaceStyle userInterfaceStyle;
#endif
@property (nonatomic, assign, readonly) UIContentSizeCategory preferredContentSizeCategory;
@property (nonatomic, assign, readonly) CGSize containerSize;
+ (ASTraitCollection *)traitCollectionWithASPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traits;
@ -136,18 +184,48 @@ AS_SUBCLASSING_RESTRICTED
+ (ASTraitCollection *)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection
containerSize:(CGSize)windowSize;
+ (ASTraitCollection *)traitCollectionWithUITraitCollection:(UITraitCollection *)traitCollection
containerSize:(CGSize)windowSize
fallbackContentSizeCategory:(UIContentSizeCategory)fallbackContentSizeCategory;
+ (ASTraitCollection *)traitCollectionWithDisplayScale:(CGFloat)displayScale
userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom
horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
forceTouchCapability:(UIForceTouchCapability)forceTouchCapability
containerSize:(CGSize)windowSize;
#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;
#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;
#endif
- (ASPrimitiveTraitCollection)primitiveTraitCollection;
- (BOOL)isEqualToTraitCollection:(ASTraitCollection *)traitCollection;
@end
@interface ASTraitCollection (Deprecated)
+ (ASTraitCollection *)traitCollectionWithDisplayScale:(CGFloat)displayScale
userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom
horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
forceTouchCapability:(UIForceTouchCapability)forceTouchCapability
containerSize:(CGSize)windowSize
ASDISPLAYNODE_DEPRECATED_MSG("Use full version of this method instead.");
@end
NS_ASSUME_NONNULL_END

View File

@ -20,6 +20,60 @@
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
#import <AsyncDisplayKit/ASLayoutElement.h>
#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";
}
}
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
extern void ASTraitCollectionPropagateDown(id<ASLayoutElement> element, ASPrimitiveTraitCollection traitCollection) {
@ -32,36 +86,57 @@ extern void ASTraitCollectionPropagateDown(id<ASLayoutElement> element, ASPrimit
}
}
ASPrimitiveTraitCollection ASPrimitiveTraitCollectionMakeDefault()
{
ASPrimitiveTraitCollection ASPrimitiveTraitCollectionMakeDefault() {
return (ASPrimitiveTraitCollection) {
// Default values can be defined in here
.displayGamut = UIDisplayGamutUnspecified,
.userInterfaceIdiom = UIUserInterfaceIdiomUnspecified,
.layoutDirection = UITraitEnvironmentLayoutDirectionUnspecified,
.preferredContentSizeCategory = ASPrimitiveContentSizeCategoryMake(AS_UIContentSizeCategoryUnspecified()),
.containerSize = CGSizeZero,
};
}
ASPrimitiveTraitCollection ASPrimitiveTraitCollectionFromUITraitCollection(UITraitCollection *traitCollection)
{
ASPrimitiveTraitCollection ASPrimitiveTraitCollectionFromUITraitCollection(UITraitCollection *traitCollection) {
ASPrimitiveTraitCollection environmentTraitCollection = ASPrimitiveTraitCollectionMakeDefault();
environmentTraitCollection.displayScale = traitCollection.displayScale;
environmentTraitCollection.horizontalSizeClass = traitCollection.horizontalSizeClass;
environmentTraitCollection.verticalSizeClass = traitCollection.verticalSizeClass;
environmentTraitCollection.displayScale = traitCollection.displayScale;
environmentTraitCollection.userInterfaceIdiom = traitCollection.userInterfaceIdiom;
if (AS_AVAILABLE_IOS(9)) {
environmentTraitCollection.forceTouchCapability = traitCollection.forceTouchCapability;
environmentTraitCollection.forceTouchCapability = traitCollection.forceTouchCapability;
if (AS_AVAILABLE_IOS(10)) {
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
}
return environmentTraitCollection;
}
BOOL ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(ASPrimitiveTraitCollection lhs, ASPrimitiveTraitCollection rhs)
{
BOOL ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(ASPrimitiveTraitCollection lhs, ASPrimitiveTraitCollection rhs) {
UIContentSizeCategory leftSizeCategory = (UIContentSizeCategory)lhs.preferredContentSizeCategory;
UIContentSizeCategory rightSizeCategory = (UIContentSizeCategory)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);
}
@ -105,14 +180,58 @@ ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIUserInterfaceSizeClass(UIUserInt
}
}
NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection traits)
{
// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline
ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIDisplayGamut(UIDisplayGamut displayGamut) {
switch (displayGamut) {
case UIDisplayGamutSRGB:
return @"sRGB";
case UIDisplayGamutP3:
return @"P3";
default:
return @"Unspecified";
}
}
// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline
ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUITraitEnvironmentLayoutDirection(UITraitEnvironmentLayoutDirection layoutDirection) {
switch (layoutDirection) {
case UITraitEnvironmentLayoutDirectionLeftToRight:
return @"LeftToRight";
case UITraitEnvironmentLayoutDirectionRightToLeft:
return @"RightToLeft";
default:
return @"Unspecified";
}
}
#if TARGET_OS_TV
// Named so as not to conflict with a hidden Apple function, in case compiler decides not to inline
ASDISPLAYNODE_INLINE NSString *AS_NSStringFromUIUserInterfaceStyle(UIUserInterfaceStyle userInterfaceStyle) {
switch (userInterfaceStyle) {
case UIUserInterfaceStyleLight:
return @"Light";
case UIUserInterfaceStyleDark:
return @"Dark";
default:
return @"Unspecified";
}
}
#endif
NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection traits) {
NSMutableArray<NSDictionary *> *props = [NSMutableArray array];
[props addObject:@{ @"userInterfaceIdiom": AS_NSStringFromUIUserInterfaceIdiom(traits.userInterfaceIdiom) }];
[props addObject:@{ @"containerSize": NSStringFromCGSize(traits.containerSize) }];
[props addObject:@{ @"horizontalSizeClass": AS_NSStringFromUIUserInterfaceSizeClass(traits.horizontalSizeClass) }];
[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
[props addObject:@{ @"userInterfaceStyle": AS_NSStringFromUIUserInterfaceStyle(traits.userInterfaceStyle) }];
#endif
[props addObject:@{ @"preferredContentSizeCategory": (UIContentSizeCategory)traits.preferredContentSizeCategory }];
[props addObject:@{ @"containerSize": NSStringFromCGSize(traits.containerSize) }];
return ASObjectDescriptionMakeWithoutObject(props);
}
@ -120,69 +239,238 @@ NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection trai
@implementation ASTraitCollection
- (instancetype)initWithDisplayScale:(CGFloat)displayScale
userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom
horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
forceTouchCapability:(UIForceTouchCapability)forceTouchCapability
containerSize:(CGSize)windowSize
#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)preferredContentSizeCategory
containerSize:(CGSize)windowSize
{
self = [super init];
if (self) {
_displayScale = displayScale;
_userInterfaceIdiom = userInterfaceIdiom;
_horizontalSizeClass = horizontalSizeClass;
_verticalSizeClass = verticalSizeClass;
_displayScale = displayScale;
_displayGamut = displayGamut;
_userInterfaceIdiom = userInterfaceIdiom;
_forceTouchCapability = forceTouchCapability;
_layoutDirection = layoutDirection;
_userInterfaceStyle = userInterfaceStyle;
_preferredContentSizeCategory = preferredContentSizeCategory;
_containerSize = windowSize;
}
return self;
}
+ (instancetype)traitCollectionWithDisplayScale:(CGFloat)displayScale
userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom
horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
forceTouchCapability:(UIForceTouchCapability)forceTouchCapability
containerSize:(CGSize)windowSize
+ (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)preferredContentSizeCategory
containerSize:(CGSize)windowSize
{
return [[self alloc] initWithDisplayScale:displayScale
userInterfaceIdiom:userInterfaceIdiom
horizontalSizeClass:horizontalSizeClass
verticalSizeClass:verticalSizeClass
forceTouchCapability:forceTouchCapability
containerSize:windowSize];
return [[self alloc] initWithHorizontalSizeClass:horizontalSizeClass
verticalSizeClass:verticalSizeClass
displayScale:displayScale
displayGamut:displayGamut
userInterfaceIdiom:userInterfaceIdiom
forceTouchCapability:forceTouchCapability
layoutDirection:layoutDirection
userInterfaceStyle:userIntefaceStyle
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)preferredContentSizeCategory
containerSize:(CGSize)windowSize
{
self = [super init];
if (self) {
_horizontalSizeClass = horizontalSizeClass;
_verticalSizeClass = verticalSizeClass;
_displayScale = displayScale;
_displayGamut = displayGamut;
_userInterfaceIdiom = userInterfaceIdiom;
_forceTouchCapability = forceTouchCapability;
_layoutDirection = layoutDirection;
_preferredContentSizeCategory = preferredContentSizeCategory;
_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)preferredContentSizeCategory
containerSize:(CGSize)windowSize
{
return [[self alloc] initWithHorizontalSizeClass:horizontalSizeClass
verticalSizeClass:verticalSizeClass
displayScale:displayScale
displayGamut:displayGamut
userInterfaceIdiom:userInterfaceIdiom
forceTouchCapability:forceTouchCapability
layoutDirection:layoutDirection
preferredContentSizeCategory:preferredContentSizeCategory
containerSize:windowSize];
}
#endif
+ (ASTraitCollection *)traitCollectionWithDisplayScale:(CGFloat)displayScale
userInterfaceIdiom:(UIUserInterfaceIdiom)userInterfaceIdiom
horizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
verticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
forceTouchCapability:(UIForceTouchCapability)forceTouchCapability
containerSize:(CGSize)windowSize
{
#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
}
+ (instancetype)traitCollectionWithASPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traits
{
return [self traitCollectionWithDisplayScale:traits.displayScale
userInterfaceIdiom:traits.userInterfaceIdiom
horizontalSizeClass:traits.horizontalSizeClass
verticalSizeClass:traits.verticalSizeClass
forceTouchCapability:traits.forceTouchCapability
containerSize:traits.containerSize];
#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
containerSize:(CGSize)windowSize
{
return [self traitCollectionWithDisplayScale:traitCollection.displayScale
userInterfaceIdiom:traitCollection.userInterfaceIdiom
horizontalSizeClass:traitCollection.horizontalSizeClass
verticalSizeClass:traitCollection.verticalSizeClass
forceTouchCapability:traitCollection.forceTouchCapability
containerSize: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) {
.displayScale = self.displayScale,
.horizontalSizeClass = self.horizontalSizeClass,
.userInterfaceIdiom = self.userInterfaceIdiom,
.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,
};
}
@ -193,12 +481,19 @@ NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection trai
return YES;
}
return self.displayScale == traitCollection.displayScale &&
self.horizontalSizeClass == traitCollection.horizontalSizeClass &&
self.verticalSizeClass == traitCollection.verticalSizeClass &&
self.userInterfaceIdiom == traitCollection.userInterfaceIdiom &&
CGSizeEqualToSize(self.containerSize, traitCollection.containerSize) &&
self.forceTouchCapability == traitCollection.forceTouchCapability;
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

View File

@ -1097,7 +1097,7 @@
[window layoutIfNeeded];
// The initial reload is async, changing the trait collection here should be "mid-update"
ASPrimitiveTraitCollection traitCollection;
ASPrimitiveTraitCollection traitCollection = ASPrimitiveTraitCollectionMakeDefault();
traitCollection.displayScale = cn.primitiveTraitCollection.displayScale + 1; // Just a dummy change
traitCollection.containerSize = screenBounds.size;
cn.primitiveTraitCollection = traitCollection;

View File

@ -0,0 +1,34 @@
//
// ASTraitCollectionTests.m
// Texture
//
// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved.
// 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
//
#import <XCTest/XCTest.h>
#import <AsyncDisplayKit/ASTraitCollection.h>
@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