Skip to content

Commit

Permalink
Improve popup content scrollview interaction
Browse files Browse the repository at this point in the history
Closes #572
  • Loading branch information
LeoNatan committed Aug 9, 2024
1 parent d8c8acc commit 915a16e
Show file tree
Hide file tree
Showing 9 changed files with 214 additions and 2 deletions.
8 changes: 8 additions & 0 deletions LNPopupController/LNPopupController.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
390DD0111BB2EAC30064DB4A /* LNPopupContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = 390DD0101BB2EAC30064DB4A /* LNPopupContentView.h */; settings = {ATTRIBUTES = (Public, ); }; };
391481BA1DCFA514002416D1 /* LNChevronView.m in Sources */ = {isa = PBXBuildFile; fileRef = 391481B81DCFA514002416D1 /* LNChevronView.m */; };
391481BB1DCFA514002416D1 /* LNChevronView.h in Headers */ = {isa = PBXBuildFile; fileRef = 391481B91DCFA514002416D1 /* LNChevronView.h */; settings = {ATTRIBUTES = (Private, ); }; };
392200702C663A03008AFD36 /* LNAddressInfo.h in Sources */ = {isa = PBXBuildFile; fileRef = 3922006F2C663A03008AFD36 /* LNAddressInfo.h */; };
39222ADB1F1A1C5800388E06 /* LNPopupOpenTapGestureRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 39222AD91F1A1C5800388E06 /* LNPopupOpenTapGestureRecognizer.h */; settings = {ATTRIBUTES = (Private, ); }; };
39222ADC1F1A1C5800388E06 /* LNPopupOpenTapGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 39222ADA1F1A1C5800388E06 /* LNPopupOpenTapGestureRecognizer.m */; };
39314A521B6AE7A400574D3C /* MarqueeLabel.h in Headers */ = {isa = PBXBuildFile; fileRef = 39314A501B6AE7A400574D3C /* MarqueeLabel.h */; settings = {ATTRIBUTES = (Private, ); }; };
Expand Down Expand Up @@ -42,6 +43,7 @@
396A8DE926BEA36B005914B0 /* LNPopupBarAppearanceChainProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 396A8DE726BEA36B005914B0 /* LNPopupBarAppearanceChainProxy.m */; };
396D62722610A42000D03A42 /* UIContextMenuInteraction+LNPopupSupportPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 396D62702610A42000D03A42 /* UIContextMenuInteraction+LNPopupSupportPrivate.h */; };
396D62732610A42000D03A42 /* UIContextMenuInteraction+LNPopupSupportPrivate.m in Sources */ = {isa = PBXBuildFile; fileRef = 396D62712610A42000D03A42 /* UIContextMenuInteraction+LNPopupSupportPrivate.m */; };
3975A5C42C663B520027FCDD /* LNAddressInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = 392200712C663A23008AFD36 /* LNAddressInfo.mm */; };
397AFBFB1F1A1ED200E7D95C /* LNForwardingDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 397AFBF91F1A1ED200E7D95C /* LNForwardingDelegate.h */; settings = {ATTRIBUTES = (Private, ); }; };
397AFBFC1F1A1ED200E7D95C /* LNForwardingDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 397AFBFA1F1A1ED200E7D95C /* LNForwardingDelegate.m */; };
397AFC011F1A21DD00E7D95C /* LNPopupLongPressGestureRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 397AFBFF1F1A21DD00E7D95C /* LNPopupLongPressGestureRecognizer.h */; settings = {ATTRIBUTES = (Private, ); }; };
Expand Down Expand Up @@ -80,6 +82,8 @@
39109DA11DD8A305004B5FAB /* LNPopupCloseButton+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LNPopupCloseButton+Private.h"; sourceTree = "<group>"; };
391481B81DCFA514002416D1 /* LNChevronView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LNChevronView.m; sourceTree = "<group>"; };
391481B91DCFA514002416D1 /* LNChevronView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LNChevronView.h; sourceTree = "<group>"; };
3922006F2C663A03008AFD36 /* LNAddressInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LNAddressInfo.h; sourceTree = "<group>"; };
392200712C663A23008AFD36 /* LNAddressInfo.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = LNAddressInfo.mm; sourceTree = "<group>"; };
39222AD91F1A1C5800388E06 /* LNPopupOpenTapGestureRecognizer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LNPopupOpenTapGestureRecognizer.h; sourceTree = "<group>"; };
39222ADA1F1A1C5800388E06 /* LNPopupOpenTapGestureRecognizer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LNPopupOpenTapGestureRecognizer.m; sourceTree = "<group>"; };
39314A501B6AE7A400574D3C /* MarqueeLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MarqueeLabel.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -207,6 +211,8 @@
397AFBFE1F1A216400E7D95C /* GestureRecognizers */ = {
isa = PBXGroup;
children = (
3922006F2C663A03008AFD36 /* LNAddressInfo.h */,
392200712C663A23008AFD36 /* LNAddressInfo.mm */,
397AFBF91F1A1ED200E7D95C /* LNForwardingDelegate.h */,
397AFBFA1F1A1ED200E7D95C /* LNForwardingDelegate.m */,
39222AD91F1A1C5800388E06 /* LNPopupOpenTapGestureRecognizer.h */,
Expand Down Expand Up @@ -377,6 +383,7 @@
3900316A2AC06FE10046D3DD /* _LNPopupBackgroundShadowView.m in Sources */,
397AFC021F1A21DD00E7D95C /* LNPopupLongPressGestureRecognizer.m in Sources */,
39222ADC1F1A1C5800388E06 /* LNPopupOpenTapGestureRecognizer.m in Sources */,
3975A5C42C663B520027FCDD /* LNAddressInfo.mm in Sources */,
3947E1AA1B61CDA40001178B /* LNPopupCloseButton.m in Sources */,
396A8DE926BEA36B005914B0 /* LNPopupBarAppearanceChainProxy.m in Sources */,
39B3EB0E24D4ECC40034B81D /* UIView+LNPopupSupportPrivate.m in Sources */,
Expand All @@ -391,6 +398,7 @@
399BA1D52A9E975E00CA5167 /* _LNPopupUIBarAppearanceProxy.m in Sources */,
397D9A182687474E005164AB /* _LNPopupBarBackgroundView.m in Sources */,
3947E1A61B61CD650001178B /* LNPopupBar.m in Sources */,
392200702C663A03008AFD36 /* LNAddressInfo.h in Sources */,
39A0DDB426F7894700E5D751 /* NSAttributedString+LNPopupSupport.m in Sources */,
394A85C91B63FE52004FFC61 /* UIViewController+LNPopupSupportPrivate.m in Sources */,
397AFC061F1A229400E7D95C /* LNPopupInteractionPanGestureRecognizer.m in Sources */,
Expand Down
26 changes: 26 additions & 0 deletions LNPopupController/LNPopupController/Private/LNAddressInfo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// LNAddressInfo.h
// LNPopupController
//
// Created by Léo Natan on 9/8/24.
// Copyright © 2024 Léo Natan. All rights reserved.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface LNAddressInfo : NSObject

- (instancetype)initWithAddress:(NSUInteger)address;

@property (nonatomic, readonly) NSUInteger address;
@property (nonatomic, copy, readonly) NSString* image;
@property (nonatomic, copy, readonly) NSString* symbol;
@property (nonatomic, readonly) NSUInteger offset;

- (NSString*)formattedDescriptionForIndex:(NSUInteger)index;

@end

NS_ASSUME_NONNULL_END
86 changes: 86 additions & 0 deletions LNPopupController/LNPopupController/Private/LNAddressInfo.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//
// LNAddressInfo.mm
// LNPopupController
//
// Created by Léo Natan on 9/8/24.
// Copyright © 2024 Léo Natan. All rights reserved.
//

#import "LNAddressInfo.h"
#include <dlfcn.h>
#include <cxxabi.h>

@implementation LNAddressInfo
{
Dl_info _info;
}

@synthesize image, symbol, offset, address;

- (instancetype)initWithAddress:(NSUInteger)_address
{
self = [super init];

if(self)
{
address = _address;
dladdr((void*)address, &_info);
}

return self;
}

- (NSString *)image
{
if(_info.dli_fname != NULL)
{
NSString* potentialImage = [NSString stringWithUTF8String:_info.dli_fname];

if([potentialImage containsString:@"/"])
{
return potentialImage.lastPathComponent;
}
}

return @"???";
}

- (NSString *)symbol
{
if(_info.dli_sname != NULL)
{
return [NSString stringWithUTF8String:_info.dli_sname];
}
else if(_info.dli_fname != NULL)
{
return self.image;
}

return [NSString stringWithFormat:@"0x%1lx", (unsigned long)_info.dli_saddr];
}

- (NSUInteger)offset
{
NSString* str = nil;
if(_info.dli_sname != NULL && (str = [NSString stringWithUTF8String:_info.dli_sname]) != nil)
{
return address - (NSUInteger)_info.dli_saddr;
}
else if(_info.dli_fname != NULL && (str = [NSString stringWithUTF8String:_info.dli_fname]) != nil)
{
return address - (NSUInteger)_info.dli_fbase;
}

return address - (NSUInteger)_info.dli_saddr;
}

- (NSString*)description
{
#if __LP64__
return [NSString stringWithFormat:@"%-35s 0x%016llx %@ + %ld", self.image.UTF8String, (uint64_t)address, self.symbol, self.offset];
#else
return [NSString stringWithFormat:@"%-35s 0x%08lx %@ + %d", self.image.UTF8String, (unsigned long)address, self.symbol, self.offset];
#endif
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@

@property (nonatomic, weak) id forwardedDelegate;

+ (BOOL)isCallerUIKit:(NSArray*)callStackReturnAddresses;

@end
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//

#import "LNForwardingDelegate.h"
#import "LNAddressInfo.h"

@implementation LNForwardingDelegate

Expand Down Expand Up @@ -37,4 +38,12 @@ - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
return [self.forwardedDelegate methodSignatureForSelector:aSelector];
}

+ (BOOL)isCallerUIKit:(NSArray *)callStackReturnAddresses
{
NSUInteger addr = [callStackReturnAddresses[1] unsignedIntegerValue];
LNAddressInfo* addrInfo = [[LNAddressInfo alloc] initWithAddress:addr];

return [addrInfo.image hasPrefix:@"UIKit"];
}

@end
21 changes: 20 additions & 1 deletion LNPopupController/LNPopupController/Private/LNPopupController.m
Original file line number Diff line number Diff line change
Expand Up @@ -706,14 +706,28 @@ - (void)_popupBarPresentationByUserPanGestureHandler_changed:(UIPanGestureRecogn
return;
}

CGPoint translation = [pgr translationInView:pgr.view];
BOOL isVerticalPan = fabs(translation.y) > fabs(translation.x);

if(pgr != _popupContentView.popupInteractionGestureRecognizer)
{
UIScrollView* possibleScrollView = (id)pgr.view;
if([possibleScrollView isKindOfClass:[UIScrollView class]])
{
//If the scroll view has horizontal scroll, ignore the scroll view's pan gesture recognizer.
if(possibleScrollView.contentSize.width > possibleScrollView.bounds.size.width)
if(possibleScrollView._ln_hasHorizontalContent == YES)
{
if(isVerticalPan == false)
{
_popupContentView.popupInteractionGestureRecognizer.enabled = NO;
_popupContentView.popupInteractionGestureRecognizer.enabled = YES;
}
else if(_dismissGestureStarted == true)
{
pgr.enabled = NO;
pgr.enabled = YES;
}

return;
}

Expand Down Expand Up @@ -830,6 +844,11 @@ - (void)_popupBarPresentationByUserPanGestureHandler_changed:(UIPanGestureRecogn

- (void)_popupBarPresentationByUserPanGestureHandler_endedOrCancelled:(UIPanGestureRecognizer*)pgr
{
if(_dismissGestureStarted == true && pgr != _popupContentView.popupInteractionGestureRecognizer)
{
return;
}

LNPopupInteractionStyle resolvedStyle = _LNPopupResolveInteractionStyleFromInteractionStyle(_containerController.popupInteractionStyle);

if(_dismissGestureStarted == YES)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#import "LNForwardingDelegate.h"
#import "UIViewController+LNPopupSupportPrivate.h"
#import "LNPopupController.h"
#import "UIView+LNPopupSupportPrivate.h"

extern LNPopupInteractionStyle _LNPopupResolveInteractionStyleFromInteractionStyle(LNPopupInteractionStyle style);

Expand Down Expand Up @@ -64,6 +65,11 @@ - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
return rv;
}

//- (BOOL)_panGestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer shouldTryToBeginHorizontallyWithEvent:(UIEvent*)event
//{
// return NO;
//}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if([NSStringFromClass(otherGestureRecognizer.view.class) containsString:@"DropShadow"])
Expand All @@ -82,6 +88,11 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecogni
return YES;
}

if([otherGestureRecognizer.view isKindOfClass:UIScrollView.class] && [(UIScrollView*)otherGestureRecognizer.view _ln_hasVerticalContent] == NO)
{
return YES;
}

if(_popupController.popupControllerInternalState != LNPopupPresentationStateOpen)
{
if([self.forwardedDelegate respondsToSelector:_cmd])
Expand Down Expand Up @@ -111,7 +122,7 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequi
}
else
{
return YES;
return [(UIScrollView*)otherGestureRecognizer.view _ln_hasVerticalContent] == YES;
}
}

Expand Down Expand Up @@ -139,6 +150,11 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequire
{
return NO;
}

if([otherGestureRecognizer.view isKindOfClass:UIScrollView.class] && [(UIScrollView*)otherGestureRecognizer.view _ln_hasVerticalContent] == NO)
{
return NO;
}

if([NSStringFromClass(otherGestureRecognizer.view.class) containsString:@"SwiftUI"])
{
Expand Down Expand Up @@ -170,6 +186,11 @@ - (instancetype)initWithTarget:(id)target action:(SEL)action popupController:(LN

- (id<UIGestureRecognizerDelegate>)delegate
{
if([LNForwardingDelegate isCallerUIKit:NSThread.callStackReturnAddresses])
{
return _actualDelegate;
}

return _actualDelegate.forwardedDelegate;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,10 @@ typedef void (^LNInWindowBlock)(dispatch_block_t);
#endif

NS_ASSUME_NONNULL_END

@interface UIScrollView (LNPopupSupportPrivate)

- (BOOL)_ln_hasHorizontalContent;
- (BOOL)_ln_hasVerticalContent;

@end
Original file line number Diff line number Diff line change
Expand Up @@ -771,3 +771,37 @@ - (void)_lnpopup_setStandardAppearance:(UITabBarAppearance *)standardAppearance
}

@end

@implementation UIScrollView (LNPopupSupportPrivate)

- (CGSize)_ln_adjustedContentSize
{
CGRect rect = (CGRect){0, 0, self.contentSize};
if([NSStringFromClass(self.class) containsString:@"Queu"] == NO)
{
rect = UIEdgeInsetsInsetRect(rect, self.adjustedContentInset);
}
return rect.size;
}

- (BOOL)_ln_hasHorizontalContent
{
CGSize contentSize = self._ln_adjustedContentSize;
BOOL rv = contentSize.width > self.bounds.size.width;

// NSLog(@"_ln_hasHorizontalContent: %@ contentSize: %@", @(rv), @(contentSize));

return rv;
}

- (BOOL)_ln_hasVerticalContent
{
CGSize contentSize = self._ln_adjustedContentSize;
BOOL rv = contentSize.height > self.bounds.size.height;

// NSLog(@"_ln_hasVerticalContent: %@", @(rv));

return rv;
}

@end

0 comments on commit 915a16e

Please sign in to comment.