[YogaKit] support auto apply layout.
- support auto apply layout - build Yoga & YogaKit framework as staticlib; - update YogaKitSample.
This commit is contained in:
@@ -34,4 +34,9 @@ typedef void (^YGLayoutConfigurationBlock)(YGLayout* layout);
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface UIView (YogaKitAutoApplyLayout)
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@@ -35,3 +35,88 @@ static const void* kYGYogaAssociatedKey = &kYGYogaAssociatedKey;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
static const void* kYGBoundsSizeAssociatedKey = &kYGBoundsSizeAssociatedKey;
|
||||
static void YogaSwizzleInstanceMethod(Class cls, SEL originalSelector, SEL swizzledSelector);
|
||||
|
||||
@implementation UIView (YogaKitAutoApplyLayout)
|
||||
|
||||
+ (void)load {
|
||||
static dispatch_once_t onceToken = 0;
|
||||
dispatch_once(&onceToken, ^{
|
||||
YogaSwizzleInstanceMethod(self, @selector(initWithFrame:), @selector(_yoga_initWithFrame:));
|
||||
YogaSwizzleInstanceMethod(self, @selector(setFrame:), @selector(_yoga_setFrame:));
|
||||
YogaSwizzleInstanceMethod(self, @selector(setBounds:), @selector(_yoga_setBounds:));
|
||||
});
|
||||
}
|
||||
|
||||
- (CGSize)_yoga_boundsSize {
|
||||
NSValue *value = (NSValue *)objc_getAssociatedObject(self, kYGBoundsSizeAssociatedKey);
|
||||
|
||||
return value ? value.CGSizeValue : CGSizeMake(YGUndefined, YGUndefined);
|
||||
}
|
||||
|
||||
- (void)set_yoga_boundsSize:(CGSize)size {
|
||||
objc_setAssociatedObject(self,
|
||||
kYGBoundsSizeAssociatedKey,
|
||||
[NSValue valueWithCGSize:size],
|
||||
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
|
||||
- (instancetype)_yoga_initWithFrame:(CGRect)frame {
|
||||
id _self = [self _yoga_initWithFrame:frame];
|
||||
if (_self) {
|
||||
[self _yoga_applyLayout];
|
||||
}
|
||||
|
||||
return _self;
|
||||
}
|
||||
|
||||
- (void)_yoga_setFrame:(CGRect)frame {
|
||||
[self _yoga_setFrame:frame];
|
||||
|
||||
[self _yoga_applyLayout];
|
||||
}
|
||||
|
||||
- (void)_yoga_setBounds:(CGRect)bounds {
|
||||
[self _yoga_setBounds:bounds];
|
||||
|
||||
[self _yoga_applyLayout];
|
||||
}
|
||||
|
||||
- (void)_yoga_applyLayout {
|
||||
if (self.isYogaEnabled && self.yoga.isEnabled) {
|
||||
CGSize size = self.bounds.size;
|
||||
CGSize prev = self._yoga_boundsSize;
|
||||
if (!CGSizeEqualToSize(size, prev)) {
|
||||
self._yoga_boundsSize = size;
|
||||
[self.yoga applyLayoutPreservingOrigin:YES];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
static void YogaSwizzleInstanceMethod(Class cls, SEL originalSelector, SEL swizzledSelector) {
|
||||
if (!cls || !originalSelector || !swizzledSelector) {
|
||||
return;
|
||||
}
|
||||
|
||||
Method originalMethod = class_getInstanceMethod(cls, originalSelector);
|
||||
Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector);
|
||||
if (!originalMethod || !swizzledMethod) {
|
||||
return;
|
||||
}
|
||||
|
||||
IMP swizzledIMP = method_getImplementation(swizzledMethod);
|
||||
if (class_addMethod(cls, originalSelector, swizzledIMP, method_getTypeEncoding(swizzledMethod))) {
|
||||
class_replaceMethod(cls,
|
||||
swizzledSelector,
|
||||
method_getImplementation(originalMethod),
|
||||
method_getTypeEncoding(originalMethod));
|
||||
} else {
|
||||
method_exchangeImplementations(originalMethod, swizzledMethod);
|
||||
}
|
||||
}
|
||||
|
@@ -162,6 +162,7 @@ static YGConfigRef globalConfig;
|
||||
|
||||
@property(nonatomic, weak, readonly) UIView* view;
|
||||
@property(nonatomic, assign, readonly) BOOL isUIView;
|
||||
@property(nonatomic, assign) BOOL isApplingLayout;
|
||||
|
||||
@end
|
||||
|
||||
@@ -292,11 +293,19 @@ YG_PROPERTY(CGFloat, aspectRatio, AspectRatio)
|
||||
}
|
||||
|
||||
- (void)applyLayout {
|
||||
if (self.isApplingLayout) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self calculateLayoutWithSize:self.view.bounds.size];
|
||||
YGApplyLayoutToViewHierarchy(self.view, NO);
|
||||
}
|
||||
|
||||
- (void)applyLayoutPreservingOrigin:(BOOL)preserveOrigin {
|
||||
if (self.isApplingLayout) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self calculateLayoutWithSize:self.view.bounds.size];
|
||||
YGApplyLayoutToViewHierarchy(self.view, preserveOrigin);
|
||||
}
|
||||
@@ -304,6 +313,10 @@ YG_PROPERTY(CGFloat, aspectRatio, AspectRatio)
|
||||
- (void)applyLayoutPreservingOrigin:(BOOL)preserveOrigin
|
||||
dimensionFlexibility:
|
||||
(YGDimensionFlexibility)dimensionFlexibility {
|
||||
if (self.isApplingLayout) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGSize size = self.view.bounds.size;
|
||||
if (dimensionFlexibility & YGDimensionFlexibilityFlexibleWidth) {
|
||||
size.width = YGUndefined;
|
||||
@@ -463,12 +476,22 @@ static void YGApplyLayoutToViewHierarchy(UIView* view, BOOL preserveOrigin) {
|
||||
[NSThread isMainThread],
|
||||
@"Framesetting should only be done on the main thread.");
|
||||
|
||||
if (!view.isYogaEnabled || !view.yoga.isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const YGLayout* yoga = view.yoga;
|
||||
|
||||
if (yoga.isApplingLayout) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!yoga.isIncludedInLayout) {
|
||||
return;
|
||||
}
|
||||
|
||||
yoga.isApplingLayout = YES;
|
||||
|
||||
YGNodeRef node = yoga.node;
|
||||
const CGPoint topLeft = {
|
||||
YGNodeLayoutGetLeft(node),
|
||||
@@ -481,7 +504,7 @@ static void YGApplyLayoutToViewHierarchy(UIView* view, BOOL preserveOrigin) {
|
||||
};
|
||||
|
||||
const CGPoint origin = preserveOrigin ? view.frame.origin : CGPointZero;
|
||||
view.frame = (CGRect){
|
||||
CGRect frame = (CGRect){
|
||||
.origin =
|
||||
{
|
||||
.x = YGRoundPixelValue(topLeft.x + origin.x),
|
||||
@@ -489,18 +512,31 @@ static void YGApplyLayoutToViewHierarchy(UIView* view, BOOL preserveOrigin) {
|
||||
},
|
||||
.size =
|
||||
{
|
||||
.width = YGRoundPixelValue(bottomRight.x) -
|
||||
YGRoundPixelValue(topLeft.x),
|
||||
.height = YGRoundPixelValue(bottomRight.y) -
|
||||
YGRoundPixelValue(topLeft.y),
|
||||
.width = MAX(YGRoundPixelValue(bottomRight.x) -
|
||||
YGRoundPixelValue(topLeft.x), 0),
|
||||
.height = MAX(YGRoundPixelValue(bottomRight.y) -
|
||||
YGRoundPixelValue(topLeft.y), 0),
|
||||
},
|
||||
};
|
||||
|
||||
// use bounds/center and not frame if non-identity transform.
|
||||
view.bounds = (CGRect) {
|
||||
.origin = view.bounds.origin,
|
||||
.size = frame.size
|
||||
};
|
||||
|
||||
view.center = (CGPoint) {
|
||||
.x = YGRoundPixelValue(CGRectGetMinX(frame) + CGRectGetWidth(frame) * 0.5),
|
||||
.y = YGRoundPixelValue(CGRectGetMinY(frame) + CGRectGetHeight(frame) * 0.5)
|
||||
};
|
||||
|
||||
if (!yoga.isLeaf) {
|
||||
for (NSUInteger i = 0; i < view.subviews.count; i++) {
|
||||
YGApplyLayoutToViewHierarchy(view.subviews[i], NO);
|
||||
}
|
||||
}
|
||||
|
||||
yoga.isApplingLayout = NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
Reference in New Issue
Block a user