Added support for inline anchors in Instant View

This commit is contained in:
Ilya Laktyushin 2018-11-12 22:53:43 +04:00
parent 4d2c2a022d
commit cc55b0ed93
5 changed files with 62 additions and 12 deletions

View File

@ -21,7 +21,7 @@ final class AuthorizationSequenceSplashController: ViewController {
var nextPressed: ((PresentationStrings?) -> Void)? var nextPressed: ((PresentationStrings?) -> Void)?
private let suggestedLocalization = Promise<SuggestedLocalizationInfo?>(nil) private let suggestedLocalization = Promise<SuggestedLocalizationInfo?>()
private let activateLocalizationDisposable = MetaDisposable() private let activateLocalizationDisposable = MetaDisposable()
init(postbox: Postbox, network: Network, theme: AuthorizationTheme) { init(postbox: Postbox, network: Network, theme: AuthorizationTheme) {
@ -29,7 +29,8 @@ final class AuthorizationSequenceSplashController: ViewController {
self.network = network self.network = network
self.theme = theme self.theme = theme
self.suggestedLocalization.set(currentlySuggestedLocalization(network: network, extractKeys: ["Login.ContinueWithLocalization"])) self.suggestedLocalization.set(.single(nil)
|> then(currentlySuggestedLocalization(network: network, extractKeys: ["Login.ContinueWithLocalization"])))
let suggestedLocalization = self.suggestedLocalization let suggestedLocalization = self.suggestedLocalization
let localizationSignal = SSignal(generator: { subscriber in let localizationSignal = SSignal(generator: { subscriber in

View File

@ -893,11 +893,16 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
} }
} }
private func findAnchorItem(_ anchor: String, items: [InstantPageItem]) -> InstantPageAnchorItem? { private func findAnchorItem(_ anchor: String, items: [InstantPageItem]) -> (InstantPageItem, CGFloat)? {
for item in items { for item in items {
if let item = item as? InstantPageAnchorItem, item.anchor == anchor { if let item = item as? InstantPageAnchorItem, item.anchor == anchor {
return item return (item, 0.0)
} else if let item = item as? InstantPageDetailsItem { } 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? InstantPageDetailsItem {
if let anchorItem = findAnchorItem(anchor, items: item.items) { if let anchorItem = findAnchorItem(anchor, items: item.items) {
return anchorItem return anchorItem
} }
@ -910,12 +915,18 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
guard let items = self.currentLayout?.items else { guard let items = self.currentLayout?.items else {
return return
} }
if let webPage = self.webPage, url.webpageId == webPage.id, let anchorRange = url.url.range(of: "#") { var baseUrl = url.url
let anchor = url.url[anchorRange.upperBound...] var anchor: String?
if let anchorRange = url.url.range(of: "#") {
anchor = String(baseUrl[anchorRange.upperBound...])
baseUrl = String(baseUrl[..<anchorRange.lowerBound])
}
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 !anchor.isEmpty {
if let anchorItem = findAnchorItem(String(anchor), items: items) { if let (anchorItem, offset) = findAnchorItem(String(anchor), items: items) {
self.scrollNode.view.setContentOffset(CGPoint(x: 0.0, y: anchorItem.frame.origin.y - self.scrollNode.view.contentInset.top), animated: true) self.scrollNode.view.setContentOffset(CGPoint(x: 0.0, y: anchorItem.frame.minY + offset - self.scrollNode.view.contentInset.top), animated: true)
return return
} }
} }

View File

@ -33,6 +33,10 @@ struct InstantPageTextImageItem {
let id: MediaId let id: MediaId
} }
struct InstantPageTextAnchorItem {
let name: String
}
final class InstantPageTextLine { final class InstantPageTextLine {
let line: CTLine let line: CTLine
let range: NSRange let range: NSRange
@ -40,15 +44,17 @@ final class InstantPageTextLine {
let strikethroughItems: [InstantPageTextStrikethroughItem] let strikethroughItems: [InstantPageTextStrikethroughItem]
let markedItems: [InstantPageTextMarkedItem] let markedItems: [InstantPageTextMarkedItem]
let imageItems: [InstantPageTextImageItem] let imageItems: [InstantPageTextImageItem]
let anchorItems: [InstantPageTextAnchorItem]
let isRTL: Bool let isRTL: Bool
init(line: CTLine, range: NSRange, frame: CGRect, strikethroughItems: [InstantPageTextStrikethroughItem], markedItems: [InstantPageTextMarkedItem], imageItems: [InstantPageTextImageItem], isRTL: Bool) { init(line: CTLine, range: NSRange, frame: CGRect, strikethroughItems: [InstantPageTextStrikethroughItem], markedItems: [InstantPageTextMarkedItem], imageItems: [InstantPageTextImageItem], anchorItems: [InstantPageTextAnchorItem], isRTL: Bool) {
self.line = line self.line = line
self.range = range self.range = range
self.frame = frame self.frame = frame
self.strikethroughItems = strikethroughItems self.strikethroughItems = strikethroughItems
self.markedItems = markedItems self.markedItems = markedItems
self.imageItems = imageItems self.imageItems = imageItems
self.anchorItems = anchorItems
self.isRTL = isRTL self.isRTL = isRTL
} }
} }
@ -70,6 +76,7 @@ final class InstantPageTextItem: InstantPageItem {
var frame: CGRect var frame: CGRect
let alignment: NSTextAlignment let alignment: NSTextAlignment
let medias: [InstantPageMedia] = [] let medias: [InstantPageMedia] = []
let anchors: [String: Int]
let wantsNode: Bool = false let wantsNode: Bool = false
let separatesTiles: Bool = false let separatesTiles: Bool = false
var selectable: Bool = true var selectable: Bool = true
@ -85,13 +92,18 @@ final class InstantPageTextItem: InstantPageItem {
self.lines = lines self.lines = lines
var index = 0 var index = 0
var rtlLineIndices = Set<Int>() var rtlLineIndices = Set<Int>()
var anchors: [String: Int] = [:]
for line in lines { for line in lines {
if line.isRTL { if line.isRTL {
rtlLineIndices.insert(index) rtlLineIndices.insert(index)
} }
for anchor in line.anchorItems {
anchors[anchor.name] = index
}
index += 1 index += 1
} }
self.rtlLineIndices = rtlLineIndices self.rtlLineIndices = rtlLineIndices
self.anchors = anchors
} }
func drawInTile(context: CGContext) { func drawInTile(context: CGContext) {
@ -410,6 +422,15 @@ func attributedStringForRichText(_ text: RichText, styleStack: InstantPageTextSt
let delegate = CTRunDelegateCreate(&callbacks, extentBuffer) let delegate = CTRunDelegateCreate(&callbacks, extentBuffer)
let attrDictionaryDelegate = [(kCTRunDelegateAttributeName as NSAttributedStringKey): (delegate as Any), NSAttributedStringKey(rawValue: InstantPageMediaIdAttribute): id.id] let attrDictionaryDelegate = [(kCTRunDelegateAttributeName as NSAttributedStringKey): (delegate as Any), NSAttributedStringKey(rawValue: InstantPageMediaIdAttribute): id.id]
return NSAttributedString(string: " ", attributes: attrDictionaryDelegate) return NSAttributedString(string: " ", attributes: attrDictionaryDelegate)
case let .anchor(text, name):
styleStack.push(.anchor(name))
var text = text
if case .empty = text {
text = .plain("\u{200b}")
}
let result = attributedStringForRichText(text, styleStack: styleStack, url: url)
styleStack.pop()
return result
} }
} }
@ -497,6 +518,7 @@ func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFlo
var strikethroughItems: [InstantPageTextStrikethroughItem] = [] var strikethroughItems: [InstantPageTextStrikethroughItem] = []
var markedItems: [InstantPageTextMarkedItem] = [] var markedItems: [InstantPageTextMarkedItem] = []
var anchorItems: [InstantPageTextAnchorItem] = []
string.enumerateAttributes(in: lineRange, options: []) { attributes, range, _ in string.enumerateAttributes(in: lineRange, options: []) { attributes, range, _ in
if let _ = attributes[NSAttributedStringKey.strikethroughStyle] { if let _ = attributes[NSAttributedStringKey.strikethroughStyle] {
@ -507,6 +529,8 @@ func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFlo
let lowerX = floor(CTLineGetOffsetForStringIndex(line, range.location, nil)) let lowerX = floor(CTLineGetOffsetForStringIndex(line, range.location, nil))
let upperX = ceil(CTLineGetOffsetForStringIndex(line, range.location + range.length, nil)) let upperX = ceil(CTLineGetOffsetForStringIndex(line, range.location + range.length, nil))
markedItems.append(InstantPageTextMarkedItem(frame: CGRect(x: currentLineOrigin.x + lowerX, y: currentLineOrigin.y, width: upperX - lowerX, height: fontLineHeight), color: item)) markedItems.append(InstantPageTextMarkedItem(frame: CGRect(x: currentLineOrigin.x + lowerX, y: currentLineOrigin.y, width: upperX - lowerX, height: fontLineHeight), color: item))
} else if let item = attributes[NSAttributedStringKey.init(rawValue: InstantPageAnchorAttribute)] as? String {
anchorItems.append(InstantPageTextAnchorItem(name: item))
} }
} }
@ -556,7 +580,7 @@ func layoutTextItemWithString(_ string: NSAttributedString, boundingWidth: CGFlo
} }
let height = !fontLineHeight.isZero ? fontLineHeight : maxImageHeight let height = !fontLineHeight.isZero ? fontLineHeight : maxImageHeight
let textLine = InstantPageTextLine(line: line, range: lineRange, frame: CGRect(x: currentLineOrigin.x, y: currentLineOrigin.y, width: lineWidth, height: height), strikethroughItems: strikethroughItems, markedItems: markedItems, imageItems: lineImageItems, isRTL: isRTL) let textLine = InstantPageTextLine(line: line, range: lineRange, frame: CGRect(x: currentLineOrigin.x, y: currentLineOrigin.y, width: lineWidth, height: height), strikethroughItems: strikethroughItems, markedItems: markedItems, imageItems: lineImageItems, anchorItems: anchorItems, isRTL: isRTL)
lines.append(textLine) lines.append(textLine)
imageItems.append(contentsOf: lineImageItems) imageItems.append(contentsOf: lineImageItems)

View File

@ -16,11 +16,13 @@ enum InstantPageTextStyle {
case superscript case superscript
case markerColor(UIColor) case markerColor(UIColor)
case marker case marker
case anchor(String)
} }
let InstantPageLineSpacingFactorAttribute = "LineSpacingFactorAttribute" let InstantPageLineSpacingFactorAttribute = "LineSpacingFactorAttribute"
let InstantPageMarkerColorAttribute = "MarkerColorAttribute" let InstantPageMarkerColorAttribute = "MarkerColorAttribute"
let InstantPageMediaIdAttribute = "MediaIdAttribute" let InstantPageMediaIdAttribute = "MediaIdAttribute"
let InstantPageAnchorAttribute = "AnchorAttribute"
final class InstantPageTextStyleStack { final class InstantPageTextStyleStack {
private var items: [InstantPageTextStyle] = [] private var items: [InstantPageTextStyle] = []
@ -48,6 +50,7 @@ final class InstantPageTextStyleStack {
var baselineOffset: CGFloat? var baselineOffset: CGFloat?
var markerColor: UIColor? var markerColor: UIColor?
var marker: Bool? var marker: Bool?
var anchor: String?
for item in self.items.reversed() { for item in self.items.reversed() {
switch item { switch item {
@ -104,6 +107,10 @@ final class InstantPageTextStyleStack {
if marker == nil { if marker == nil {
marker = true marker = true
} }
case let .anchor(name):
if anchor == nil {
anchor = name
}
} }
} }
@ -177,6 +184,10 @@ final class InstantPageTextStyleStack {
attributes[NSAttributedStringKey(rawValue: InstantPageMarkerColorAttribute)] = markerColor attributes[NSAttributedStringKey(rawValue: InstantPageMarkerColorAttribute)] = markerColor
} }
if let anchor = anchor {
attributes[NSAttributedStringKey(rawValue: InstantPageAnchorAttribute)] = anchor
}
return attributes return attributes
} }
} }

View File

@ -288,6 +288,9 @@ private final class LanguageSuggestionAlertContentNode: AlertContentNode {
} }
override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize { override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
var size = size
size.width = min(size.width , 270.0)
self.validLayout = size self.validLayout = size
var origin: CGPoint = CGPoint(x: 0.0, y: 17.0) var origin: CGPoint = CGPoint(x: 0.0, y: 17.0)