Skip to content

Instantly share code, notes, and snippets.

@hfossli
Last active December 27, 2016 22:17
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save hfossli/6776203 to your computer and use it in GitHub Desktop.
Save hfossli/6776203 to your computer and use it in GitHub Desktop.
A simple way to make sure there is only one scrollview which is able to scroll to top at a time
#import <UIKit/UIKit.h>
@interface UIScrollView (ScrollsToTop)
+ (void)forceNewViewsDefaultValueForScrollsToTop;
+ (void)makeOnlyThisScrollViewScrollToTopOnStatusBarTap:(UIScrollView *)scrollView;
- (void)makeOnlyThisScrollViewScrollToTopOnStatusBarTap;
@end
#import "UIScrollView+ScrollsToTop.h"
#import <objc/runtime.h>
__weak static UIScrollView *s_currentScrollTopTopScrollView;
static void * const AGDidAddObserverKey;
@interface UIScrollView ()
@property (nonatomic, assign) BOOL didAddScrollToTopObserver;
@end
@implementation UIScrollView (ScrollsToTop)
static void SwizzleMethods(Class c, SEL orig, SEL new)
{
struct objc_method *origMethod = class_getInstanceMethod(c, orig);
struct objc_method *newMethod = class_getInstanceMethod(c, new);
if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, newMethod);
}
+ (void)load
{
[self forceNewViewsDefaultValueForScrollsToTop];
}
+ (void)forceNewViewsDefaultValueForScrollsToTop
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SwizzleMethods([UIScrollView class], @selector(didMoveToWindow), @selector(AGS_didMoveToWindowNew));
});
}
+ (void)makeOnlyThisScrollViewScrollToTopOnStatusBarTap:(UIScrollView *)scrollView
{
if(s_currentScrollTopTopScrollView != nil)
{
s_currentScrollTopTopScrollView.scrollsToTop = NO;
}
s_currentScrollTopTopScrollView = scrollView;
if(s_currentScrollTopTopScrollView != nil)
{
s_currentScrollTopTopScrollView.scrollsToTop = YES;
}
}
- (void)makeOnlyThisScrollViewScrollToTopOnStatusBarTap
{
[[self class] makeOnlyThisScrollViewScrollToTopOnStatusBarTap:self];
}
- (void)AGS_didMoveToWindowNew
{
[self AGS_didMoveToWindowNew];
if (self.window != nil)
{
[self disableScrollsToTopIfNotTheChosenOne];
[self addObserver:self forKeyPath:@"scrollsToTop" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:NULL];
self.didAddScrollToTopObserver = YES;
}
else
{
[self disableScrollsToTopIfNotTheChosenOne];
if(self.didAddScrollToTopObserver)
{
[self removeObserver:self forKeyPath:@"scrollsToTop"];
self.didAddScrollToTopObserver = NO;
}
}
}
- (void)setDidAddScrollToTopObserver:(BOOL)value
{
objc_setAssociatedObject(self, AGDidAddObserverKey, @(value), OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (BOOL)didAddScrollToTopObserver
{
NSNumber *value = objc_getAssociatedObject(self, AGDidAddObserverKey);
return [value boolValue];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
[self disableScrollsToTopIfNotTheChosenOne];
}
- (void)disableScrollsToTopIfNotTheChosenOne
{
if (self != s_currentScrollTopTopScrollView && self.scrollsToTop == YES)
{
self.scrollsToTop = NO;
}
}
@end
/*
* NOTE: In your info.plist you need to set "View controller-based status bar appearance" to "NO"
*/
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[UIScrollView forceNewViewsDefaultValueForScrollsToTop];
return YES;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment