Brion VIBBER has submitted this change and it was merged. Change subject: iOS 8 table of contents fixes. ......................................................................
iOS 8 table of contents fixes. Change-Id: I46a34d3f7e01c14c5a1501b14afa65a0bf7fa7f2 --- M wikipedia/Base.lproj/Main_iPhone.storyboard M wikipedia/View Controllers/Navigation/Primary/PrimaryMenuTableViewCell.m M wikipedia/View Controllers/Navigation/Top/TopMenuViewController.m M wikipedia/View Controllers/PullToRefresh/PullToRefreshViewController.m M wikipedia/View Controllers/TableOfContents/TOCViewController.m M wikipedia/View Controllers/WebView/WebViewController.m 6 files changed, 242 insertions(+), 176 deletions(-) Approvals: Brion VIBBER: Verified; Looks good to me, approved diff --git a/wikipedia/Base.lproj/Main_iPhone.storyboard b/wikipedia/Base.lproj/Main_iPhone.storyboard index 878fd8c..c67efbe 100644 --- a/wikipedia/Base.lproj/Main_iPhone.storyboard +++ b/wikipedia/Base.lproj/Main_iPhone.storyboard @@ -1140,9 +1140,6 @@ <rect key="frame" x="0.0" y="0.0" width="320" height="508"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> - <constraints> - <constraint firstAttribute="height" constant="508" placeholder="YES" id="vpd-JA-MM4"/> - </constraints> <dataDetectorType key="dataDetectorTypes"/> <connections> <outlet property="delegate" destination="vXZ-lx-hvc" id="Pmz-tr-TG8"/> @@ -1195,8 +1192,9 @@ </subviews> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/> <constraints> - <constraint firstItem="WeL-Mj-Zsh" firstAttribute="top" secondItem="C8y-0k-FBq" secondAttribute="bottom" id="3Gh-gP-O56"/> <constraint firstItem="WeL-Mj-Zsh" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" id="3Zs-jL-a48"/> + <constraint firstItem="WeL-Mj-Zsh" firstAttribute="top" secondItem="kh9-bI-dsS" secondAttribute="top" id="D7t-vR-6cB"/> + <constraint firstAttribute="bottom" secondItem="WeL-Mj-Zsh" secondAttribute="bottom" id="GWO-jz-Ox4"/> <constraint firstItem="WeL-Mj-Zsh" firstAttribute="trailing" secondItem="F4Q-Xc-ImV" secondAttribute="trailing" id="J2p-jy-zrW"/> <constraint firstAttribute="trailing" secondItem="WeL-Mj-Zsh" secondAttribute="trailing" id="QSr-uN-iN4"/> <constraint firstItem="WeL-Mj-Zsh" firstAttribute="leading" secondItem="gB8-UC-wuQ" secondAttribute="leading" id="TPG-uM-6qs"/> diff --git a/wikipedia/View Controllers/Navigation/Primary/PrimaryMenuTableViewCell.m b/wikipedia/View Controllers/Navigation/Primary/PrimaryMenuTableViewCell.m index 818a65c..e8e9a68 100644 --- a/wikipedia/View Controllers/Navigation/Primary/PrimaryMenuTableViewCell.m +++ b/wikipedia/View Controllers/Navigation/Primary/PrimaryMenuTableViewCell.m @@ -10,6 +10,16 @@ { [super setSelected:selected animated:animated]; + if ([[[UIDevice currentDevice] systemVersion] floatValue] <= 6.1) { + // Old iOS6 devices do a choppy W menu reveal animation if background + // isn't transparent. + self.backgroundColor = [UIColor clearColor]; + }else{ + // iPads of all versions show a white background if we use clearColor, + // so use black. + self.backgroundColor = [UIColor blackColor]; + } + // Configure the view for the selected state } diff --git a/wikipedia/View Controllers/Navigation/Top/TopMenuViewController.m b/wikipedia/View Controllers/Navigation/Top/TopMenuViewController.m index d02d89a..fd87735 100644 --- a/wikipedia/View Controllers/Navigation/Top/TopMenuViewController.m +++ b/wikipedia/View Controllers/Navigation/Top/TopMenuViewController.m @@ -242,13 +242,14 @@ BOOL isRTL = [WikipediaAppUtils isDeviceLanguageRTL]; NSString *caret = !isRTL ? WIKIGLYPH_CARET_LEFT: IOS_WIKIGLYPH_FORWARD; + NSString *toc = !isRTL ? IOS_WIKIGLYPH_TOC_COLLAPSED: IOS_WIKIGLYPH_TOC_EXPANDED; self.buttonX = getWikiGlyphButton(WIKIGLYPH_X, MWLocalizedString(@"menu-close-accessibility-label", nil), NAVBAR_BUTTON_X, size); self.buttonEye = getWikiGlyphButton(WIKIGLYPH_EYE, MWLocalizedString(@"menu-preview-accessibility-label", nil), NAVBAR_BUTTON_EYE, size); self.buttonArrowLeft = getWikiGlyphButton(caret, MWLocalizedString(@"menu-back-accessibility-label", nil), NAVBAR_BUTTON_ARROW_LEFT, size); self.buttonArrowRight = getWikiGlyphButton(caret, MWLocalizedString(@"menu-forward-accessibility-label", nil), NAVBAR_BUTTON_ARROW_RIGHT, size); self.buttonW = getWikiGlyphButton(IOS_WIKIGLYPH_W, MWLocalizedString(@"menu-w-accessibility-label", nil), NAVBAR_BUTTON_LOGO_W, size); - self.buttonTOC = getWikiGlyphButton(IOS_WIKIGLYPH_TOC_COLLAPSED, MWLocalizedString(@"menu-toc-accessibility-label", nil), NAVBAR_BUTTON_TOC, size); + self.buttonTOC = getWikiGlyphButton(toc, MWLocalizedString(@"menu-toc-accessibility-label", nil), NAVBAR_BUTTON_TOC, size); self.buttonMagnify = getWikiGlyphButton(IOS_WIKIGLYPH_MAGNIFY, MWLocalizedString(@"menu-search-accessibility-label", nil), NAVBAR_BUTTON_MAGNIFY, size); self.buttonBlank = getWikiGlyphButton(@"", @"", NAVBAR_BUTTON_BLANK, size); self.buttonCancel = getWikiGlyphButton(@"", MWLocalizedString(@"menu-cancel-accessibility-label", nil), NAVBAR_BUTTON_CANCEL, size); diff --git a/wikipedia/View Controllers/PullToRefresh/PullToRefreshViewController.m b/wikipedia/View Controllers/PullToRefresh/PullToRefreshViewController.m index c1267f8..c9b9404 100644 --- a/wikipedia/View Controllers/PullToRefresh/PullToRefreshViewController.m +++ b/wikipedia/View Controllers/PullToRefresh/PullToRefreshViewController.m @@ -160,7 +160,7 @@ self.isAnimatingHide = YES; [UIView animateWithDuration: 0.3f delay: 0.6f - options: UIViewAnimationOptionTransitionNone + options: UIViewAnimationOptionBeginFromCurrentState animations: ^{ self.pullToRefreshView.alpha = 0.0f; self.pullToRefreshViewBottomConstraint.constant = 0; diff --git a/wikipedia/View Controllers/TableOfContents/TOCViewController.m b/wikipedia/View Controllers/TableOfContents/TOCViewController.m index eb8cb1e..de8b1bb 100644 --- a/wikipedia/View Controllers/TableOfContents/TOCViewController.m +++ b/wikipedia/View Controllers/TableOfContents/TOCViewController.m @@ -33,6 +33,17 @@ #pragma mark View lifecycle + +-(void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + + // This prevents iOS 6 on old devices from shifting the scrollContainer up after + // another view controller's view is pushed, then popped, then the TOC shown again. + // For instance, when edit pencil is tapped then back, then toc button tapped. + self.scrollView.contentOffset = CGPointZero; +} + - (instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; @@ -55,18 +66,16 @@ self.scrollView.showsHorizontalScrollIndicator = NO; self.scrollView.showsVerticalScrollIndicator = NO; + // Add bottom inset so last TOC cell can be scrolled up near top. + // Otherwise table would restrict it to not being scrolled up past + // bottom. The "limitVerticalScrolling:" method depends on this. + self.scrollView.contentInset = UIEdgeInsetsMake(0, 0, 2000, 0); + self.scrollContainer = nil; self.navigationItem.hidesBackButton = YES; UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tocTapped:)]; [self.view addGestureRecognizer:tap]; - - // Adjust scrollview content inset when contentSize changes so bottom entry can be scrolled to top. - [self.scrollView addObserver: self - forKeyPath: @"contentSize" - options: NSKeyValueObservingOptionNew|NSKeyValueObservingOptionInitial - context: nil]; - } - (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView @@ -116,7 +125,6 @@ -(void)refreshForCurrentArticle { - //NSLog(@"%f", CACurrentMediaTime() - begin); self.scrollView.delegate = nil; @@ -125,12 +133,12 @@ [self setupSectionCells]; - for (TOCSectionCellView *cell in [self.sectionCells copy]) { + for (TOCSectionCellView *cell in self.sectionCells.copy) { [self.scrollContainer addSubview:cell]; } // Ensure the scrollContainer is scrolled to the top before its sub-views are constrained. - self.scrollView.contentOffset = CGPointMake(0, 0); + self.scrollView.contentOffset = CGPointZero; [self.view setNeedsUpdateConstraints]; @@ -150,6 +158,15 @@ self.scrollContainer = [[UIView alloc] init]; self.scrollContainer.translatesAutoresizingMaskIntoConstraints = NO; self.scrollContainer.opaque = YES; + + // Need to reset the offset mostly for iOS 6 to ensure top TOC section cell doesn't get stuck + // such that its top half can't be scrolled onscreen. Without this, if you load and article + // like "Food", then load "Mutual and Balanced Force Reductions" (ie an article with a longer + // title - so long that it will wrap to more lines than the previous article's title) then + // quit app, restart, "Mutual and Balanced Force Reductions" should load, now back up to + // "Food", then go forward to "Mutual and Balanced Force Reductions" again. On an old iOS 6 + // device the top TOC section cell will be stuck - you can't scroll it completely onscreen. + self.scrollView.contentOffset = CGPointZero; NSDictionary *views = @{@"scrollContainer": self.scrollContainer}; [self.scrollView addSubview:self.scrollContainer]; @@ -311,11 +328,6 @@ - (void)scrollViewDidScroll:(UIScrollView *)scrollView { - static NSInteger lastOffsetY = 0; - NSInteger thisOffsetY = (NSInteger)scrollView.contentOffset.y; - if ((thisOffsetY == lastOffsetY) || (thisOffsetY % 2)) return; - lastOffsetY = thisOffsetY; - //BOOL pastFocalCell = NO; if (scrollView == self.scrollView) { @@ -338,6 +350,29 @@ cell.isHighlighted = YES; } } + + if ((self.sectionCells.count > 0)) { + [self limitVerticalScrolling:self.scrollView]; + } + } +} + +-(void)limitVerticalScrolling:(UIScrollView *)scrollView +{ + // Prevents last cell from being scrolled up completely offscreen. + UIView *lastCell = self.scrollContainer.subviews.lastObject; + CGRect r = [lastCell.superview convertRect:lastCell.frame toView:self.view]; + if (r.origin.y < 0) { + [scrollView setContentOffset:CGPointMake(0, lastCell.frame.origin.y) animated:NO]; + return; + } + + // Prevents top of first cell from being scrolled down past screen top. + // (eliminates top "bounce" - the bounce takes too long imo) + UIView *firstCell = self.scrollContainer.subviews.firstObject; + r = [firstCell.superview convertRect:firstCell.frame toView:self.view]; + if (r.origin.y > 0) { + [scrollView setContentOffset:CGPointMake(0, firstCell.frame.origin.y) animated:NO]; } } @@ -372,6 +407,8 @@ - (void)scrollViewScrollingEnded:(UIScrollView *)scrollView { + [self scrollViewDidScroll:scrollView]; + for (TOCSectionCellView *cell in [self.sectionCells copy]) { if (cell.isSelected) { @@ -467,32 +504,6 @@ }]; } --(void)insetToRestrictScrollingTopAndBottomCellsPastCenter -{ - if (!self.scrollContainer || (self.scrollContainer.subviews.count == 0)) return; - - // Make it so the last TOCSectionCellView can't scroll off top of screen. - // Assumes a TOCSectionCellView cells come at end. - UIView *lastView = self.scrollContainer.subviews.lastObject; - - // Don't report scrolling when changing inset. - self.scrollView.delegate = nil; - CGFloat insetAmount = self.scrollView.bounds.size.height - lastView.bounds.size.height; - - UIEdgeInsets inset = UIEdgeInsetsMake( - 0, - 0, - insetAmount, - 0 - ); - - if(!UIEdgeInsetsEqualToEdgeInsets(inset, self.scrollView.contentInset)){ - self.scrollView.contentInset = inset; - } - - self.scrollView.delegate = self; -} - -(void)updateHighlightedCellToReflectWebView { // Highlight cell for section currently nearest top of webview. @@ -504,33 +515,21 @@ NSInteger indexOfFirstOnscreenSection = [self.webVC.webView getIndexOfTopOnScreenElementWithPrefix: @"section_heading_and_content_block_" count: self.sectionCells.count]; + + //NSLog(@"indexOfFirstOnscreenSection = %ld sectionCells.count = %ld", indexOfFirstOnscreenSection, self.sectionCells.count); + + // Set to the last cell index if no match. + // (We may have added extra html at bottom of article at display time.) + if (indexOfFirstOnscreenSection == -1) { + indexOfFirstOnscreenSection = self.sectionCells.count - 1; + } + if (indexOfFirstOnscreenSection < self.sectionCells.count) { TOCSectionCellView *cell = ((TOCSectionCellView *)self.sectionCells[indexOfFirstOnscreenSection]); cell.isSelected = YES; cell.isHighlighted = YES; } } -} - --(void)observeValueForKeyPath: (NSString *)keyPath - ofObject: (id)object - change: (NSDictionary *)change - context: (void *)context -{ - if ( - (object == self.scrollView) - && - [keyPath isEqual:@"contentSize"] - ) { - [self insetToRestrictScrollingTopAndBottomCellsPastCenter]; - } -} - --(void)dealloc -{ - // NSLog(@"tocVC dealloc"); - - [self.scrollView removeObserver:self forKeyPath:@"contentSize"]; } #pragma mark Memory diff --git a/wikipedia/View Controllers/WebView/WebViewController.m b/wikipedia/View Controllers/WebView/WebViewController.m index 3dd9217..debcd35 100644 --- a/wikipedia/View Controllers/WebView/WebViewController.m +++ b/wikipedia/View Controllers/WebView/WebViewController.m @@ -71,6 +71,8 @@ #define SCROLL_INDICATOR_BORDER_COLOR [UIColor lightGrayColor] #define SCROLL_INDICATOR_BACKGROUND_COLOR [UIColor whiteColor] +#define BOTTOM_SCROLL_LIMIT_HEIGHT 2000 + // This controls how fast the swipe has to be (side-to-side). #define TOC_SWIPE_TRIGGER_MIN_X_VELOCITY 600.0f // This controls what angle from the horizontal axis will trigger the swipe. @@ -102,7 +104,6 @@ @property (weak, nonatomic) IBOutlet NSLayoutConstraint *webViewLeftConstraint; @property (weak, nonatomic) IBOutlet NSLayoutConstraint *webViewRightConstraint; -@property (strong, nonatomic) NSLayoutConstraint *webViewHeightConstraint; @property (strong, nonatomic) UIView *scrollIndicatorView; @property (strong, nonatomic) NSLayoutConstraint *scrollIndicatorViewTopConstraint; @@ -163,41 +164,12 @@ #pragma mark View lifecycle methods --(void)constrainWebViewHeight -{ - // It's important that the web view height be constrained to a multiple of the - // self.view's height and that this constraint not be changed - especially during - // toc show/hide animations. The height is fixed rather than being constrained to - // the bottom of self.view so the web view content doesn't shift around when the - // toc is revealed/hidden. This was especially problematic when toggling the toc - // when near the bottom of an article. - CGFloat heightMultiple = 3.0f; - - self.webViewHeightConstraint = [NSLayoutConstraint constraintWithItem: self.webView - attribute: NSLayoutAttributeHeight - relatedBy: NSLayoutRelationEqual - toItem: nil - attribute: NSLayoutAttributeNotAnAttribute - multiplier: 1.0 - constant: self.view.frame.size.height * heightMultiple]; - - [self.view addConstraint:self.webViewHeightConstraint]; - - // Set the bottom inset to 0.5 screen height less than the height multiple. - // This allows the bottom of the article to be scrolled halfway up the page. - heightMultiple -= 0.5f; - - UIEdgeInsets insets = UIEdgeInsetsMake(0, 0, (self.view.frame.size.height * heightMultiple), 0); - self.webView.scrollView.contentInset = insets; -} - - (void)viewDidLoad { [super viewDidLoad]; self.scrollingToTop = NO; - [self constrainWebViewHeight]; [self scrollIndicatorSetup]; self.panSwipeRecognizer = nil; @@ -289,6 +261,9 @@ //self.referencesContainerView.layer.borderWidth = 10; //self.referencesContainerView.layer.borderColor = [UIColor redColor].CGColor; + + // Ensure toc show/hide animation scales the web view w/o vertical motion. + self.webView.layer.anchorPoint = CGPointZero; } -(void)showAlert:(id)alertText type:(AlertType)type duration:(CGFloat)duration @@ -436,9 +411,12 @@ -(void)scrollIndicatorMove { + CGFloat f = self.webView.scrollView.contentSize.height - BOTTOM_SCROLL_LIMIT_HEIGHT; + if (f == 0) f = 0.00001f; //self.scrollIndicatorView.alpha = [self tocDrawerIsOpen] ? 0.0f : 1.0f; - CGFloat percent = self.webView.scrollView.contentOffset.y / (self.webView.scrollView.contentSize.height + 0.0001f); - self.scrollIndicatorViewTopConstraint.constant = (percent * self.bottomBarView.frame.origin.y) + 2.0f; + CGFloat percent = self.webView.scrollView.contentOffset.y / f; + //NSLog(@"percent = %f", percent); + self.scrollIndicatorViewTopConstraint.constant = percent * (self.bottomBarView.frame.origin.y - SCROLL_INDICATOR_HEIGHT) + 8.0; } #pragma mark Sync config/ios.json if necessary @@ -535,92 +513,139 @@ { if (![self tocDrawerIsOpen]) return; - self.unsafeToToggleTOC = YES; - - // Save the scroll position; if we're near the end of the page things will - // get reset correctly when we start to zoom out! - __block CGPoint origScrollPosition = self.webView.scrollView.contentOffset; + // Throwing on mainQ prevents iOS 6 bug on old devices which would cause the web + // view to blank out if it was tapped when the toc was open. + [[NSOperationQueue mainQueue] addOperationWithBlock: ^ { + + self.unsafeToToggleTOC = YES; + + // Save the scroll position; if we're near the end of the page things will + // get reset correctly when we start to zoom out! + __block CGPoint origScrollPosition = self.webView.scrollView.contentOffset; + + // Clear alerts + [self fadeAlert]; + + [self.view setNeedsUpdateConstraints]; + [UIView animateWithDuration: duration.floatValue + delay: 0.0f + options: UIViewAnimationOptionBeginFromCurrentState + animations: ^{ + self.scrollIndicatorView.alpha = 1.0; + // If the top menu isn't hidden, reveal the bottom menu. + self.bottomMenuHidden = ROOT.topMenuHidden; + + self.webView.transform = CGAffineTransformIdentity; + + self.referencesContainerView.transform = CGAffineTransformIdentity; + + self.bottomBarView.transform = CGAffineTransformIdentity; + self.webViewRightConstraint.constant = 0; + + [self.view layoutIfNeeded]; + }completion: ^(BOOL done){ + [self.tocVC didHide]; + self.unsafeToToggleTOC = NO; + self.webView.scrollView.contentOffset = origScrollPosition; + + BOOL isRTL = [WikipediaAppUtils isDeviceLanguageRTL]; - // Clear alerts - [self fadeAlert]; - - [self.view setNeedsUpdateConstraints]; - [UIView animateWithDuration: duration.floatValue - delay: 0.0f - options: UIViewAnimationOptionBeginFromCurrentState - animations: ^{ - - // If the top menu isn't hidden, reveal the bottom menu. - self.bottomMenuHidden = ROOT.topMenuHidden; - - self.webView.transform = CGAffineTransformIdentity; - - self.referencesContainerView.transform = CGAffineTransformIdentity; - - self.bottomBarView.transform = CGAffineTransformIdentity; - self.webViewRightConstraint.constant = 0; - - [self.view layoutIfNeeded]; - }completion: ^(BOOL done){ - [self.tocVC didHide]; - self.unsafeToToggleTOC = NO; - self.webView.scrollView.contentOffset = origScrollPosition; - - WikiGlyphButton *tocButton = [ROOT.topMenuViewController getNavBarItem:NAVBAR_BUTTON_TOC]; - [tocButton.label setWikiText: IOS_WIKIGLYPH_TOC_COLLAPSED - color: tocButton.label.color - size: tocButton.label.size - baselineOffset: tocButton.label.baselineOffset]; - }]; + WikiGlyphButton *tocButton = [ROOT.topMenuViewController getNavBarItem:NAVBAR_BUTTON_TOC]; + [tocButton.label setWikiText: (isRTL ? IOS_WIKIGLYPH_TOC_EXPANDED: IOS_WIKIGLYPH_TOC_COLLAPSED) + color: tocButton.label.color + size: tocButton.label.size + baselineOffset: tocButton.label.baselineOffset]; + }]; + }]; } -(void)tocShowWithDuration:(NSNumber *)duration { if ([self tocDrawerIsOpen]) return; - self.unsafeToToggleTOC = YES; + [[NSOperationQueue mainQueue] addOperationWithBlock: ^ { + + self.unsafeToToggleTOC = YES; + + // Hide any alerts immediately. + [self hideAlert]; + + [self.tocVC willShow]; + + CGFloat webViewScale = [self tocGetWebViewScaleWhenTOCVisible]; + CGAffineTransform xf = CGAffineTransformMakeScale(webViewScale, webViewScale); + CGFloat tocWidth = [self tocGetWidthForWebViewScale:webViewScale]; + + [self.view setNeedsUpdateConstraints]; + [UIView animateWithDuration: duration.floatValue + delay: 0.0f + options: UIViewAnimationOptionBeginFromCurrentState + animations: ^{ + self.scrollIndicatorView.alpha = 0.0; + self.bottomMenuHidden = YES; + self.referencesHidden = YES; + self.webView.transform = xf; + self.referencesContainerView.transform = xf; + self.bottomBarView.transform = xf; + self.webViewRightConstraint.constant = tocWidth; + + [self.view layoutIfNeeded]; + + }completion: ^(BOOL done){ + self.unsafeToToggleTOC = NO; + + BOOL isRTL = [WikipediaAppUtils isDeviceLanguageRTL]; - // Hide any alerts immediately. - [self hideAlert]; + WikiGlyphButton *tocButton = [ROOT.topMenuViewController getNavBarItem:NAVBAR_BUTTON_TOC]; + [tocButton.label setWikiText: (isRTL ? IOS_WIKIGLYPH_TOC_COLLAPSED: IOS_WIKIGLYPH_TOC_EXPANDED) + color: tocButton.label.color + size: tocButton.label.size + baselineOffset: tocButton.label.baselineOffset]; + }]; + }]; +} +-(void)tocUpdateLayoutAfterRotate +{ // setNeedsUpdateConstraints causes updateViewConstraints to be called which is needed because // it calls tocConstrainView which sets the width of the toc view. Needed before the animation // block below because the device may have been rotated so the toc view may need new width. - // We want this new width set before the animation begins. + // We want this new width set before the animation begins. (Easy to test with the "Beach Boys" + // article. Load it, open and close toc, rotate and do same. Without setNeedsUpdateConstraints + // and layoutIfNeeded the toc entries will shift.) [self.view setNeedsUpdateConstraints]; // Layout to ensure that the width is in place before the toc view starts to animate to being // onscreen. [self.view layoutIfNeeded]; - // Among other things, the willShow method then makes the toc cells be the correct size for - // the current (potentially new) toc view width. - [self.tocVC willShow]; - - CGFloat webViewScale = [self tocGetWebViewScaleWhenTOCVisible]; - CGAffineTransform xf = CGAffineTransformMakeScale(webViewScale, webViewScale); +} - [self.view setNeedsUpdateConstraints]; - [UIView animateWithDuration: duration.floatValue - delay: 0.0f - options: 0 // UIViewAnimationOptionBeginFromCurrentState <--Don't do this, can cause toc to jump as it appears (if top/bottom menus visibility changes) - animations: ^{ +- (void)viewDidLayoutSubviews +{ + // viewDidLayoutSubviews is called after autolayout has done its thing. So here we can safely make changes + // directly to a frame without worrying that autolayout will blast them. Note that the web view frame + // adjustment below needs to happen not only when layoutIfNeeded is called from the "tocShowWithDuration:" + // animation block, but also any other time subviews are laid out when the toc is onscreen! That's the + // reason why this code is not to be placed directly in the animation block itself. + [self resizeWebViewFrame]; +} - self.bottomMenuHidden = YES; - self.referencesHidden = YES; - self.webView.transform = xf; - self.referencesContainerView.transform = xf; - self.bottomBarView.transform = xf; - self.webViewRightConstraint.constant = [self tocGetWidthForWebViewScale:webViewScale]; - [self.view layoutIfNeeded]; - }completion: ^(BOOL done){ - self.unsafeToToggleTOC = NO; +-(void)resizeWebViewFrame +{ + //NSLog(@"self.webView.frame = %@", NSStringFromCGRect(self.webView.frame)); + CGFloat scale = ([self tocDrawerIsOpen]) ? [self tocGetWebViewScaleWhenTOCVisible] : 1.0f; - WikiGlyphButton *tocButton = [ROOT.topMenuViewController getNavBarItem:NAVBAR_BUTTON_TOC]; - [tocButton.label setWikiText: IOS_WIKIGLYPH_TOC_EXPANDED - color: tocButton.label.color - size: tocButton.label.size - baselineOffset: tocButton.label.baselineOffset]; - - }]; + BOOL isRTL = [WikipediaAppUtils isDeviceLanguageRTL]; + + CGRect newFrame = CGRectMake( + (isRTL ? self.view.frame.size.width - self.view.frame.size.width * scale : 0), + 0.0, + self.view.frame.size.width * scale, + self.view.frame.size.height + ); + + if (!CGRectEqualToRect(newFrame, self.webView.frame)) { + self.webView.frame = newFrame; + } } - (void)tocViewControllerSetup @@ -1040,7 +1065,8 @@ [self.bridge addListener:@"nonAnchorTouchEndedWithoutDragging" withBlock:^(NSString *messageType, NSDictionary *payload) { NSLog(@"nonAnchorTouchEndedWithoutDragging = %@", payload); - [weakSelf animateTopAndBottomMenuReveal]; + // Tiny delay prevents menus from occasionally appearing when user swipes to reveal toc. + [weakSelf performSelector:@selector(animateTopAndBottomMenuReveal) withObject:nil afterDelay:0.05]; // nonAnchorTouchEndedWithoutDragging is used so TOC may be hidden if user tapped, but did *not* drag. // Used because UIWebView is difficult to attach one-finger touch events to. @@ -1243,10 +1269,34 @@ [self.tocVC centerCellForWebViewTopMostSectionAnimated:NO]; } +#pragma mark Web view limit scroll up + +- (void)limitScrollUp:(UIScrollView *)webScrollView +{ + // When trying to scroll the bottom of the web view article all the way to + // the top, this is the minimum amount that will be allowed to be onscreen + // before we limit scrolling. + CGFloat onscreenMinHeight = 210; + + CGFloat offsetMaxY = BOTTOM_SCROLL_LIMIT_HEIGHT + onscreenMinHeight; + + if ((webScrollView.contentSize.height - webScrollView.contentOffset.y) < offsetMaxY){ + CGPoint p = CGPointMake(webScrollView.contentOffset.x, + webScrollView.contentSize.height - offsetMaxY); + + // This limits scrolling! + [webScrollView setContentOffset:p animated: NO]; + } +} + #pragma mark Scroll hiding keyboard threshold - (void)scrollViewDidScroll:(UIScrollView *)scrollView { + if (scrollView == self.webView.scrollView) { + [self limitScrollUp:scrollView]; + } + // Hide the keyboard if it was visible when the results are scrolled, but only if // the results have been scrolled in excess of some small distance threshold first. // This prevents tiny scroll adjustments, which seem to occur occasionally for some @@ -1258,12 +1308,14 @@ [self hideKeyboard]; //NSLog(@"Keyboard Hidden!"); } - - [self adjustTopAndBottomMenuVisibilityOnScroll]; [self scrollIndicatorMove]; - [super scrollViewDidScroll:scrollView]; + if (![self tocDrawerIsOpen]){ + [self adjustTopAndBottomMenuVisibilityOnScroll]; + // No need to report scroll event to pull to refresh super vc if toc open. + [super scrollViewDidScroll:scrollView]; + } } - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView @@ -1310,9 +1362,7 @@ // Toggle the menus closed on tap (only if they were showing). if (![self tocDrawerIsOpen]) { if (ROOT.topMenuViewController.navBarMode != NAVBAR_MODE_SEARCH) { - if (![self tocDrawerIsOpen]){ - [ROOT animateTopAndBottomMenuHidden:NO]; - } + [ROOT animateTopAndBottomMenuHidden:NO]; } } } @@ -1613,7 +1663,9 @@ [self fadeAlert]; } errorBlock:^(NSError *error){ NSString *errorMsg = error.localizedDescription; - [self showAlert:errorMsg type:ALERT_TYPE_TOP duration:-1]; + if(error.code != 555){ // Quick hack for hiding MWNetworkOp cancel messages. + [self showAlert:errorMsg type:ALERT_TYPE_TOP duration:-1]; + } }]; remainingSectionsOp.delegate = self; @@ -1887,6 +1939,10 @@ [sectionTextArray addObject: [self renderLastModified:lastModified by:lastModifiedBy]]; [sectionTextArray addObject: [self renderLanguageButtonForCount: langCount.integerValue]]; [sectionTextArray addObject: [self renderLicenseFooter]]; + + // This is important! Ensures bottom of web view article can be scrolled closer to the top of + // the screen. Works in conjunction with "limitScrollUp:" method. + [sectionTextArray addObject: [NSString stringWithFormat:@"<div style='height:%d;background-color:white;'></div>", BOTTOM_SCROLL_LIMIT_HEIGHT]]; } @@ -2014,6 +2070,8 @@ { [super didRotateFromInterfaceOrientation:fromInterfaceOrientation]; + [self tocUpdateLayoutAfterRotate]; + [self scrollToElementOnScreenBeforeRotate]; } @@ -2060,7 +2118,7 @@ self.zeroStatusLabel.padding = UIEdgeInsetsMake(3, 10, 3, 10); self.zeroStatusLabel.backgroundColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.93]; - [self showAlert:title type:ALERT_TYPE_TOP duration:-1]; + [self showAlert:title type:ALERT_TYPE_TOP duration:2]; [NAV promptFirstTimeZeroOnWithTitleIfAppropriate:title]; }); } -- To view, visit https://gerrit.wikimedia.org/r/158793 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I46a34d3f7e01c14c5a1501b14afa65a0bf7fa7f2 Gerrit-PatchSet: 4 Gerrit-Project: apps/ios/wikipedia Gerrit-Branch: master Gerrit-Owner: Mhurd <mh...@wikimedia.org> Gerrit-Reviewer: Brion VIBBER <br...@wikimedia.org> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits