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

Reply via email to