Different drawing algorithm

This commit is contained in:
Leo Natan
2020-10-15 20:49:25 +03:00
parent 762a6095b9
commit b1483ac2f4
2 changed files with 155 additions and 50 deletions
@@ -207,21 +207,18 @@ DTX_DIRECT_MEMBERS
DTXAssert(indexOfTestedWindow != NSNotFound, @"Window hierarchy mutated while iterated; should not happen");
if(testedView != nil)
{
[windowToUse dtx_drawViewHierarchyUpToSubview:testedView inRect:windowToUse.bounds afterScreenUpdates:NO];
}
else
if(testedView == nil)
{
[UIColor.blackColor setFill];
[[UIBezierPath bezierPathWithRect:windowToUse.bounds] fill];
[windowToUse drawViewHierarchyInRect:windowToUse.bounds afterScreenUpdates:NO];
}
[windowToUse dtx_drawViewHierarchyUpToSubview:testedView inRect:windowToUse.bounds afterScreenUpdates:NO];
for (NSUInteger idx = indexOfTestedWindow + 1; idx < windows.count; idx++) {
UIWindow* currentWindow = windows[idx];
[currentWindow drawViewHierarchyInRect:currentWindow.bounds afterScreenUpdates:NO];
[currentWindow dtx_drawViewHierarchyUpToSubview:nil inRect:currentWindow.bounds afterScreenUpdates:NO];
}
//Overlay the keyboard scene windows on top
@@ -233,7 +230,7 @@ DTX_DIRECT_MEMBERS
windows = [UIWindow dtx_allWindowsForScene:scene];
for (UIWindow* keyboardSceneWindow in windows) {
[keyboardSceneWindow drawViewHierarchyInRect:keyboardSceneWindow.bounds afterScreenUpdates:NO];
[keyboardSceneWindow dtx_drawViewHierarchyUpToSubview:nil inRect:keyboardSceneWindow.bounds afterScreenUpdates:NO];
}
}
}
+150 -42
View File
@@ -9,13 +9,126 @@
#import "UIView+Drawing.h"
@import ObjectiveC;
@interface CALayer ()
- (void)_renderSublayersInContext:(struct CGContext *)arg1;
- (void)_renderForegroundInContext:(struct CGContext *)arg1;
- (void)_renderBackgroundInContext:(struct CGContext *)arg1;
@end
@interface CALayer (BackdropDrawingUtils) @end
@implementation CALayer (BackdropDrawingUtils)
static BOOL _hack = NO;
static BOOL _found = NO;
static UIView* _lookingFor = nil;
static NSArray* (*__orig_CALayer_sublayers)(id self, SEL _cmd);
static NSArray* _dtx_layer_sublayers(id self, SEL _cmd)
{
if(_hack == NO)
{
return __orig_CALayer_sublayers(self, _cmd);
}
NSArray* zSorted = [__orig_CALayer_sublayers(self, _cmd) sortedArrayWithOptions:NSSortStable usingComparator:^NSComparisonResult(CALayer* _Nonnull obj1, CALayer* _Nonnull obj2) {
return [@(obj1.zPosition) compare:@(obj2.zPosition)];
}];
return zSorted;
}
static void (*__orig_CALayer_renderForegroundInContext)(id self, SEL _cmd, CGContextRef ctx);
static void _dtx_layer_renderForegroundInContext(CALayer* self, SEL _cmd, CGContextRef ctx)
{
if(_hack == NO)
{
__orig_CALayer_renderForegroundInContext(self, _cmd, ctx);
return;
}
UIView* delegate = (self.delegate != nil && [self.delegate isKindOfClass:UIView.class]) ? (id)self.delegate : nil;
if(_found == NO && (delegate == nil || [_lookingFor isDescendantOfView:delegate] == NO))
{
//This layer tree is under the subview we are looking for.
return;
}
if([_lookingFor isDescendantOfView:delegate] == YES)
{
return;
}
__orig_CALayer_renderForegroundInContext(self, _cmd, ctx);
}
static void (*__orig_CALayer_renderBackgroundInContext)(id self, SEL _cmd, CGContextRef ctx);
static void _dtx_layer_renderBackgroundInContext(CALayer* self, SEL _cmd, CGContextRef ctx)
{
if(_hack == NO)
{
__orig_CALayer_renderBackgroundInContext(self, _cmd, ctx);
return;
}
UIView* delegate = (self.delegate != nil && [self.delegate isKindOfClass:UIView.class]) ? (id)self.delegate : nil;
if(_found == NO && (delegate == nil || [_lookingFor isDescendantOfView:delegate] == NO))
{
//This layer tree is under the subview we are looking for.
return;
}
if([_lookingFor isDescendantOfView:delegate] == YES)
{
return;
}
__orig_CALayer_renderBackgroundInContext(self, _cmd, ctx);
}
static void (*__orig_CALayer_renderInContext)(id self, SEL _cmd, CGContextRef ctx);
static void _dtx_layer_renderInContext(CALayer* self, SEL _cmd, CGContextRef ctx)
{
if(_hack == NO)
{
__orig_CALayer_renderInContext(self, _cmd, ctx);
return;
}
UIView* delegate = (self.delegate != nil && [self.delegate isKindOfClass:UIView.class]) ? (id)self.delegate : nil;
if(_found == NO && (delegate == nil || [_lookingFor isDescendantOfView:delegate] == NO))
{
//This layer tree is under the subview we are looking for.
return;
}
if(delegate == _lookingFor)
{
//This layer tree is the one we are looking for, mark as found and return.
_found = YES;
return;
}
if(_found == YES)
{
__orig_CALayer_renderInContext(self, _cmd, ctx);
return;
}
// [self _renderSublayersInContext:ctx];
__orig_CALayer_renderInContext(self, _cmd, ctx);
}
static void (*__orig_VKMapView_renderInContext)(id self, SEL _cmd, CGContextRef ctx);
+ (void)_dtx_applyDrawingFixes
{
_hack = YES;
_found = NO;
Class cls = NSClassFromString(@"VKMapView");
if(cls != nil)
{
@@ -34,6 +147,8 @@ static void (*__orig_VKMapView_renderInContext)(id self, SEL _cmd, CGContextRef
__orig_VKMapView_renderInContext = (void*)method_getImplementation(m);
method_setImplementation(m, (void*)__orig_VKMapView_renderInContext);
}
_hack = NO;
}
+ (void)load
@@ -42,10 +157,26 @@ static void (*__orig_VKMapView_renderInContext)(id self, SEL _cmd, CGContextRef
{
Method m = class_getInstanceMethod(CALayer.class, @selector(renderInContext:));
class_addMethod(NSClassFromString(@"CABackdropLayer"), method_getName(m), (IMP)_dtx_backdrop_renderInContext, method_getTypeEncoding(m));
m = class_getInstanceMethod(CALayer.class, @selector(renderInContext:));
__orig_CALayer_renderInContext = (void*)method_getImplementation(m);
method_setImplementation(m, (void*)_dtx_layer_renderInContext);
m = class_getInstanceMethod(CALayer.class, @selector(_renderForegroundInContext:));
__orig_CALayer_renderForegroundInContext = (void*)method_getImplementation(m);
method_setImplementation(m, (void*)_dtx_layer_renderForegroundInContext);
m = class_getInstanceMethod(CALayer.class, @selector(_renderBackgroundInContext:));
__orig_CALayer_renderBackgroundInContext = (void*)method_getImplementation(m);
method_setImplementation(m, (void*)_dtx_layer_renderBackgroundInContext);
m = class_getInstanceMethod(CALayer.class, @selector(sublayers));
__orig_CALayer_sublayers = (void*)method_getImplementation(m);
method_setImplementation(m, (void*)_dtx_layer_sublayers);
}
}
static void _dtx_map_renderInContext(CALayer* self, SEL _sel, CGContextRef ctx)
static void _dtx_map_renderInContext(CALayer* self, SEL _cmd, CGContextRef ctx)
{
CGContextSaveGState(ctx);
CGContextBeginTransparencyLayer(ctx, nil);
@@ -69,7 +200,7 @@ static void _dtx_map_renderInContext(CALayer* self, SEL _sel, CGContextRef ctx)
CGContextRestoreGState(ctx);
}
static void _dtx_backdrop_renderInContext(CALayer* self, SEL _sel, CGContextRef ctx)
static void _dtx_backdrop_renderInContext(CALayer* self, SEL _cmd, CGContextRef ctx)
{
CGContextSaveGState(ctx);
CGContextBeginTransparencyLayer(ctx, nil);
@@ -89,60 +220,37 @@ static void _dtx_backdrop_renderInContext(CALayer* self, SEL _sel, CGContextRef
DTX_DIRECT_MEMBERS
@implementation UIView (Drawing)
- (void)dtx_drawViewHierarchyUpToSubview:(UIView*)subview inRect:(CGRect)rect afterScreenUpdates:(BOOL)afterUpdates
{
[CALayer _dtx_applyDrawingFixes];
[self _dtx_drawViewHierarchyUpToSubview:subview rootView:self inRect:rect afterScreenUpdates:afterUpdates];
[CALayer _dtx_restoreDrawingDefaults];
}
CALayer* _DTXLayerForView(UIView* view, BOOL afterUpdates)
{
return afterUpdates ? view.layer : view.layer.presentationLayer;
}
- (void)_dtx_drawViewHierarchyUpToSubview:(UIView*)subview rootView:(UIView*)rootView inRect:(CGRect)rect afterScreenUpdates:(BOOL)afterUpdates
- (void)dtx_drawViewHierarchyUpToSubview:(UIView*)subview inRect:(CGRect)rect afterScreenUpdates:(BOOL)afterUpdates
{
[CALayer _dtx_applyDrawingFixes];
_lookingFor = subview;
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
if(subview == self)
// Center the context around the view's anchor point
CGContextTranslateCTM(ctx, self.center.x, self.center.y);
// Apply the view's transform about the anchor point
CGContextConcatCTM(ctx, self.transform);
// Offset by the portion of the bounds left of and above the anchor point
CGContextTranslateCTM(ctx, -self.bounds.size.width * self.layer.anchorPoint.x, -self.bounds.size.height * self.layer.anchorPoint.y);
if(subview == nil)
{
return;
[self drawViewHierarchyInRect:self.bounds afterScreenUpdates:afterUpdates];
}
if([subview isDescendantOfView:self] == NO)
else
{
[_DTXLayerForView(self, afterUpdates) renderInContext:ctx];
return;
}
NSArray* zSorted = [self.subviews.reverseObjectEnumerator.allObjects sortedArrayWithOptions:NSSortStable usingComparator:^NSComparisonResult(UIView* _Nonnull obj1, UIView* _Nonnull obj2) {
return [@(obj2.layer.zPosition) compare:@(obj1.layer.zPosition)];
}];
for (UIView* obj in zSorted) {
if([subview isDescendantOfView:obj] == NO)
{
CGContextSaveGState(ctx);
CGRect bounds = obj.bounds;
CGRect boundsInRootViewCoords = [rootView convertRect:bounds fromView:obj];
CGContextTranslateCTM(ctx, boundsInRootViewCoords.origin.x, boundsInRootViewCoords.origin.y);
CALayer* layer = _DTXLayerForView(obj, afterUpdates);
CGContextConcatCTM(ctx, [layer affineTransform]);
[layer renderInContext:ctx];
CGContextRestoreGState(ctx);
}
else
{
[obj _dtx_drawViewHierarchyUpToSubview:subview rootView:rootView inRect:obj.bounds afterScreenUpdates:afterUpdates];
//Everything else is now under the view we are searching for.
return;
}
}
CGContextRestoreGState(ctx);
[CALayer _dtx_restoreDrawingDefaults];
}
@end