From 45e1756a32b5a8e2abda9c9469a96b9d7368fd16 Mon Sep 17 00:00:00 2001 From: Marino Faggiana Date: Tue, 6 Mar 2018 18:32:37 +0100 Subject: [PATCH] =?UTF-8?q?Feature=20Missing:=20iOS=20app=20is=20missing?= =?UTF-8?q?=20=E2=80=9CScroll=20Bar=E2=80=9D=20to=20scroll=20through=20old?= =?UTF-8?q?er=20=E2=80=98Photos=E2=80=99=20#519?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Libraries external/TOScrollBar/TOScrollBar.h | 123 ++++ Libraries external/TOScrollBar/TOScrollBar.m | 691 ++++++++++++++++++ .../TOScrollBarGestureRecognizer.h | 27 + .../TOScrollBarGestureRecognizer.m | 75 ++ .../TOScrollBar/UIScrollView+TOScrollBar.h | 43 ++ .../TOScrollBar/UIScrollView+TOScrollBar.m | 51 ++ Nextcloud.xcodeproj/project.pbxproj | 26 + iOSClient/Photos/CCPhotos.m | 14 + iOSClient/Settings/Acknowledgements.rtf | 11 +- 9 files changed, 1060 insertions(+), 1 deletion(-) create mode 100755 Libraries external/TOScrollBar/TOScrollBar.h create mode 100755 Libraries external/TOScrollBar/TOScrollBar.m create mode 100755 Libraries external/TOScrollBar/TOScrollBarGestureRecognizer.h create mode 100755 Libraries external/TOScrollBar/TOScrollBarGestureRecognizer.m create mode 100755 Libraries external/TOScrollBar/UIScrollView+TOScrollBar.h create mode 100755 Libraries external/TOScrollBar/UIScrollView+TOScrollBar.m diff --git a/Libraries external/TOScrollBar/TOScrollBar.h b/Libraries external/TOScrollBar/TOScrollBar.h new file mode 100755 index 0000000000..9d46688078 --- /dev/null +++ b/Libraries external/TOScrollBar/TOScrollBar.h @@ -0,0 +1,123 @@ +// +// TOScrollBar.h +// +// Copyright 2016-2017 Timothy Oliver. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#import +#import "UIScrollView+TOScrollBar.h" + +typedef NS_ENUM(NSInteger, TOScrollBarStyle) { + TOScrollBarStyleDefault, + TOScrollBarStyleDark +}; + +NS_ASSUME_NONNULL_BEGIN + +@interface TOScrollBar : UIView + +/* The visual style of the scroll bar, either light or dark */ +@property (nonatomic, assign) TOScrollBarStyle style; + +/** Aligns the scroll bar to the top of the scroll view content offset. + Set this to `YES` when using this in a view controller with iOS 11 large titles. */ +@property (nonatomic, assign) BOOL insetForLargeTitles; + +/** The amount of padding above and below the scroll bar (Only top and bottom values are counted. Default is {20,20} ) */ +@property (nonatomic, assign) UIEdgeInsets verticalInset; + +/** The inset, in points of the middle of track from the edge of the scroll view */ +@property (nonatomic, assign) CGFloat edgeInset; + +/** The tint color of the track */ +@property (nonatomic, strong) UIColor *trackTintColor; + +/** The width in points, of the track (Default value is 2.0) */ +@property (nonatomic, assign) CGFloat trackWidth; + +/** The tint color of the handle (Defaults to the system tint color) */ +@property (nonatomic, strong, nullable) UIColor *handleTintColor; + +/** The width in points, of the handle. (Default value is 4.0) */ +@property (nonatomic, assign) CGFloat handleWidth; + +/** The minimum height in points the handle may be in relation to the content height. (Default value is 64.0) */ +@property (nonatomic, assign) CGFloat handleMinimiumHeight; + +/** The user is currently dragging the handle */ +@property (nonatomic, assign, readonly) BOOL dragging; + +/** The minimum required scale of the scroll view's content height before the scroll bar is shown (Default is 5.0) */ +@property (nonatomic, assign) CGFloat minimumContentHeightScale; + +/** The scroll view in which this scroll bar has been added. */ +@property (nonatomic, weak, readonly) UIScrollView *scrollView; + +/** When enabled, the scroll bar will only respond to direct touches to the handle control. + Touches to the track will be passed to the UI controls beneath it. + Default is NO. */ +@property (nonatomic, assign) BOOL handleExclusiveInteractionEnabled; + +/** + Creates a new instance of the scroll bar view + + @param style The initial style of the scroll bar upon creation + */ +- (instancetype)initWithStyle:(TOScrollBarStyle)style; + +/** + Adds the scroll bar to a scroll view + + @param scrollView The scroll view that will receive this scroll bar + */ +- (void)addToScrollView:(UIScrollView *)scrollView; + +/** + Removes the scroll bar from the scroll view and resets the scroll view's state + */ +- (void)removeFromScrollView; + +/** + If added to a table view, this convienience method will compute the appropriate + inset values for the table separator so they don't underlap the scroll bar + + @param inset The original separator inset value of the table view + */ +- (UIEdgeInsets)adjustedTableViewSeparatorInsetForInset:(UIEdgeInsets)inset; + +/** + If added to a table view, this convienience method will compute the appropriate + insets values for each cell's layout margins in order to appropriately push the cell's + content inwards + + @param layoutMargins The current `layoutMargins` value of the `UITableViewCell` instance. + @param offset If desired, any additional horizontal offset for this specific use case + + */ +- (UIEdgeInsets)adjustedTableViewCellLayoutMarginsForMargins:(UIEdgeInsets)layoutMargins manualOffset:(CGFloat)offset; + +/** + Shows or hides the scroll bar from the scroll view with an optional animation + */ +- (void)setHidden:(BOOL)hidden animated:(BOOL)animated; + +@end + +NS_ASSUME_NONNULL_END + diff --git a/Libraries external/TOScrollBar/TOScrollBar.m b/Libraries external/TOScrollBar/TOScrollBar.m new file mode 100755 index 0000000000..b458e983f6 --- /dev/null +++ b/Libraries external/TOScrollBar/TOScrollBar.m @@ -0,0 +1,691 @@ +// +// TOScrollBar.m +// +// Copyright 2016-2017 Timothy Oliver. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#import "TOScrollBar.h" +#import "UIScrollView+TOScrollBar.h" +#import "TOScrollBarGestureRecognizer.h" + +/** Default values for the scroll bar */ +static const CGFloat kTOScrollBarTrackWidth = 2.0f; // The default width of the scrollable space indicator +static const CGFloat kTOScrollBarHandleWidth = 4.0f; // The default width of the handle control +static const CGFloat kTOScrollBarEdgeInset = 7.5f; // The distance from the edge of the view to the center of the track +static const CGFloat kTOScrollBarHandleMinHeight = 64.0f; // The minimum usable size to which the handle can shrink +static const CGFloat kTOScrollBarWidth = 30.0f; // The width of this control (44 is minimum recommended tapping space) +static const CGFloat kTOScrollBarVerticalPadding = 10.0f; // The default padding at the top and bottom of the view +static const CGFloat kTOScrollBarMinimumContentScale = 5.0f; // The minimum scale of the content view before showing the scroll view is necessary + +/************************************************************************/ + +// A struct to hold the scroll view's previous state before this bar was applied +struct TOScrollBarScrollViewState { + BOOL showsVerticalScrollIndicator; +}; +typedef struct TOScrollBarScrollViewState TOScrollBarScrollViewState; + +/************************************************************************/ +// Private interface exposure for scroll view category + +@interface UIScrollView () //TOScrollBar +- (void)setTo_scrollBar:(TOScrollBar *)scrollBar; +@end + +/************************************************************************/ + +@interface TOScrollBar () { + TOScrollBarScrollViewState _scrollViewState; +} + +@property (nonatomic, weak, readwrite) UIScrollView *scrollView; // The parent scroll view in which we belong + +@property (nonatomic, assign) BOOL userHidden; // View was explicitly hidden by the user as opposed to us + +@property (nonatomic, strong) UIImageView *trackView; // The track indicating the scrollable distance +@property (nonatomic, strong) UIImageView *handleView; // The handle that may be dragged in the scroll bar + +@property (nonatomic, assign, readwrite) BOOL dragging; // The user is presently dragging the handle +@property (nonatomic, assign) CGFloat yOffset; // The offset from the center of the thumb + +@property (nonatomic, assign) CGFloat originalYOffset; // The original placement of the scroll bar when the user started dragging +@property (nonatomic, assign) CGFloat originalHeight; // The original height of the scroll bar when the user started dragging +@property (nonatomic, assign) CGFloat originalTopInset; // The original safe area inset of the scroll bar when the user started dragging + +@property (nonatomic, assign) CGFloat horizontalOffset; // The horizontal offset when the edge inset is too small for the touch region + +@property (nonatomic, assign) BOOL disabled; // Disabled when there's not enough scroll content to merit showing this + +@property (nonatomic, strong) UIImpactFeedbackGenerator *feedbackGenerator; // Taptic feedback for iPhone 7 and above + +@property (nonatomic, strong) TOScrollBarGestureRecognizer *gestureRecognizer; // Our custom recognizer for handling user interactions with the scroll bar + +@end + +/************************************************************************/ + +@implementation TOScrollBar + +#pragma mark - Class Creation - + +- (instancetype)initWithStyle:(TOScrollBarStyle)style +{ + if (self = [super initWithFrame:CGRectZero]) { + _style = style; + [self setUpInitialProperties]; + } + + return self; +} + +- (instancetype)initWithFrame:(CGRect)frame +{ + if (self = [super initWithFrame:frame]) { + [self setUpInitialProperties]; + } + + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + if (self = [super initWithCoder:aDecoder]) { + [self setUpInitialProperties]; + } + + return self; +} + +#pragma mark - Set-up - + +- (void)setUpInitialProperties +{ + _trackWidth = kTOScrollBarTrackWidth; + _handleWidth = kTOScrollBarHandleWidth; + _edgeInset = kTOScrollBarEdgeInset; + _handleMinimiumHeight = kTOScrollBarHandleMinHeight; + _minimumContentHeightScale = kTOScrollBarMinimumContentScale; + _verticalInset = UIEdgeInsetsMake(kTOScrollBarVerticalPadding, 0.0f, kTOScrollBarVerticalPadding, 0.0f); + _feedbackGenerator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight]; + _gestureRecognizer = [[TOScrollBarGestureRecognizer alloc] initWithTarget:self action:@selector(scrollBarGestureRecognized:)]; +} + +- (void)setUpViews +{ + if (self.trackView || self.handleView) { + return; + } + + self.backgroundColor = [UIColor clearColor]; + + // Create and add the track view + self.trackView = [[UIImageView alloc] initWithImage:[TOScrollBar verticalCapsuleImageWithWidth:self.trackWidth]]; + [self addSubview:self.trackView]; + + // Add the handle view + self.handleView = [[UIImageView alloc] initWithImage:[TOScrollBar verticalCapsuleImageWithWidth:self.handleWidth]]; + [self addSubview:self.handleView]; + + // Add the initial styling + [self configureViewsForStyle:self.style]; + + // Add gesture recognizer + [self addGestureRecognizer:self.gestureRecognizer]; +} + +- (void)configureViewsForStyle:(TOScrollBarStyle)style +{ + BOOL dark = (style == TOScrollBarStyleDark); + + CGFloat whiteColor = 0.0f; + if (dark) { + whiteColor = 1.0f; + } + self.trackView.tintColor = [UIColor colorWithWhite:whiteColor alpha:0.1f]; +} + +- (void)dealloc +{ + [self restoreScrollView:self.scrollView]; +} + +- (void)configureScrollView:(UIScrollView *)scrollView +{ + if (scrollView == nil) { + return; + } + + // Make a copy of the scroll view's state and then configure + _scrollViewState.showsVerticalScrollIndicator = self.scrollView.showsVerticalScrollIndicator; + scrollView.showsVerticalScrollIndicator = NO; + + //Key-value Observers + [scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil]; + [scrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil]; +} + +- (void)restoreScrollView:(UIScrollView *)scrollView +{ + if (scrollView == nil) { + return; + } + + // Restore the scroll view's state + scrollView.showsVerticalScrollIndicator = _scrollView.showsVerticalScrollIndicator; + + // Remove the observers + [scrollView removeObserver:self forKeyPath:@"contentOffset"]; + [scrollView removeObserver:self forKeyPath:@"contentSize"]; +} + +- (void)willMoveToSuperview:(UIView *)newSuperview +{ + [super willMoveToSuperview:newSuperview]; + [self setUpViews]; +} + +#pragma mark - Content Layout - + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object + change:(NSDictionary *)change context:(void *)context +{ + [self updateStateForScrollView]; + if (self.hidden) { return; } + [self layoutInScrollView]; + [self setNeedsLayout]; +} + +- (CGFloat)heightOfHandleForContentSize +{ + if (_scrollView == nil) { + return _handleMinimiumHeight; + } + + CGFloat heightRatio = self.scrollView.frame.size.height / self.scrollView.contentSize.height; + CGFloat height = self.frame.size.height * heightRatio; + + return MAX(floorf(height), _handleMinimiumHeight); +} + +- (void)updateStateForScrollView +{ + CGRect frame = _scrollView.frame; + CGSize contentSize = _scrollView.contentSize; + self.disabled = (contentSize.height / frame.size.height) < _minimumContentHeightScale; + [self setHidden:(self.disabled || self.userHidden) animated:NO]; +} + +- (void)layoutInScrollView +{ + CGRect scrollViewFrame = _scrollView.frame; + UIEdgeInsets insets = _scrollView.contentInset; + CGPoint contentOffset = _scrollView.contentOffset; + CGFloat halfWidth = (kTOScrollBarWidth * 0.5f); + + if (@available(iOS 11.0, *)) { + insets = _scrollView.adjustedContentInset; + } + + // Contract the usable space by the scroll view's content inset (eg navigation/tool bars) + scrollViewFrame.size.height -= (insets.top + insets.bottom); + + CGFloat largeTitleDelta = 0.0f; + if (_insetForLargeTitles) { + largeTitleDelta = fabs(MIN(insets.top + contentOffset.y, 0.0f)); + } + + // Work out the final height be further contracting by the padding + CGFloat height = (scrollViewFrame.size.height - (_verticalInset.top + _verticalInset.bottom)) - largeTitleDelta; + + // Work out how much we have to offset the track by to make sure all of the parent view + // is visible at the edge of the screen (Or else we'll be unable to tap properly) + CGFloat horizontalOffset = halfWidth - _edgeInset; + self.horizontalOffset = (horizontalOffset > 0.0f) ? horizontalOffset : 0.0f; + + // Work out the frame for the scroll view + CGRect frame = CGRectZero; + + // Size + frame.size.width = kTOScrollBarWidth; + frame.size.height = (_dragging ? _originalHeight : height); + + // Horizontal placement + frame.origin.x = scrollViewFrame.size.width - (_edgeInset + halfWidth); + if (@available(iOS 11.0, *)) { frame.origin.x -= _scrollView.safeAreaInsets.right; } + frame.origin.x = MIN(frame.origin.x, scrollViewFrame.size.width - kTOScrollBarWidth); + + // Vertical placement in scroll view + if (_dragging) { + frame.origin.y = _originalYOffset; + } + else { + frame.origin.y = _verticalInset.top; + frame.origin.y += insets.top; + frame.origin.y += largeTitleDelta; + } + frame.origin.y += contentOffset.y; + + // Set the frame + self.frame = frame; + + // Bring the scroll bar to the front in case other subviews were subsequently added over it + [self.superview bringSubviewToFront:self]; +} + +- (void)layoutSubviews +{ + CGRect frame = self.frame; + + // The frame of the track + CGRect trackFrame = CGRectZero; + trackFrame.size.width = _trackWidth; + trackFrame.size.height = frame.size.height; + trackFrame.origin.x = ceilf(((frame.size.width - _trackWidth) * 0.5f) + _horizontalOffset); + self.trackView.frame = CGRectIntegral(trackFrame); + + // Don't handle automatic layout when dragging; we'll do that manually elsewhere + if (self.dragging || self.disabled) { + return; + } + + // The frame of the handle + CGRect handleFrame = CGRectZero; + handleFrame.size.width = _handleWidth; + handleFrame.size.height = [self heightOfHandleForContentSize]; + handleFrame.origin.x = ceilf(((frame.size.width - _handleWidth) * 0.5f) + _horizontalOffset); + + // Work out the y offset of the handle + UIEdgeInsets contentInset = _scrollView.contentInset; + if (@available(iOS 11.0, *)) { + contentInset = _scrollView.safeAreaInsets; + } + + CGPoint contentOffset = _scrollView.contentOffset; + CGSize contentSize = _scrollView.contentSize; + CGRect scrollViewFrame = _scrollView.frame; + + CGFloat scrollableHeight = (contentSize.height + contentInset.top + contentInset.bottom) - scrollViewFrame.size.height; + CGFloat scrollProgress = (contentOffset.y + contentInset.top) / scrollableHeight; + handleFrame.origin.y = (frame.size.height - handleFrame.size.height) * scrollProgress; + + // If the scroll view expanded beyond its scrollable range, shrink the handle to match the rubber band effect + if (contentOffset.y < -contentInset.top) { // The top + handleFrame.size.height -= (-contentOffset.y - contentInset.top); + handleFrame.size.height = MAX(handleFrame.size.height, (_trackWidth * 2 + 2)); + } + else if (contentOffset.y + scrollViewFrame.size.height > contentSize.height + contentInset.bottom) { // The bottom + CGFloat adjustedContentOffset = contentOffset.y + scrollViewFrame.size.height; + CGFloat delta = adjustedContentOffset - (contentSize.height + contentInset.bottom); + handleFrame.size.height -= delta; + handleFrame.size.height = MAX(handleFrame.size.height, (_trackWidth * 2 + 2)); + handleFrame.origin.y = frame.size.height - handleFrame.size.height; + } + + // Clamp to the bounds of the frame + handleFrame.origin.y = MAX(handleFrame.origin.y, 0.0f); + handleFrame.origin.y = MIN(handleFrame.origin.y, (frame.size.height - handleFrame.size.height)); + + self.handleView.frame = handleFrame; +} + +- (void)setScrollYOffsetForHandleYOffset:(CGFloat)yOffset animated:(BOOL)animated +{ + CGFloat heightRange = _trackView.frame.size.height - _handleView.frame.size.height; + yOffset = MAX(0.0f, yOffset); + yOffset = MIN(heightRange, yOffset); + + CGFloat positionRatio = yOffset / heightRange; + + CGRect frame = _scrollView.frame; + UIEdgeInsets inset = _scrollView.contentInset; + CGSize contentSize = _scrollView.contentSize; + + if (@available(iOS 11.0, *)) { + inset = _scrollView.adjustedContentInset; + } + inset.top = _originalTopInset; + + CGFloat totalScrollSize = (contentSize.height + inset.top + inset.bottom) - frame.size.height; + CGFloat scrollOffset = totalScrollSize * positionRatio; + scrollOffset -= inset.top; + + CGPoint contentOffset = _scrollView.contentOffset; + contentOffset.y = scrollOffset; + + // Animate to help coax the large title navigation bar to behave + if (@available(iOS 11.0, *)) { + [UIView animateWithDuration:animated ? 0.1f : 0.00001f animations:^{ + [self.scrollView setContentOffset:contentOffset animated:NO]; + }]; + } + else { + [self.scrollView setContentOffset:contentOffset animated:NO]; + } +} + +#pragma mark - Scroll View Integration - + +- (void)addToScrollView:(UIScrollView *)scrollView +{ + if (scrollView == self.scrollView) { + return; + } + + // Restore the previous scroll view + [self restoreScrollView:self.scrollView]; + + // Assign the new scroll view + self.scrollView = scrollView; + + // Apply the observers/settings to the new scroll view + [self configureScrollView:scrollView]; + + // Add the scroll bar to the scroll view's content view + [self.scrollView addSubview:self]; + + // Add ourselves as a property of the scroll view + [self.scrollView setTo_scrollBar:self]; + + // Begin layout + [self layoutInScrollView]; +} + +- (void)removeFromScrollView +{ + [self restoreScrollView:self.scrollView]; + [self removeFromSuperview]; + [self.scrollView setTo_scrollBar:nil]; + self.scrollView = nil; +} + +- (UIEdgeInsets)adjustedTableViewSeparatorInsetForInset:(UIEdgeInsets)inset +{ + inset.right = _edgeInset * 2.0f; + return inset; +} + +- (UIEdgeInsets)adjustedTableViewCellLayoutMarginsForMargins:(UIEdgeInsets)layoutMargins manualOffset:(CGFloat)offset +{ + layoutMargins.right = (_edgeInset * 2.0f) + 15.0f; // Magic system number is 20, but we can't infer that from here on time + layoutMargins.right += offset; + return layoutMargins; +} + +#pragma mark - User Interaction - +- (void)scrollBarGestureRecognized:(TOScrollBarGestureRecognizer *)recognizer +{ + CGPoint touchPoint = [recognizer locationInView:self]; + + switch (recognizer.state) { + case UIGestureRecognizerStateBegan: + [self gestureBeganAtPoint:touchPoint]; + break; + case UIGestureRecognizerStateChanged: + [self gestureMovedToPoint:touchPoint]; + break; + case UIGestureRecognizerStateEnded: + case UIGestureRecognizerStateCancelled: + [self gestureEnded]; + break; + default: + break; + } +} + +- (void)gestureBeganAtPoint:(CGPoint)touchPoint +{ + if (self.disabled) { + return; + } + + // Warm-up the feedback generator + [_feedbackGenerator prepare]; + + self.scrollView.scrollEnabled = NO; + self.dragging = YES; + + // Capture the original position + self.originalHeight = self.frame.size.height; + self.originalYOffset = self.frame.origin.y - self.scrollView.contentOffset.y; + + if (@available(iOS 11.0, *)) { + self.originalTopInset = _scrollView.adjustedContentInset.top; + } else { + self.originalTopInset = _scrollView.contentInset.top; + } + + // Check if the user tapped inside the handle + CGRect handleFrame = self.handleView.frame; + if (touchPoint.y > (handleFrame.origin.y - 20) && + touchPoint.y < handleFrame.origin.y + (handleFrame.size.height + 20)) + { + self.yOffset = (touchPoint.y - handleFrame.origin.y); + return; + } + + if (!self.handleExclusiveInteractionEnabled) { + // User tapped somewhere else, animate the handle to that point + CGFloat halfHeight = (handleFrame.size.height * 0.5f); + + CGFloat destinationYOffset = touchPoint.y - halfHeight; + destinationYOffset = MAX(0.0f, destinationYOffset); + destinationYOffset = MIN(self.frame.size.height - halfHeight, destinationYOffset); + + self.yOffset = (touchPoint.y - destinationYOffset); + handleFrame.origin.y = destinationYOffset; + + [UIView animateWithDuration:0.2f + delay:0.0f + usingSpringWithDamping:1.0f + initialSpringVelocity:0.1f options:UIViewAnimationOptionBeginFromCurrentState + animations:^{ + self.handleView.frame = handleFrame; + } completion:nil]; + + [self setScrollYOffsetForHandleYOffset:floorf(destinationYOffset) animated:NO]; + } +} + +- (void)gestureMovedToPoint:(CGPoint)touchPoint +{ + if (self.disabled) { + return; + } + + CGFloat delta = 0.0f; + CGRect handleFrame = _handleView.frame; + CGRect trackFrame = _trackView.frame; + CGFloat minimumY = 0.0f; + CGFloat maximumY = trackFrame.size.height - handleFrame.size.height; + + if (self.handleExclusiveInteractionEnabled) { + if (touchPoint.y < (handleFrame.origin.y - 20) || + touchPoint.y > handleFrame.origin.y + (handleFrame.size.height + 20)) + { + // This touch is not on the handle; eject. + return; + } + } + + // Apply the updated Y value plus the previous offset + delta = handleFrame.origin.y; + handleFrame.origin.y = touchPoint.y - _yOffset; + + //Clamp the handle, and adjust the y offset to counter going outside the bounds + if (handleFrame.origin.y < minimumY) { + _yOffset += handleFrame.origin.y; + _yOffset = MAX(minimumY, _yOffset); + handleFrame.origin.y = minimumY; + } + else if (handleFrame.origin.y > maximumY) { + CGFloat handleOverflow = CGRectGetMaxY(handleFrame) - trackFrame.size.height; + _yOffset += handleOverflow; + _yOffset = MIN(self.yOffset, handleFrame.size.height); + handleFrame.origin.y = MIN(handleFrame.origin.y, maximumY); + } + + _handleView.frame = handleFrame; + + delta -= handleFrame.origin.y; + delta = fabs(delta); + + // If the delta is not 0.0, but we're at either extreme, + // this is first frame we've since reaching that point. + // Play a taptic feedback impact + if (delta > FLT_EPSILON && (CGRectGetMinY(handleFrame) < FLT_EPSILON || CGRectGetMinY(handleFrame) >= maximumY - FLT_EPSILON)) { + [_feedbackGenerator impactOccurred]; + } + + // If the user is doing really granualar swipes, add a subtle amount + // of vertical animation so the scroll view isn't jumping on each frame + [self setScrollYOffsetForHandleYOffset:floorf(handleFrame.origin.y) animated:NO]; //(delta < 0.51f) +} + +- (void)gestureEnded +{ + self.scrollView.scrollEnabled = YES; + self.dragging = NO; + + [UIView animateWithDuration:0.5f delay:0.0f usingSpringWithDamping:1.0f initialSpringVelocity:0.5f options:0 animations:^{ + [self layoutInScrollView]; + [self layoutIfNeeded]; + } completion:nil]; +} + +- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event +{ + if (!self.handleExclusiveInteractionEnabled) { + return [super pointInside:point withEvent:event]; + } + else { + CGFloat handleMinY = CGRectGetMinY(self.handleView.frame); + CGFloat handleMaxY = CGRectGetMaxY(self.handleView.frame); + return (0 <= point.x) && (handleMinY <= point.y) && (point.y <= handleMaxY); + } +} + +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event +{ + UIView *result = [super hitTest:point withEvent:event]; + + if (self.disabled || self.dragging) { + return result; + } + + // If the user contacts the screen in a swiping motion, + // the scroll view will automatically highjack the touch + // event unless we explicitly override it here. + + self.scrollView.scrollEnabled = (result != self); + return result; +} + +#pragma mark - Accessors - +- (void)setStyle:(TOScrollBarStyle)style +{ + _style = style; + [self configureViewsForStyle:style]; +} + +- (UIColor *)trackTintColor { return self.trackView.tintColor; } + +- (void)setTrackTintColor:(UIColor *)trackTintColor +{ + self.trackView.tintColor = trackTintColor; +} + +- (UIColor *)handleTintColor { return self.handleView.tintColor; } + +- (void)setHandleTintColor:(UIColor *)handleTintColor +{ + self.handleView.tintColor = handleTintColor; +} + +- (void)setHidden:(BOOL)hidden +{ + self.userHidden = hidden; + [self setHidden:hidden animated:NO]; +} + +- (void)setHidden:(BOOL)hidden animated:(BOOL)animated +{ + // Override. It cannot be shown if it's disabled + if (_disabled) { + super.hidden = YES; + return; + } + + // Simply show or hide it if we're not animating + if (animated == NO) { + super.hidden = hidden; + return; + } + + // Show it if we're going to animate it + if (self.hidden && hidden == NO) { + super.hidden = NO; + [self layoutInScrollView]; + [self setNeedsLayout]; + } + + CGRect fromFrame = self.frame; + CGRect toFrame = self.frame; + + CGFloat widestElement = MAX(_trackWidth, _handleWidth); + CGFloat hiddenOffset = fromFrame.origin.x + _edgeInset + (widestElement * 2.0f); + if (hidden == NO) { + fromFrame.origin.x = hiddenOffset; + } + else { + toFrame.origin.x = hiddenOffset; + } + + self.frame = fromFrame; + [UIView animateWithDuration:0.3f + delay:0.0f + usingSpringWithDamping:1.0f + initialSpringVelocity:0.1f + options:UIViewAnimationOptionBeginFromCurrentState + animations:^{ + self.frame = toFrame; + } completion:^(BOOL finished) { + super.hidden = hidden; + }]; + +} + +#pragma mark - Image Generation - ++ (UIImage *)verticalCapsuleImageWithWidth:(CGFloat)width +{ + UIImage *image = nil; + CGFloat radius = width * 0.5f; + CGRect frame = (CGRect){0, 0, width+1, width+1}; + + UIGraphicsBeginImageContextWithOptions(frame.size, NO, 0.0f); + [[UIBezierPath bezierPathWithRoundedRect:frame cornerRadius:radius] fill]; + image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + image = [image resizableImageWithCapInsets:UIEdgeInsetsMake(radius, radius, radius, radius) resizingMode:UIImageResizingModeStretch]; + image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + + return image; +} + +@end diff --git a/Libraries external/TOScrollBar/TOScrollBarGestureRecognizer.h b/Libraries external/TOScrollBar/TOScrollBarGestureRecognizer.h new file mode 100755 index 0000000000..f7f693d136 --- /dev/null +++ b/Libraries external/TOScrollBar/TOScrollBarGestureRecognizer.h @@ -0,0 +1,27 @@ +// +// TOScrollBarGestureRecognizer.h +// +// Copyright 2017 Timothy Oliver. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#import + +@interface TOScrollBarGestureRecognizer : UIGestureRecognizer + +@end diff --git a/Libraries external/TOScrollBar/TOScrollBarGestureRecognizer.m b/Libraries external/TOScrollBar/TOScrollBarGestureRecognizer.m new file mode 100755 index 0000000000..9068d13f8d --- /dev/null +++ b/Libraries external/TOScrollBar/TOScrollBarGestureRecognizer.m @@ -0,0 +1,75 @@ +// +// TOScrollBarGestureRecognizer.h +// +// Copyright 2017 Timothy Oliver. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#import "TOScrollBarGestureRecognizer.h" +#import +#import "TOScrollBar.h" + +@interface TOScrollBarGestureRecognizer () + +@property (nonatomic, readonly) TOScrollBar *scrollBar; // The scroll bar this recognizer is attached to + +@end + +@implementation TOScrollBarGestureRecognizer + +#pragma mark - Gesture Recognizer Filtering - +- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer +{ + // Ensure that the pan gesture recognizer from the scroll view doesn't override the scroll bar + UIView *view = preventedGestureRecognizer.view; + if ([view isEqual:self.scrollBar.scrollView]) { + return YES; + } + + return NO; +} + +#pragma mark - Touch Interaction - +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + self.state = UIGestureRecognizerStateBegan; +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + self.state = UIGestureRecognizerStateChanged; +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + self.state = UIGestureRecognizerStateEnded; +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + self.state = UIGestureRecognizerStateCancelled; +} + +#pragma mark - Accessors - +- (TOScrollBar *)scrollBar +{ + if ([self.view isKindOfClass:[TOScrollBar class]] == NO) { return nil; } + return (TOScrollBar *)self.view; +} + +@end diff --git a/Libraries external/TOScrollBar/UIScrollView+TOScrollBar.h b/Libraries external/TOScrollBar/UIScrollView+TOScrollBar.h new file mode 100755 index 0000000000..1cc51d33df --- /dev/null +++ b/Libraries external/TOScrollBar/UIScrollView+TOScrollBar.h @@ -0,0 +1,43 @@ +// +// UIScrollView+TOScrollBar.h +// +// Copyright 2016 Timothy Oliver. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#import + +@class TOScrollBar; + +@interface UIScrollView (TOScrollBar) + +/** The scroll bar view currently added to this scroll view */ +@property (nullable, nonatomic, readonly) TOScrollBar *to_scrollBar; + +/** + Adds a new scroll bar instance to this scroll bar + @param scrollBar The scroll bar in which to add + */ +- (void)to_addScrollBar:(nullable TOScrollBar *)scrollBar; + +/** + Removes the current scroll bar (if any) from the scroll bar + */ +- (void)to_removeScrollbar; + +@end diff --git a/Libraries external/TOScrollBar/UIScrollView+TOScrollBar.m b/Libraries external/TOScrollBar/UIScrollView+TOScrollBar.m new file mode 100755 index 0000000000..b0db1a2f15 --- /dev/null +++ b/Libraries external/TOScrollBar/UIScrollView+TOScrollBar.m @@ -0,0 +1,51 @@ +// +// UIScrollView+TOScrollBar.m +// +// Copyright 2016 Timothy Oliver. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#import +#import "UIScrollView+TOScrollBar.h" +#import "TOScrollBar.h" + +static void * TOScrollBarPropertyKey = &TOScrollBarPropertyKey; + +@implementation UIScrollView (TOScrollBar) + +- (TOScrollBar *)to_scrollBar +{ + return objc_getAssociatedObject(self, TOScrollBarPropertyKey); +} + +- (void)setTo_scrollBar:(TOScrollBar *)scrollBar +{ + objc_setAssociatedObject(self, TOScrollBarPropertyKey, scrollBar, OBJC_ASSOCIATION_RETAIN); +} + +- (void)to_addScrollBar:(TOScrollBar *)scrollBar +{ + [scrollBar addToScrollView:self]; +} + +- (void)to_removeScrollbar +{ + [self.to_scrollBar removeFromScrollView]; +} + +@end diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index 452077ddc1..f02593556b 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -375,6 +375,9 @@ F7A321AD1E9E6AD50069AD1B /* CCAdvanced.m in Sources */ = {isa = PBXBuildFile; fileRef = F7A321AC1E9E6AD50069AD1B /* CCAdvanced.m */; }; F7A377161EB2364A002856D3 /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F7A377141EB2364A002856D3 /* Crashlytics.framework */; }; F7A3771A1EB2364A002856D3 /* Fabric.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F7A377151EB2364A002856D3 /* Fabric.framework */; }; + F7A5541E204EF8AF008468EC /* TOScrollBarGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = F7A55418204EF8AF008468EC /* TOScrollBarGestureRecognizer.m */; }; + F7A5541F204EF8AF008468EC /* TOScrollBar.m in Sources */ = {isa = PBXBuildFile; fileRef = F7A5541B204EF8AF008468EC /* TOScrollBar.m */; }; + F7A55420204EF8AF008468EC /* UIScrollView+TOScrollBar.m in Sources */ = {isa = PBXBuildFile; fileRef = F7A5541D204EF8AF008468EC /* UIScrollView+TOScrollBar.m */; }; F7B0C0CD1EE7E7750033AC24 /* CCSynchronize.m in Sources */ = {isa = PBXBuildFile; fileRef = F7B0C0CC1EE7E7750033AC24 /* CCSynchronize.m */; }; F7B0C1751EE839A30033AC24 /* NCAutoUpload.m in Sources */ = {isa = PBXBuildFile; fileRef = F7B0C1741EE839A30033AC24 /* NCAutoUpload.m */; }; F7B1FBC41E72E3D1001781FE /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F7B1FBB11E72E3D1001781FE /* Media.xcassets */; }; @@ -1329,6 +1332,12 @@ F7A377151EB2364A002856D3 /* Fabric.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Fabric.framework; sourceTree = ""; }; F7A54C341C6267B500E2C8BF /* CCExifGeo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCExifGeo.h; sourceTree = ""; }; F7A54C351C6267B500E2C8BF /* CCExifGeo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCExifGeo.m; sourceTree = ""; }; + F7A55418204EF8AF008468EC /* TOScrollBarGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TOScrollBarGestureRecognizer.m; sourceTree = ""; }; + F7A55419204EF8AF008468EC /* TOScrollBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TOScrollBar.h; sourceTree = ""; }; + F7A5541A204EF8AF008468EC /* UIScrollView+TOScrollBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIScrollView+TOScrollBar.h"; sourceTree = ""; }; + F7A5541B204EF8AF008468EC /* TOScrollBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TOScrollBar.m; sourceTree = ""; }; + F7A5541C204EF8AF008468EC /* TOScrollBarGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TOScrollBarGestureRecognizer.h; sourceTree = ""; }; + F7A5541D204EF8AF008468EC /* UIScrollView+TOScrollBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIScrollView+TOScrollBar.m"; sourceTree = ""; }; F7A582D61A24DAB500E903D7 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = AppDelegate.m; sourceTree = ""; }; F7A582D71A24DAB500E903D7 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = AppDelegate.h; sourceTree = ""; }; F7ACE4291BAC0268006C0017 /* Acknowledgements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Acknowledgements.h; sourceTree = ""; }; @@ -1874,6 +1883,7 @@ F75037421DBFA91A008FB480 /* PureLayout */, F70F05241C889184008DAB36 /* Reachability */, F7DFE24E1EBDC3A400CF5202 /* Realm */, + F7A55417204EF8AF008468EC /* TOScrollBar */, F75AE3C51E9D12900088BB09 /* SwiftyAvatar */, F73CCE271DC13798007E38D8 /* UICKeyChainStore */, F70F05561C889184008DAB36 /* UIImage+animatedGIF */, @@ -2545,6 +2555,19 @@ path = "Libraries external/Fabric"; sourceTree = SOURCE_ROOT; }; + F7A55417204EF8AF008468EC /* TOScrollBar */ = { + isa = PBXGroup; + children = ( + F7A55419204EF8AF008468EC /* TOScrollBar.h */, + F7A5541B204EF8AF008468EC /* TOScrollBar.m */, + F7A5541C204EF8AF008468EC /* TOScrollBarGestureRecognizer.h */, + F7A55418204EF8AF008468EC /* TOScrollBarGestureRecognizer.m */, + F7A5541A204EF8AF008468EC /* UIScrollView+TOScrollBar.h */, + F7A5541D204EF8AF008468EC /* UIScrollView+TOScrollBar.m */, + ); + path = TOScrollBar; + sourceTree = ""; + }; F7ACE4281BAC0268006C0017 /* Settings */ = { isa = PBXGroup; children = ( @@ -3799,6 +3822,7 @@ F7D4238A1F0596C6009C9782 /* ThumbsMainToolbar.m in Sources */, F70022EC1EC4C9100080073F /* OCXMLSharedParser.m in Sources */, F7F54D061E5B14C800E19C62 /* MWCaptionView.m in Sources */, + F7A55420204EF8AF008468EC /* UIScrollView+TOScrollBar.m in Sources */, F762CB001EACB66200B38484 /* XLFormSegmentedCell.m in Sources */, F732BA061D76CE1500E9878B /* CCNetworking.m in Sources */, F70022B01EC4C9100080073F /* AFURLSessionManager.m in Sources */, @@ -3814,6 +3838,7 @@ F7F54D0C1E5B14C800E19C62 /* MWTapDetectingView.m in Sources */, F7D424631F063B82009C9782 /* CTAssetSelectionLabel.m in Sources */, F7B1FBC61E72E3D1001781FE /* SwiftModalWebVC.swift in Sources */, + F7A5541F204EF8AF008468EC /* TOScrollBar.m in Sources */, F7A321651E9E37960069AD1B /* CCActivity.m in Sources */, F762CB0C1EACB66200B38484 /* XLFormSectionDescriptor.m in Sources */, F77B0E131D118A16002130FE /* AppDelegate.m in Sources */, @@ -3885,6 +3910,7 @@ F7FCFFE01D707B83000E6E29 /* CCPeekPop.m in Sources */, F7BAADC81ED5A87C00B7EAD4 /* NCDatabase.swift in Sources */, F77B0E541D118A16002130FE /* CCMove.m in Sources */, + F7A5541E204EF8AF008468EC /* TOScrollBarGestureRecognizer.m in Sources */, F70022E61EC4C9100080073F /* OCXMLServerErrorsParser.m in Sources */, F762CB171EACB66200B38484 /* XLFormRegexValidator.m in Sources */, F73CC0691E813DFF006E3047 /* BKPasscodeDummyViewController.m in Sources */, diff --git a/iOSClient/Photos/CCPhotos.m b/iOSClient/Photos/CCPhotos.m index 669fec3b3a..f26020db84 100644 --- a/iOSClient/Photos/CCPhotos.m +++ b/iOSClient/Photos/CCPhotos.m @@ -24,6 +24,7 @@ #import "CCPhotos.h" #import "AppDelegate.h" #import "CCManageAutoUpload.h" +#import "TOScrollBar.h" #import "NCBridgeSwift.h" @interface CCPhotos () @@ -41,6 +42,8 @@ @interface CCPhotos ()