Skip to content

Commit

Permalink
Add support for UIView safe area inset freezing
Browse files Browse the repository at this point in the history
Add a fix for _UIHostingView.hitTest(_:, with:) bugs in iOS 18
  • Loading branch information
LeoNatan committed Jul 7, 2024
1 parent 1c6d246 commit 953f206
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 16 deletions.
35 changes: 35 additions & 0 deletions LNPCSwiftRefinements/SwiftRefinements.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,41 @@ import UIKit
@_exported import LNPopupController_ObjC
#endif

#if canImport(SwiftUI)
import SwiftUI

@_cdecl("__fixUIHostingViewHitTest")
internal
func fixUIHostingViewHitTest() {
DispatchQueue.main.async {
let cls = type(of: UIHostingController(rootView: EmptyView()).view!)
let sel = #selector(UIView.hitTest(_:with:))
let method = class_getInstanceMethod(cls, sel)!

let _orig: @convention(c) (_ self: UIView, _ sel: Selector, _ point: CGPoint, _ event: UIEvent?) -> UIView?
_orig = unsafeBitCast(method_getImplementation(method), to: type(of: _orig))

let orig: (UIView, CGPoint, UIEvent?) -> UIView? = { _self, point, event in
_orig(_self, sel, point, event)
}

let impl: @convention(block) (UIView, CGPoint, UIEvent?) -> UIView? = { _self, point, event in
if let popupContentView = _self.subviews.filter({ $0 is LNPopupContentView }).first, popupContentView.point(inside: popupContentView.convert(point, from: _self), with: event), let popupContentViewHitTest = popupContentView.hitTest(popupContentView.convert(point, from: _self), with: event) {
return popupContentViewHitTest
}

if let popupBar = _self.subviews.filter({ $0 is LNPopupBar }).first, popupBar.point(inside: popupBar.convert(point, from: _self), with: event), let popupBarHitTest = popupBar.hitTest(popupBar.convert(point, from: _self), with: event) {
return popupBarHitTest
}

return orig(_self, point, event)
}

method_setImplementation(method, imp_implementationWithBlock(impl))
}
}
#endif

public extension Double {
/// The default popup snap percent. See `LNPopupInteractionStyle.customizedSnap(percent:)` for more information.
static var defaultPopupSnapPercent: Double {
Expand Down
54 changes: 38 additions & 16 deletions LNPopupController/LNPopupController/Private/LNPopupBar.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#import "_LNPopupSwizzlingUtils.h"
#import "NSAttributedString+LNPopupSupport.h"
#import "_LNPopupBarShadowedImageView.h"
#import "UIView+LNPopupSupportPrivate.h"

#ifdef DEBUG
static NSUserDefaults* __LNDebugUserDefaults(void)
Expand Down Expand Up @@ -1067,8 +1068,13 @@ - (void)setSwiftuiTitleContentView:(UIView *)swiftuiTitleContentView
}

_swiftuiTitleContentView = swiftuiTitleContentView;
_swiftuiTitleContentView.backgroundColor = UIColor.clearColor;
_swiftuiTitleContentView.translatesAutoresizingMaskIntoConstraints = NO;

if(_swiftuiTitleContentView != nil)
{
[_swiftuiTitleContentView _ln_freezeInsets];
_swiftuiTitleContentView.backgroundColor = UIColor.clearColor;
_swiftuiTitleContentView.translatesAutoresizingMaskIntoConstraints = NO;
}

[self _setNeedsTitleLayoutRemovingLabels:YES];
}
Expand Down Expand Up @@ -1105,11 +1111,18 @@ - (void)setSwiftuiHiddenLeadingController:(UIViewController *)swiftuiHiddenLeadi
_swiftHacksWindow1.hidden = YES;
_swiftHacksWindow1 = nil;
}
_swiftHacksWindow1 = [[UIWindow alloc] initWithWindowScene:self.window.windowScene];
_swiftHacksWindow1.frame = CGRectMake(-4000, 0, 400, 400);
_swiftHacksWindow1.rootViewController = _swiftuiHiddenLeadingController;
_swiftHacksWindow1.hidden = NO;
_swiftHacksWindow1.alpha = 0.0;

if(_swiftuiHiddenLeadingController != nil)
{
[UIView performWithoutAnimation:^{
_swiftHacksWindow1 = [[UIWindow alloc] initWithWindowScene:self.window.windowScene];
_swiftHacksWindow1.frame = CGRectMake(-4000, 0, 400, 400);
_swiftHacksWindow1.rootViewController = _swiftuiHiddenLeadingController;
_swiftHacksWindow1.hidden = NO;
_swiftHacksWindow1.alpha = 0.0;
[_swiftHacksWindow1 layoutSubviews];
}];
}

[self _fixupSwiftUIControllersWithBarStyle];
}
Expand All @@ -1134,11 +1147,18 @@ - (void)setSwiftuiHiddenTrailingController:(UIViewController *)swiftuiHiddenTrai
_swiftHacksWindow2.hidden = YES;
_swiftHacksWindow2 = nil;
}
_swiftHacksWindow2 = [[UIWindow alloc] initWithWindowScene:self.window.windowScene];
_swiftHacksWindow2.frame = CGRectMake(-4000, 0, 400, 400);
_swiftHacksWindow2.rootViewController = _swiftuiHiddenTrailingController;
_swiftHacksWindow2.hidden = NO;
_swiftHacksWindow2.alpha = 0.0;

if(_swiftuiHiddenTrailingController != nil)
{
[UIView performWithoutAnimation:^{
_swiftHacksWindow2 = [[UIWindow alloc] initWithWindowScene:self.window.windowScene];
_swiftHacksWindow2.frame = CGRectMake(-4000, 0, 400, 400);
_swiftHacksWindow2.rootViewController = _swiftuiHiddenTrailingController;
_swiftHacksWindow2.hidden = NO;
_swiftHacksWindow2.alpha = 0.0;
[_swiftHacksWindow2 layoutSubviews];
}];
}

[self _fixupSwiftUIControllersWithBarStyle];
}
Expand Down Expand Up @@ -1521,10 +1541,12 @@ - (void)_layoutTitles
{
[_titlesView addArrangedSubview:_swiftuiTitleContentView];
[_titlesView layoutIfNeeded];
UIView* textView = _swiftuiTitleContentView.subviews.firstObject;
[NSLayoutConstraint activateConstraints:@[
[_swiftuiTitleContentView.heightAnchor constraintEqualToAnchor:textView.heightAnchor],
]];
if(unavailable(iOS 17.0, *)) {
UIView* textView = _swiftuiTitleContentView.subviews.firstObject;
[NSLayoutConstraint activateConstraints:@[
[_swiftuiTitleContentView.heightAnchor constraintEqualToAnchor:textView.heightAnchor],
]];
}
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ typedef void (^LNInWindowBlock)(dispatch_block_t);
- (void)_ln_forgetAboutIt;
- (nullable NSString*)_ln_effectGroupingIdentifierIfAvailable;

- (void)_ln_freezeInsets;

@end

@interface UIView ()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,33 @@
static NSString* _bV = @"X2JhY2tncm91bmRWaWV3";
//_registeredScrollToTopViews
static NSString* _rSTTV = @"X3JlZ2lzdGVyZWRTY3JvbGxUb1RvcFZpZXdz";
//_safeAreaInsetsFrozen
static NSString* _sAIF = @"X3NhZmVBcmVhSW5zZXRzRnJvemVu";

#endif

@interface __LNPopupUIViewFrozenInsets : NSObject @end
@implementation __LNPopupUIViewFrozenInsets

+ (void)load
{
@autoreleasepool
{
const char* encoding = method_getTypeEncoding(class_getInstanceMethod(UIView.class, @selector(needsUpdateConstraints)));
//_safeAreaInsetsFrozen
class_addMethod(self, NSSelectorFromString(_LNPopupDecodeBase64String(_sAIF)), imp_implementationWithBlock(^ (id self, SEL _cmd) {
return YES;
}), encoding);
}
}

//- (BOOL)_safeAreaInsetsFrozen
//{
// return YES;
//}

@end

@interface UIViewController ()

- (void)_ln_popup_viewDidMoveToWindow;
Expand Down Expand Up @@ -280,6 +304,11 @@ - (NSString*)_ln_effectGroupingIdentifierIfAvailable
#endif
}

- (void)_ln_freezeInsets
{
LNDynamicallySubclass(self, __LNPopupUIViewFrozenInsets.class);
}

@end

#if ! LNPopupControllerEnforceStrictClean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ static void __accessibilityBundleLoadHandler(void)

#pragma mark - UIViewController

extern void __fixUIHostingViewHitTest(void);

@interface UIViewController (LNPopupLayout) @end
@implementation UIViewController (LNPopupLayout)

Expand All @@ -171,6 +173,8 @@ + (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__fixUIHostingViewHitTest();

__LNPopupBuggyAdditionalSafeAreaClasses = [NSSet setWithObjects:UINavigationController.class, UITabBarController.class, nil];

LNSwizzleMethod(self,
Expand Down

0 comments on commit 953f206

Please sign in to comment.