mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-08 15:23:12 +00:00
Added CollectionIndexNode
This commit is contained in:
parent
c8a8570465
commit
d71707c31b
@ -76,6 +76,7 @@
|
|||||||
D03E7DF91C96C5F200C07816 /* NSWeakReference.m in Sources */ = {isa = PBXBuildFile; fileRef = D03E7DF71C96C5F200C07816 /* NSWeakReference.m */; };
|
D03E7DF91C96C5F200C07816 /* NSWeakReference.m in Sources */ = {isa = PBXBuildFile; fileRef = D03E7DF71C96C5F200C07816 /* NSWeakReference.m */; };
|
||||||
D03E7DFF1C96F7B400C07816 /* StatusBarManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E7DFE1C96F7B400C07816 /* StatusBarManager.swift */; };
|
D03E7DFF1C96F7B400C07816 /* StatusBarManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E7DFE1C96F7B400C07816 /* StatusBarManager.swift */; };
|
||||||
D03E7E011C974AB300C07816 /* DisplayLinkDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E7E001C974AB300C07816 /* DisplayLinkDispatcher.swift */; };
|
D03E7E011C974AB300C07816 /* DisplayLinkDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E7E001C974AB300C07816 /* DisplayLinkDispatcher.swift */; };
|
||||||
|
D04554AA21BDB93E007A6DD9 /* CollectionIndexNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04554A921BDB93E007A6DD9 /* CollectionIndexNode.swift */; };
|
||||||
D04C468E1F4C97BE00D30FE1 /* PageControlNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04C468D1F4C97BE00D30FE1 /* PageControlNode.swift */; };
|
D04C468E1F4C97BE00D30FE1 /* PageControlNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04C468D1F4C97BE00D30FE1 /* PageControlNode.swift */; };
|
||||||
D05174B31EAA833200A1BF36 /* CASeeThroughTracingLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = D05174B11EAA833200A1BF36 /* CASeeThroughTracingLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
D05174B31EAA833200A1BF36 /* CASeeThroughTracingLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = D05174B11EAA833200A1BF36 /* CASeeThroughTracingLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
D05174B41EAA833200A1BF36 /* CASeeThroughTracingLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = D05174B21EAA833200A1BF36 /* CASeeThroughTracingLayer.m */; };
|
D05174B41EAA833200A1BF36 /* CASeeThroughTracingLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = D05174B21EAA833200A1BF36 /* CASeeThroughTracingLayer.m */; };
|
||||||
@ -254,6 +255,7 @@
|
|||||||
D03E7DF71C96C5F200C07816 /* NSWeakReference.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSWeakReference.m; sourceTree = "<group>"; };
|
D03E7DF71C96C5F200C07816 /* NSWeakReference.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSWeakReference.m; sourceTree = "<group>"; };
|
||||||
D03E7DFE1C96F7B400C07816 /* StatusBarManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBarManager.swift; sourceTree = "<group>"; };
|
D03E7DFE1C96F7B400C07816 /* StatusBarManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBarManager.swift; sourceTree = "<group>"; };
|
||||||
D03E7E001C974AB300C07816 /* DisplayLinkDispatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayLinkDispatcher.swift; sourceTree = "<group>"; };
|
D03E7E001C974AB300C07816 /* DisplayLinkDispatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayLinkDispatcher.swift; sourceTree = "<group>"; };
|
||||||
|
D04554A921BDB93E007A6DD9 /* CollectionIndexNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionIndexNode.swift; sourceTree = "<group>"; };
|
||||||
D04C468D1F4C97BE00D30FE1 /* PageControlNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageControlNode.swift; sourceTree = "<group>"; };
|
D04C468D1F4C97BE00D30FE1 /* PageControlNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageControlNode.swift; sourceTree = "<group>"; };
|
||||||
D05174B11EAA833200A1BF36 /* CASeeThroughTracingLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASeeThroughTracingLayer.h; sourceTree = "<group>"; };
|
D05174B11EAA833200A1BF36 /* CASeeThroughTracingLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CASeeThroughTracingLayer.h; sourceTree = "<group>"; };
|
||||||
D05174B21EAA833200A1BF36 /* CASeeThroughTracingLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASeeThroughTracingLayer.m; sourceTree = "<group>"; };
|
D05174B21EAA833200A1BF36 /* CASeeThroughTracingLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CASeeThroughTracingLayer.m; sourceTree = "<group>"; };
|
||||||
@ -508,6 +510,7 @@
|
|||||||
D0FA08C32048803C00DD23FC /* TextNode.swift */,
|
D0FA08C32048803C00DD23FC /* TextNode.swift */,
|
||||||
D0FA08C5204880C900DD23FC /* ImmediateTextNode.swift */,
|
D0FA08C5204880C900DD23FC /* ImmediateTextNode.swift */,
|
||||||
D0CA3F892073F7650042D2B6 /* LinkHighlightingNode.swift */,
|
D0CA3F892073F7650042D2B6 /* LinkHighlightingNode.swift */,
|
||||||
|
D04554A921BDB93E007A6DD9 /* CollectionIndexNode.swift */,
|
||||||
);
|
);
|
||||||
name = Nodes;
|
name = Nodes;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1012,6 +1015,7 @@
|
|||||||
D0CA3F8A2073F7650042D2B6 /* LinkHighlightingNode.swift in Sources */,
|
D0CA3F8A2073F7650042D2B6 /* LinkHighlightingNode.swift in Sources */,
|
||||||
D0C2DFCB1CC4431D0044FF83 /* ListViewAnimation.swift in Sources */,
|
D0C2DFCB1CC4431D0044FF83 /* ListViewAnimation.swift in Sources */,
|
||||||
D0BE93191E8ED71100DCC1E6 /* NativeWindowHostView.swift in Sources */,
|
D0BE93191E8ED71100DCC1E6 /* NativeWindowHostView.swift in Sources */,
|
||||||
|
D04554AA21BDB93E007A6DD9 /* CollectionIndexNode.swift in Sources */,
|
||||||
D05CC3251B695B0700E235A3 /* NavigationBarProxy.m in Sources */,
|
D05CC3251B695B0700E235A3 /* NavigationBarProxy.m in Sources */,
|
||||||
D05174B41EAA833200A1BF36 /* CASeeThroughTracingLayer.m in Sources */,
|
D05174B41EAA833200A1BF36 /* CASeeThroughTracingLayer.m in Sources */,
|
||||||
D03AA4D9202D8E5E0056C405 /* GlobalOverlayPresentationContext.swift in Sources */,
|
D03AA4D9202D8E5E0056C405 /* GlobalOverlayPresentationContext.swift in Sources */,
|
||||||
|
|||||||
141
Display/CollectionIndexNode.swift
Normal file
141
Display/CollectionIndexNode.swift
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
import Foundation
|
||||||
|
import AsyncDisplayKit
|
||||||
|
|
||||||
|
private let titleFont = Font.bold(11.0)
|
||||||
|
|
||||||
|
public final class CollectionIndexNode: ASDisplayNode {
|
||||||
|
public static let searchIndex: String = "_$search$_"
|
||||||
|
|
||||||
|
private var currentSize: CGSize?
|
||||||
|
private var currentSections: [String] = []
|
||||||
|
private var currentColor: UIColor?
|
||||||
|
private var titleNodes: [String: (node: ImmediateTextNode, size: CGSize)] = [:]
|
||||||
|
|
||||||
|
private var currentSelectedIndex: String?
|
||||||
|
public var indexSelected: ((String) -> Void)?
|
||||||
|
|
||||||
|
override public init() {
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func didLoad() {
|
||||||
|
super.didLoad()
|
||||||
|
|
||||||
|
self.view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:))))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update(size: CGSize, color: UIColor, sections: [String], transition: ContainedViewLayoutTransition) {
|
||||||
|
if self.currentColor == nil || !color.isEqual(self.currentColor) {
|
||||||
|
self.currentColor = color
|
||||||
|
for (title, nodeAndSize) in self.titleNodes {
|
||||||
|
nodeAndSize.node.attributedText = NSAttributedString(string: title, font: titleFont, textColor: color)
|
||||||
|
let _ = nodeAndSize.node.updateLayout(CGSize(width: 100.0, height: 100.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.currentSize == size && self.currentSections == sections {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.currentSize = size
|
||||||
|
self.currentSections = sections
|
||||||
|
|
||||||
|
let itemHeight: CGFloat = 15.0
|
||||||
|
let verticalInset: CGFloat = 10.0
|
||||||
|
let maxHeight = size.height - verticalInset * 2.0
|
||||||
|
|
||||||
|
let maxItemCount = min(sections.count, Int(floor(maxHeight / itemHeight)))
|
||||||
|
let skipCount = Int(ceil(CGFloat(sections.count) / CGFloat(maxItemCount)))
|
||||||
|
let actualCount: CGFloat = ceil(CGFloat(sections.count) / CGFloat(skipCount))
|
||||||
|
|
||||||
|
let totalHeight = actualCount * itemHeight
|
||||||
|
let verticalOrigin = verticalInset + floor((maxHeight - totalHeight) / 2.0)
|
||||||
|
|
||||||
|
var validTitles = Set<String>()
|
||||||
|
|
||||||
|
var index = 0
|
||||||
|
var displayIndex = 0
|
||||||
|
while index < sections.count {
|
||||||
|
let title = sections[index]
|
||||||
|
let nodeAndSize: (node: ImmediateTextNode, size: CGSize)
|
||||||
|
var animate = false
|
||||||
|
if let current = self.titleNodes[title] {
|
||||||
|
animate = true
|
||||||
|
nodeAndSize = current
|
||||||
|
} else {
|
||||||
|
let node = ImmediateTextNode()
|
||||||
|
node.attributedText = NSAttributedString(string: title, font: titleFont, textColor: color)
|
||||||
|
let nodeSize = node.updateLayout(CGSize(width: 100.0, height: 100.0))
|
||||||
|
nodeAndSize = (node, nodeSize)
|
||||||
|
self.addSubnode(node)
|
||||||
|
self.titleNodes[title] = nodeAndSize
|
||||||
|
}
|
||||||
|
validTitles.insert(title)
|
||||||
|
let previousPosition = nodeAndSize.node.position
|
||||||
|
nodeAndSize.node.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - nodeAndSize.size.width) / 2.0), y: verticalOrigin + itemHeight * CGFloat(displayIndex) + floor((itemHeight - nodeAndSize.size.height) / 2.0)), size: nodeAndSize.size)
|
||||||
|
if animate {
|
||||||
|
transition.animatePosition(node: nodeAndSize.node, from: previousPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
index += skipCount
|
||||||
|
displayIndex += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var removeTitles: [String] = []
|
||||||
|
for title in self.titleNodes.keys {
|
||||||
|
if !validTitles.contains(title) {
|
||||||
|
removeTitles.append(title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for title in removeTitles {
|
||||||
|
self.titleNodes.removeValue(forKey: title)?.node.removeFromSupernode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||||
|
if self.bounds.insetBy(dx: -5.0, dy: 0.0).contains(point) {
|
||||||
|
return self.view
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||||
|
var locationTitleAndPosition: (String, CGFloat)?
|
||||||
|
let location = recognizer.location(in: self.view)
|
||||||
|
for (title, nodeAndSize) in self.titleNodes {
|
||||||
|
let nodeFrame = nodeAndSize.node.frame
|
||||||
|
if location.y >= nodeFrame.minY - 5.0 && location.y <= nodeFrame.maxY + 5.0 {
|
||||||
|
if let currentTitleAndPosition = locationTitleAndPosition {
|
||||||
|
let distance = abs(nodeFrame.midY - location.y)
|
||||||
|
let previousDistance = abs(currentTitleAndPosition.1 - location.y)
|
||||||
|
if distance < previousDistance {
|
||||||
|
locationTitleAndPosition = (title, nodeFrame.midY)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
locationTitleAndPosition = (title, nodeFrame.midY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let locationTitle = locationTitleAndPosition?.0
|
||||||
|
switch recognizer.state {
|
||||||
|
case .began:
|
||||||
|
self.currentSelectedIndex = locationTitle
|
||||||
|
if let locationTitle = locationTitle {
|
||||||
|
self.indexSelected?(locationTitle)
|
||||||
|
}
|
||||||
|
case .changed:
|
||||||
|
if locationTitle != self.currentSelectedIndex {
|
||||||
|
self.currentSelectedIndex = locationTitle
|
||||||
|
if let locationTitle = locationTitle {
|
||||||
|
self.indexSelected?(locationTitle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case .cancelled, .ended:
|
||||||
|
self.currentSelectedIndex = nil
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -227,6 +227,12 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var indicatorStyle: UIScrollViewIndicatorStyle = .default {
|
||||||
|
didSet {
|
||||||
|
self.scrollView.indicatorStyle = self.indicatorStyle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public private(set) var opaqueState: Any?
|
public private(set) var opaqueState: Any?
|
||||||
|
|
||||||
public override init() {
|
public override init() {
|
||||||
@ -755,6 +761,9 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
|||||||
private func applyPresentaionLayoutTransition(_ presentationLayoutTransition: GridNodePresentationLayoutTransition, removedNodes: [GridItemNode], updateLayoutTransition: ContainedViewLayoutTransition?, customScrollToItem: Bool, itemTransition: ContainedViewLayoutTransition, synchronousLoads: Bool, updatingLayout: Bool, completion: (GridNodeDisplayedItemRange) -> Void) {
|
private func applyPresentaionLayoutTransition(_ presentationLayoutTransition: GridNodePresentationLayoutTransition, removedNodes: [GridItemNode], updateLayoutTransition: ContainedViewLayoutTransition?, customScrollToItem: Bool, itemTransition: ContainedViewLayoutTransition, synchronousLoads: Bool, updatingLayout: Bool, completion: (GridNodeDisplayedItemRange) -> Void) {
|
||||||
let boundsTransition: ContainedViewLayoutTransition = updateLayoutTransition ?? .immediate
|
let boundsTransition: ContainedViewLayoutTransition = updateLayoutTransition ?? .immediate
|
||||||
|
|
||||||
|
var addedNodes = false
|
||||||
|
let verticalIndicator = self.scrollView.subviews.last as? UIImageView
|
||||||
|
|
||||||
var previousItemFrames: [WrappedGridItemNode: CGRect]?
|
var previousItemFrames: [WrappedGridItemNode: CGRect]?
|
||||||
var saveItemFrames = false
|
var saveItemFrames = false
|
||||||
switch presentationLayoutTransition.transition {
|
switch presentationLayoutTransition.transition {
|
||||||
@ -782,7 +791,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
|||||||
previousItemFrames = itemFrames
|
previousItemFrames = itemFrames
|
||||||
}
|
}
|
||||||
|
|
||||||
applyingContentOffset = true
|
self.applyingContentOffset = true
|
||||||
|
|
||||||
let previousBounds = self.bounds
|
let previousBounds = self.bounds
|
||||||
self.scrollView.contentSize = presentationLayoutTransition.layout.contentSize
|
self.scrollView.contentSize = presentationLayoutTransition.layout.contentSize
|
||||||
@ -819,6 +828,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
|||||||
let itemNode = self.items[item.index].node(layout: presentationLayoutTransition.layout.layout, synchronousLoad: synchronousLoads)
|
let itemNode = self.items[item.index].node(layout: presentationLayoutTransition.layout.layout, synchronousLoad: synchronousLoads)
|
||||||
itemNode.frame = item.frame
|
itemNode.frame = item.frame
|
||||||
self.addItemNode(index: item.index, itemNode: itemNode, lowestSectionNode: lowestSectionNode)
|
self.addItemNode(index: item.index, itemNode: itemNode, lowestSectionNode: lowestSectionNode)
|
||||||
|
addedNodes = true
|
||||||
itemNode.updateLayout(item: self.items[item.index], size: item.frame.size, isVisible: bounds.intersects(item.frame), synchronousLoads: synchronousLoads)
|
itemNode.updateLayout(item: self.items[item.index], size: item.frame.size, isVisible: bounds.intersects(item.frame), synchronousLoads: synchronousLoads)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -845,6 +855,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
|||||||
let sectionNode = section.section.node()
|
let sectionNode = section.section.node()
|
||||||
sectionNode.frame = sectionFrame
|
sectionNode.frame = sectionFrame
|
||||||
self.addSectionNode(section: wrappedSection, sectionNode: sectionNode)
|
self.addSectionNode(section: wrappedSection, sectionNode: sectionNode)
|
||||||
|
addedNodes = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1100,6 +1111,12 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if addedNodes {
|
||||||
|
if let verticalIndicator = verticalIndicator, self.scrollView.subviews.last !== verticalIndicator {
|
||||||
|
verticalIndicator.superview?.bringSubview(toFront: verticalIndicator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let presentationLayoutUpdated = self.presentationLayoutUpdated {
|
if let presentationLayoutUpdated = self.presentationLayoutUpdated {
|
||||||
presentationLayoutUpdated(GridNodeCurrentPresentationLayout(layout: presentationLayoutTransition.layout.layout, contentOffset: presentationLayoutTransition.layout.contentOffset, contentSize: presentationLayoutTransition.layout.contentSize), updateLayoutTransition ?? presentationLayoutTransition.transition)
|
presentationLayoutUpdated(GridNodeCurrentPresentationLayout(layout: presentationLayoutTransition.layout.layout, contentOffset: presentationLayoutTransition.layout.contentOffset, contentSize: presentationLayoutTransition.layout.contentSize), updateLayoutTransition ?? presentationLayoutTransition.transition)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,10 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
public struct ImmediateTextNodeLayoutInfo {
|
||||||
|
public let size: CGSize
|
||||||
|
public let truncated: Bool
|
||||||
|
}
|
||||||
|
|
||||||
public class ImmediateTextNode: TextNode {
|
public class ImmediateTextNode: TextNode {
|
||||||
public var attributedText: NSAttributedString?
|
public var attributedText: NSAttributedString?
|
||||||
public var textAlignment: NSTextAlignment = .natural
|
public var textAlignment: NSTextAlignment = .natural
|
||||||
@ -31,6 +36,13 @@ public class ImmediateTextNode: TextNode {
|
|||||||
return layout.size
|
return layout.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func updateLayoutInfo(_ constrainedSize: CGSize) -> ImmediateTextNodeLayoutInfo {
|
||||||
|
let makeLayout = TextNode.asyncLayout(self)
|
||||||
|
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, lineSpacing: self.lineSpacing, cutout: nil, insets: self.insets))
|
||||||
|
let _ = apply()
|
||||||
|
return ImmediateTextNodeLayoutInfo(size: layout.size, truncated: layout.truncated)
|
||||||
|
}
|
||||||
|
|
||||||
override public func didLoad() {
|
override public func didLoad() {
|
||||||
super.didLoad()
|
super.didLoad()
|
||||||
|
|
||||||
|
|||||||
@ -97,11 +97,12 @@ public final class TextNodeLayout: NSObject {
|
|||||||
fileprivate let cutout: TextNodeCutout?
|
fileprivate let cutout: TextNodeCutout?
|
||||||
fileprivate let insets: UIEdgeInsets
|
fileprivate let insets: UIEdgeInsets
|
||||||
public let size: CGSize
|
public let size: CGSize
|
||||||
|
public let truncated: Bool
|
||||||
fileprivate let firstLineOffset: CGFloat
|
fileprivate let firstLineOffset: CGFloat
|
||||||
fileprivate let lines: [TextNodeLine]
|
fileprivate let lines: [TextNodeLine]
|
||||||
public let hasRTL: Bool
|
public let hasRTL: Bool
|
||||||
|
|
||||||
fileprivate init(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment, lineSpacing: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, size: CGSize, firstLineOffset: CGFloat, lines: [TextNodeLine], backgroundColor: UIColor?) {
|
fileprivate init(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment, lineSpacing: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, size: CGSize, truncated: Bool, firstLineOffset: CGFloat, lines: [TextNodeLine], backgroundColor: UIColor?) {
|
||||||
self.attributedString = attributedString
|
self.attributedString = attributedString
|
||||||
self.maximumNumberOfLines = maximumNumberOfLines
|
self.maximumNumberOfLines = maximumNumberOfLines
|
||||||
self.truncationType = truncationType
|
self.truncationType = truncationType
|
||||||
@ -111,6 +112,7 @@ public final class TextNodeLayout: NSObject {
|
|||||||
self.cutout = cutout
|
self.cutout = cutout
|
||||||
self.insets = insets
|
self.insets = insets
|
||||||
self.size = size
|
self.size = size
|
||||||
|
self.truncated = truncated
|
||||||
self.firstLineOffset = firstLineOffset
|
self.firstLineOffset = firstLineOffset
|
||||||
self.lines = lines
|
self.lines = lines
|
||||||
self.backgroundColor = backgroundColor
|
self.backgroundColor = backgroundColor
|
||||||
@ -327,7 +329,7 @@ public class TextNode: ASDisplayNode {
|
|||||||
var maybeTypesetter: CTTypesetter?
|
var maybeTypesetter: CTTypesetter?
|
||||||
maybeTypesetter = CTTypesetterCreateWithAttributedString(attributedString as CFAttributedString)
|
maybeTypesetter = CTTypesetterCreateWithAttributedString(attributedString as CFAttributedString)
|
||||||
if maybeTypesetter == nil {
|
if maybeTypesetter == nil {
|
||||||
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), firstLineOffset: 0.0, lines: [], backgroundColor: backgroundColor)
|
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], backgroundColor: backgroundColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
let typesetter = maybeTypesetter!
|
let typesetter = maybeTypesetter!
|
||||||
@ -364,6 +366,7 @@ public class TextNode: ASDisplayNode {
|
|||||||
|
|
||||||
let firstLineOffset = floorToScreenPixels(fontDescent)
|
let firstLineOffset = floorToScreenPixels(fontDescent)
|
||||||
|
|
||||||
|
var truncated = false
|
||||||
var first = true
|
var first = true
|
||||||
while true {
|
while true {
|
||||||
var lineConstrainedWidth = constrainedSize.width
|
var lineConstrainedWidth = constrainedSize.width
|
||||||
@ -419,6 +422,7 @@ public class TextNode: ASDisplayNode {
|
|||||||
let truncationToken = CTLineCreateWithAttributedString(truncatedTokenString)
|
let truncationToken = CTLineCreateWithAttributedString(truncatedTokenString)
|
||||||
|
|
||||||
coreTextLine = CTLineCreateTruncatedLine(originalLine, Double(constrainedSize.width), truncationType, truncationToken) ?? truncationToken
|
coreTextLine = CTLineCreateTruncatedLine(originalLine, Double(constrainedSize.width), truncationType, truncationToken) ?? truncationToken
|
||||||
|
truncated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
let lineWidth = min(constrainedSize.width, ceil(CGFloat(CTLineGetTypographicBounds(coreTextLine, nil, nil, nil) - CTLineGetTrailingWhitespaceWidth(coreTextLine))))
|
let lineWidth = min(constrainedSize.width, ceil(CGFloat(CTLineGetTypographicBounds(coreTextLine, nil, nil, nil) - CTLineGetTrailingWhitespaceWidth(coreTextLine))))
|
||||||
@ -485,9 +489,9 @@ public class TextNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(width: ceil(layoutSize.width) + insets.left + insets.right, height: ceil(layoutSize.height) + insets.top + insets.bottom), firstLineOffset: firstLineOffset, lines: lines, backgroundColor: backgroundColor)
|
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(width: ceil(layoutSize.width) + insets.left + insets.right, height: ceil(layoutSize.height) + insets.top + insets.bottom), truncated: truncated, firstLineOffset: firstLineOffset, lines: lines, backgroundColor: backgroundColor)
|
||||||
} else {
|
} else {
|
||||||
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), firstLineOffset: 0.0, lines: [], backgroundColor: backgroundColor)
|
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, alignment: alignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], backgroundColor: backgroundColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user