From e08f1ffaec971c06fe4d891245e31bfef95fb317 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 21 Nov 2018 04:24:53 +0400 Subject: [PATCH] Instant View: fixed scroll to anchors --- TelegramUI/InstantPageControllerNode.swift | 42 +++++++++++++++------- TelegramUI/InstantPageDetailsNode.swift | 6 +++- TelegramUI/InstantPageFeedbackItem.swift | 4 +-- TelegramUI/InstantPageLayout.swift | 2 +- TelegramUI/InstantPageLayoutSpacings.swift | 4 +-- TelegramUI/InstantPageTableItem.swift | 16 +++++++++ TelegramUI/InstantPageTextItem.swift | 9 +++-- 7 files changed, 62 insertions(+), 21 deletions(-) diff --git a/TelegramUI/InstantPageControllerNode.swift b/TelegramUI/InstantPageControllerNode.swift index 4ebb1d84e4..4bfb16ed3d 100644 --- a/TelegramUI/InstantPageControllerNode.swift +++ b/TelegramUI/InstantPageControllerNode.swift @@ -368,7 +368,14 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { } } - if self.currentExpandedDetails == nil { + if var currentExpandedDetails = self.currentExpandedDetails { + for (index, expanded) in expandedDetails { + if currentExpandedDetails[index] == nil { + currentExpandedDetails[index] = expanded + } + } + self.currentExpandedDetails = currentExpandedDetails + } else { self.currentExpandedDetails = expandedDetails } @@ -540,8 +547,8 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { } } } - - if let currentLayout = self.currentLayout, collapseOffset > 0.0 { + + if let currentLayout = self.currentLayout { let effectiveContentHeight = currentLayout.contentSize.height - collapseOffset if effectiveContentHeight != self.scrollNode.view.contentSize.height { transition.animateView { @@ -928,12 +935,17 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { private func findAnchorItem(_ anchor: String, items: [InstantPageItem]) -> (InstantPageItem, CGFloat, [InstantPageDetailsItem])? { for item in items { if let item = item as? InstantPageAnchorItem, item.anchor == anchor { - return (item, 0.0, []) + return (item, -10.0, []) } else if let item = item as? InstantPageTextItem { if let lineIndex = item.anchors[anchor] { return (item, item.lines[lineIndex].frame.minY - 10.0, []) } } + else if let item = item as? InstantPageTableItem { + if let offset = item.anchors[anchor] { + return (item, offset - 10.0, []) + } + } else if let item = item as? InstantPageDetailsItem { if let (foundItem, offset, detailsItems) = self.findAnchorItem(anchor, items: item.items) { var detailsItems = detailsItems @@ -965,14 +977,13 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { if let webPage = self.webPage, case let .Loaded(content) = webPage.content, let page = content.instantPage, page.url == baseUrl, let anchor = anchor { if !anchor.isEmpty { if let (item, lineOffset, detailsItems) = findAnchorItem(String(anchor), items: items) { - var previousDetailsItem: InstantPageDetailsItem? var previousDetailsNode: InstantPageDetailsNode? var containerOffset: CGFloat = 0.0 for detailsItem in detailsItems { - if let previousNode = previousDetailsNode, let previousDetailsItem = previousDetailsItem { + if let previousNode = previousDetailsNode { previousNode.contentNode.updateDetailsExpanded(detailsItem.index, true, animated: false) - let frame = previousNode.contentNode.effectiveFrameForItem(detailsItem) - containerOffset += frame.minY + previousDetailsItem.titleHeight + let frame = previousNode.effectiveFrameForItem(detailsItem) + containerOffset += frame.minY previousDetailsNode = previousNode.contentNode.nodeForDetailsItem(detailsItem) previousDetailsNode?.setExpanded(true, animated: false) @@ -983,18 +994,23 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { previousDetailsNode = self.nodeForDetailsItem(detailsItem) previousDetailsNode?.setExpanded(true, animated: false) - previousDetailsItem = detailsItem } } let frame: CGRect - if let previousDetailsNode = previousDetailsNode, let previousDetailsItem = previousDetailsItem { - containerOffset += previousDetailsItem.titleHeight - frame = previousDetailsNode.contentNode.effectiveFrameForItem(item) + if let previousDetailsNode = previousDetailsNode { + frame = previousDetailsNode.effectiveFrameForItem(item) } else { frame = self.effectiveFrameForItem(item) } - self.scrollNode.view.setContentOffset(CGPoint(x: 0.0, y: containerOffset + frame.minY + lineOffset - self.scrollNode.view.contentInset.top), animated: true) + + var targetY = min(containerOffset + frame.minY + lineOffset, self.scrollNode.view.contentSize.height - self.scrollNode.frame.height) + if targetY < self.scrollNode.view.contentOffset.y { + targetY -= self.scrollNode.view.contentInset.top + } else { + targetY -= self.containerLayout?.statusBarHeight ?? 20.0 + } + self.scrollNode.view.setContentOffset(CGPoint(x: 0.0, y: targetY), animated: true) } } else { self.scrollNode.view.setContentOffset(CGPoint(x: 0.0, y: -self.scrollNode.view.contentInset.top), animated: true) diff --git a/TelegramUI/InstantPageDetailsNode.swift b/TelegramUI/InstantPageDetailsNode.swift index 9ad1d26ad9..3c8a494c83 100644 --- a/TelegramUI/InstantPageDetailsNode.swift +++ b/TelegramUI/InstantPageDetailsNode.swift @@ -361,7 +361,7 @@ final class InstantPageDetailsContentNode : ASDisplayNode { return CGRect(origin: origin, size: tile.frame.size) } - func effectiveFrameForItem(_ item: InstantPageItem) -> CGRect { + fileprivate func effectiveFrameForItem(_ item: InstantPageItem) -> CGRect { let layoutOrigin = item.frame.origin var origin = layoutOrigin @@ -610,6 +610,10 @@ final class InstantPageDetailsNode: ASDisplayNode, InstantPageNode { var effectiveContentSize: CGSize { return self.contentNode.effectiveContentSize } + + func effectiveFrameForItem(_ item: InstantPageItem) -> CGRect { + return self.contentNode.effectiveFrameForItem(item).offsetBy(dx: 0.0, dy: self.item.titleHeight) + } } private final class InstantPageDetailsArrowNodeParameters: NSObject { diff --git a/TelegramUI/InstantPageFeedbackItem.swift b/TelegramUI/InstantPageFeedbackItem.swift index 8362b7af89..fbbb85e299 100644 --- a/TelegramUI/InstantPageFeedbackItem.swift +++ b/TelegramUI/InstantPageFeedbackItem.swift @@ -32,11 +32,11 @@ final class InstantPageFeedbackItem: InstantPageItem { } func distanceThresholdGroup() -> Int? { - return nil + return 8 } func distanceThresholdWithGroupCount(_ count: Int) -> CGFloat { - return 0.0 + return CGFloat.greatestFiniteMagnitude } func linkSelectionRects(at point: CGPoint) -> [CGRect] { diff --git a/TelegramUI/InstantPageLayout.swift b/TelegramUI/InstantPageLayout.swift index 9d236c3a1b..abdb178ba8 100644 --- a/TelegramUI/InstantPageLayout.swift +++ b/TelegramUI/InstantPageLayout.swift @@ -269,7 +269,7 @@ func layoutInstantPageBlock(webpage: TelegramMediaWebpage, rtl: Bool, block: Ins for subBlock in blocks { let subLayout = layoutInstantPageBlock(webpage: webpage, rtl: rtl, block: subBlock, boundingWidth: boundingWidth - horizontalInset * 2.0 - indexSpacing - maxIndexWidth, horizontalInset: 0.0, safeInset: 0.0, isCover: false, previousItems: listItems, fillToSize: nil, media: media, mediaIndexCounter: &mediaIndexCounter, embedIndexCounter: &embedIndexCounter, detailsIndexCounter: &detailsIndexCounter, theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, webEmbedHeights: webEmbedHeights) - let spacing: CGFloat = previousBlock != nil ? spacingBetweenBlocks(upper: previousBlock, lower: subBlock) : 0.0 + let spacing: CGFloat = previousBlock != nil && subLayout.contentSize.height > 0.0 ? spacingBetweenBlocks(upper: previousBlock, lower: subBlock) : 0.0 let blockItems = subLayout.flattenedItemsWithOrigin(CGPoint(x: horizontalInset + indexSpacing + maxIndexWidth, y: contentSize.height + spacing)) if previousBlock == nil { originY += spacing diff --git a/TelegramUI/InstantPageLayoutSpacings.swift b/TelegramUI/InstantPageLayoutSpacings.swift index 3c7c49995c..b676fbf4c5 100644 --- a/TelegramUI/InstantPageLayoutSpacings.swift +++ b/TelegramUI/InstantPageLayoutSpacings.swift @@ -4,7 +4,7 @@ import TelegramCore func spacingBetweenBlocks(upper: InstantPageBlock?, lower: InstantPageBlock?) -> CGFloat { if let upper = upper, let lower = lower { switch (upper, lower) { - case (_, .cover), (_, .channelBanner), (.details, .details), (.relatedArticles, nil), (.anchor, _), (_, .anchor): + case (_, .cover), (_, .channelBanner), (.details, .details), (.relatedArticles, nil), (_, .anchor): return 0.0 case (.divider, _), (_, .divider): return 25.0 @@ -49,7 +49,7 @@ func spacingBetweenBlocks(upper: InstantPageBlock?, lower: InstantPageBlock?) -> } } else if let lower = lower { switch lower { - case .cover, .channelBanner, .details: + case .cover, .channelBanner, .details, .anchor: return 0.0 default: return 25.0 diff --git a/TelegramUI/InstantPageTableItem.swift b/TelegramUI/InstantPageTableItem.swift index 61e1c9e661..916a3cca0e 100644 --- a/TelegramUI/InstantPageTableItem.swift +++ b/TelegramUI/InstantPageTableItem.swift @@ -108,6 +108,8 @@ final class InstantPageTableItem: InstantPageScrollableItem { fileprivate let cells: [InstantPageTableCellItem] private let borderWidth: CGFloat + let anchors: [String: CGFloat] + fileprivate init(frame: CGRect, totalWidth: CGFloat, horizontalInset: CGFloat, borderWidth: CGFloat, theme: InstantPageTheme, cells: [InstantPageTableCellItem], rtl: Bool) { self.frame = frame self.totalWidth = totalWidth @@ -116,6 +118,20 @@ final class InstantPageTableItem: InstantPageScrollableItem { self.theme = theme self.cells = cells self.isRTL = rtl + + var anchors: [String: CGFloat] = [:] + for cell in cells { + if let textItem = cell.textItem { + for (anchor, lineIndex) in textItem.anchors { + if anchors[anchor] == nil { + let textItemFrame = textItem.frame.offsetBy(dx: cell.frame.minX, dy: cell.frame.minY) + let offset = textItemFrame.minY + textItem.lines[lineIndex].frame.minY + anchors[anchor] = offset + } + } + } + } + self.anchors = anchors } var contentSize: CGSize { diff --git a/TelegramUI/InstantPageTextItem.swift b/TelegramUI/InstantPageTextItem.swift index 38dc33918c..e4553bba44 100644 --- a/TelegramUI/InstantPageTextItem.swift +++ b/TelegramUI/InstantPageTextItem.swift @@ -552,7 +552,8 @@ func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFlo var lastIndex: CFIndex = 0 var currentLineOrigin = CGPoint() - + + var hasAnchors = false var maxLineWidth: CGFloat = 0.0 var maxImageHeight: CGFloat = 0.0 var extraDescent: CGFloat = 0.0 @@ -677,6 +678,10 @@ func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFlo } } + if !anchorItems.isEmpty { + hasAnchors = true + } + if hadExtraDescent && extraDescent > 0 { workingLineOrigin.y += fontLineSpacing } @@ -706,7 +711,7 @@ func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFlo } var height: CGFloat = 0.0 - if !lines.isEmpty { + if !lines.isEmpty && !(string.length == 1 && hasAnchors) { height = lines.last!.frame.maxY + extraDescent }