mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Draw strikethrough and underline manually on iOS 18
This commit is contained in:
parent
e22b2b6b5a
commit
d70a0cf0e0
@ -112,13 +112,14 @@ private final class InteractiveTextNodeLine {
|
|||||||
let isTruncated: Bool
|
let isTruncated: Bool
|
||||||
let isRTL: Bool
|
let isRTL: Bool
|
||||||
var strikethroughs: [InteractiveTextNodeStrikethrough]
|
var strikethroughs: [InteractiveTextNodeStrikethrough]
|
||||||
|
var underlines: [InteractiveTextNodeStrikethrough]
|
||||||
var spoilers: [InteractiveTextNodeSpoiler]
|
var spoilers: [InteractiveTextNodeSpoiler]
|
||||||
var spoilerWords: [InteractiveTextNodeSpoiler]
|
var spoilerWords: [InteractiveTextNodeSpoiler]
|
||||||
var embeddedItems: [InteractiveTextNodeEmbeddedItem]
|
var embeddedItems: [InteractiveTextNodeEmbeddedItem]
|
||||||
var attachments: [InteractiveTextNodeAttachment]
|
var attachments: [InteractiveTextNodeAttachment]
|
||||||
let additionalTrailingLine: (CTLine, Double)?
|
let additionalTrailingLine: (CTLine, Double)?
|
||||||
|
|
||||||
init(line: CTLine, constrainedWidth: CGFloat, frame: CGRect, intrinsicWidth: CGFloat, ascent: CGFloat, descent: CGFloat, range: NSRange?, isTruncated: Bool, isRTL: Bool, strikethroughs: [InteractiveTextNodeStrikethrough], spoilers: [InteractiveTextNodeSpoiler], spoilerWords: [InteractiveTextNodeSpoiler], embeddedItems: [InteractiveTextNodeEmbeddedItem], attachments: [InteractiveTextNodeAttachment], additionalTrailingLine: (CTLine, Double)?) {
|
init(line: CTLine, constrainedWidth: CGFloat, frame: CGRect, intrinsicWidth: CGFloat, ascent: CGFloat, descent: CGFloat, range: NSRange?, isTruncated: Bool, isRTL: Bool, strikethroughs: [InteractiveTextNodeStrikethrough], underlines: [InteractiveTextNodeStrikethrough], spoilers: [InteractiveTextNodeSpoiler], spoilerWords: [InteractiveTextNodeSpoiler], embeddedItems: [InteractiveTextNodeEmbeddedItem], attachments: [InteractiveTextNodeAttachment], additionalTrailingLine: (CTLine, Double)?) {
|
||||||
self.line = line
|
self.line = line
|
||||||
self.constrainedWidth = constrainedWidth
|
self.constrainedWidth = constrainedWidth
|
||||||
self.frame = frame
|
self.frame = frame
|
||||||
@ -129,6 +130,7 @@ private final class InteractiveTextNodeLine {
|
|||||||
self.isTruncated = isTruncated
|
self.isTruncated = isTruncated
|
||||||
self.isRTL = isRTL
|
self.isRTL = isRTL
|
||||||
self.strikethroughs = strikethroughs
|
self.strikethroughs = strikethroughs
|
||||||
|
self.underlines = underlines
|
||||||
self.spoilers = spoilers
|
self.spoilers = spoilers
|
||||||
self.spoilerWords = spoilerWords
|
self.spoilerWords = spoilerWords
|
||||||
self.embeddedItems = embeddedItems
|
self.embeddedItems = embeddedItems
|
||||||
@ -1452,6 +1454,7 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn
|
|||||||
isTruncated: false,
|
isTruncated: false,
|
||||||
isRTL: false,
|
isRTL: false,
|
||||||
strikethroughs: [],
|
strikethroughs: [],
|
||||||
|
underlines: [],
|
||||||
spoilers: [],
|
spoilers: [],
|
||||||
spoilerWords: [],
|
spoilerWords: [],
|
||||||
embeddedItems: [],
|
embeddedItems: [],
|
||||||
@ -1493,6 +1496,7 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn
|
|||||||
isTruncated: false,
|
isTruncated: false,
|
||||||
isRTL: isRTL && segment.blockQuote == nil,
|
isRTL: isRTL && segment.blockQuote == nil,
|
||||||
strikethroughs: [],
|
strikethroughs: [],
|
||||||
|
underlines: [],
|
||||||
spoilers: [],
|
spoilers: [],
|
||||||
spoilerWords: [],
|
spoilerWords: [],
|
||||||
embeddedItems: [],
|
embeddedItems: [],
|
||||||
@ -1551,6 +1555,7 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn
|
|||||||
isTruncated: true,
|
isTruncated: true,
|
||||||
isRTL: lastLine.isRTL,
|
isRTL: lastLine.isRTL,
|
||||||
strikethroughs: [],
|
strikethroughs: [],
|
||||||
|
underlines: [],
|
||||||
spoilers: [],
|
spoilers: [],
|
||||||
spoilerWords: [],
|
spoilerWords: [],
|
||||||
embeddedItems: [],
|
embeddedItems: [],
|
||||||
@ -1605,6 +1610,7 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn
|
|||||||
isTruncated: true,
|
isTruncated: true,
|
||||||
isRTL: lastLine.isRTL,
|
isRTL: lastLine.isRTL,
|
||||||
strikethroughs: [],
|
strikethroughs: [],
|
||||||
|
underlines: [],
|
||||||
spoilers: [],
|
spoilers: [],
|
||||||
spoilerWords: [],
|
spoilerWords: [],
|
||||||
embeddedItems: [],
|
embeddedItems: [],
|
||||||
@ -1736,6 +1742,11 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn
|
|||||||
let upperX = ceil(CTLineGetOffsetForStringIndex(line.line, range.location + range.length, nil))
|
let upperX = ceil(CTLineGetOffsetForStringIndex(line.line, range.location + range.length, nil))
|
||||||
let x = lowerX < upperX ? lowerX : upperX
|
let x = lowerX < upperX ? lowerX : upperX
|
||||||
line.strikethroughs.append(InteractiveTextNodeStrikethrough(range: range, frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: line.frame.height)))
|
line.strikethroughs.append(InteractiveTextNodeStrikethrough(range: range, frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: line.frame.height)))
|
||||||
|
} else if let _ = attributes[NSAttributedString.Key.underlineStyle] {
|
||||||
|
let lowerX = floor(CTLineGetOffsetForStringIndex(line.line, range.location, nil))
|
||||||
|
let upperX = ceil(CTLineGetOffsetForStringIndex(line.line, range.location + range.length, nil))
|
||||||
|
let x = lowerX < upperX ? lowerX : upperX
|
||||||
|
line.underlines.append(InteractiveTextNodeStrikethrough(range: range, frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: line.frame.height)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let embeddedItem = (attributes[NSAttributedString.Key(rawValue: "TelegramEmbeddedItem")] as? AnyHashable ?? attributes[NSAttributedString.Key(rawValue: "Attribute__EmbeddedItem")] as? AnyHashable) {
|
if let embeddedItem = (attributes[NSAttributedString.Key(rawValue: "TelegramEmbeddedItem")] as? AnyHashable ?? attributes[NSAttributedString.Key(rawValue: "Attribute__EmbeddedItem")] as? AnyHashable) {
|
||||||
@ -2090,6 +2101,14 @@ final class TextContentItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private let drawUnderlinesManually: Bool = {
|
||||||
|
if #available(iOS 18.0, *) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
final class TextContentItemLayer: SimpleLayer {
|
final class TextContentItemLayer: SimpleLayer {
|
||||||
final class Params {
|
final class Params {
|
||||||
let item: TextContentItem
|
let item: TextContentItem
|
||||||
@ -2322,6 +2341,46 @@ final class TextContentItemLayer: SimpleLayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if drawUnderlinesManually {
|
||||||
|
if !line.strikethroughs.isEmpty {
|
||||||
|
for strikethrough in line.strikethroughs {
|
||||||
|
guard let lineRange = line.range else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var textColor: UIColor?
|
||||||
|
params.item.attributedString?.enumerateAttributes(in: NSMakeRange(lineRange.location, lineRange.length), options: []) { attributes, range, _ in
|
||||||
|
if range == strikethrough.range, let color = attributes[NSAttributedString.Key.foregroundColor] as? UIColor {
|
||||||
|
textColor = color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let textColor = textColor {
|
||||||
|
context.setFillColor(textColor.cgColor)
|
||||||
|
}
|
||||||
|
let frame = strikethrough.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY)
|
||||||
|
context.fill(CGRect(x: frame.minX, y: frame.midY, width: frame.width, height: 1.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !line.underlines.isEmpty {
|
||||||
|
for strikethrough in line.underlines {
|
||||||
|
guard let lineRange = line.range else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var textColor: UIColor?
|
||||||
|
params.item.attributedString?.enumerateAttributes(in: NSMakeRange(lineRange.location, lineRange.length), options: []) { attributes, range, _ in
|
||||||
|
if range == strikethrough.range, let color = attributes[NSAttributedString.Key.foregroundColor] as? UIColor {
|
||||||
|
textColor = color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let textColor = textColor {
|
||||||
|
context.setFillColor(textColor.cgColor)
|
||||||
|
}
|
||||||
|
let frame = strikethrough.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY)
|
||||||
|
context.fill(CGRect(x: frame.minX, y: frame.maxY - 2.0, width: frame.width, height: 1.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let (additionalTrailingLine, _) = line.additionalTrailingLine {
|
if let (additionalTrailingLine, _) = line.additionalTrailingLine {
|
||||||
context.textPosition = CGPoint(x: lineFrame.minX + line.intrinsicWidth, y: lineFrame.maxY - line.descent)
|
context.textPosition = CGPoint(x: lineFrame.minX + line.intrinsicWidth, y: lineFrame.maxY - line.descent)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user