Skip to content

Commit 62fcf1c

Browse files
authored
Optimized Textview size calculations (#207)
* Updated recalculation of grid cell bounds * Moved size calculation of AutogrowingTextView to background
1 parent 34c484e commit 62fcf1c

File tree

6 files changed

+40
-23
lines changed

6 files changed

+40
-23
lines changed

Diff for: Proton/Sources/Swift/Attachment/Attachment.swift

+8
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,14 @@ open class Attachment: NSTextAttachment, BoundsObserving {
329329
public override func attachmentBounds(for textContainer: NSTextContainer?, proposedLineFragment lineFrag: CGRect, glyphPosition position: CGPoint, characterIndex charIndex: Int) -> CGRect {
330330
self.indexInContainer = charIndex
331331

332+
// When calculating size of EditorView, this may be called on the background thread. Since size of attachment depends on contained view,
333+
// we need to put the bounds calculation back on main.
334+
guard Thread.isMainThread else {
335+
return DispatchQueue.main.sync {
336+
attachmentBounds(for: textContainer, proposedLineFragment: lineFrag, glyphPosition: position, characterIndex: charIndex)
337+
}
338+
}
339+
332340
guard let textContainer = textContainer,
333341
textContainer.size.height > 0,
334342
textContainer.size.width > 0

Diff for: Proton/Sources/Swift/Base/AutogrowingTextView.swift

+10-6
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,10 @@ class AutogrowingTextView: UITextView {
6565
private func recalculateHeight() {
6666
let bounds = self.bounds.integral
6767
let fittingSize = self.calculatedSize(attributedText: attributedText, frame: frame.size, textContainerInset: textContainerInset)
68+
6869
self.isScrollEnabled = (fittingSize.height > bounds.height) || (self.maxHeight > 0 && self.maxHeight < fittingSize.height)
69-
heightAnchorConstraint.constant = min(fittingSize.height, contentSize.height)
70+
self.heightAnchorConstraint.constant = min(fittingSize.height, self.contentSize.height)
71+
7072
}
7173

7274
override open func sizeThatFits(_ size: CGSize) -> CGSize {
@@ -90,11 +92,13 @@ class AutogrowingTextView: UITextView {
9092
}
9193

9294
private func calculatedSize(attributedText: NSAttributedString, frame: CGSize, textContainerInset: UIEdgeInsets) -> CGSize {
93-
// Adjust for horizontal paddings in textview to exclude from overall available width for attachment
94-
let horizontalAdjustments = (textContainer.lineFragmentPadding * 2) + (textContainerInset.left + textContainerInset.right)
95-
let boundingRect = attributedText.boundingRect(with: CGSize(width: frame.width - horizontalAdjustments, height: .greatestFiniteMagnitude), options: [.usesLineFragmentOrigin], context: nil).integral
95+
DispatchQueue.global(qos: .userInteractive).sync { [lineFragmentPadding = textContainer.lineFragmentPadding ] in
96+
// Adjust for horizontal paddings in textview to exclude from overall available width for attachment
97+
let horizontalAdjustments = (lineFragmentPadding * 2) + (textContainerInset.left + textContainerInset.right)
98+
let boundingRect = attributedText.boundingRect(with: CGSize(width: frame.width - horizontalAdjustments, height: .greatestFiniteMagnitude), options: [.usesLineFragmentOrigin], context: nil).integral
9699

97-
let insets = UIEdgeInsets(top: -textContainerInset.top, left: -textContainerInset.left, bottom: -textContainerInset.bottom, right: -textContainerInset.right)
98-
return boundingRect.inset(by: insets).size
100+
let insets = UIEdgeInsets(top: -textContainerInset.top, left: -textContainerInset.left, bottom: -textContainerInset.bottom, right: -textContainerInset.right)
101+
return boundingRect.inset(by: insets).size
102+
}
99103
}
100104
}

Diff for: Proton/Sources/Swift/Grid/View/GridContentView.swift

+22-17
Original file line numberDiff line numberDiff line change
@@ -283,38 +283,43 @@ class GridContentView: UIScrollView {
283283
recalculateCellBounds()
284284
}
285285

286-
private func recalculateCellBounds() {
286+
private func recalculateCellBounds(initiatingCell: GridCell? = nil) {
287287
frozenRowsConstraints.forEach { $0.isActive = false }
288288
frozenColumnsConstraints.forEach { $0.isActive = false }
289289
removeConstraints(frozenRowsConstraints + frozenColumnsConstraints)
290290

291-
for c in grid.cells.reversed() {
291+
var cells = grid.cells
292+
if let initiatingCell {
293+
cells = [initiatingCell]
294+
}
295+
296+
for cell in cells {
292297
// TODO: Optimize to recalculate frames for affected cells only i.e. row>=current
293298

294299
// Set the frame of the cell before adding to superview
295300
// This is required to avoid breaking layout constraints
296301
// as default size is 0
297-
let frame = grid.frameForCell(c, basedOn: bounds.size)
298-
c.frame = frame
299-
c.contentView.frame = frame
300-
c.widthAnchorConstraint.constant = frame.width
301-
c.heightAnchorConstraint.constant = frame.height
302+
let frame = grid.frameForCell(cell, basedOn: bounds.size)
303+
cell.frame = frame
304+
cell.contentView.frame = frame
305+
cell.widthAnchorConstraint.constant = frame.width
306+
cell.heightAnchorConstraint.constant = frame.height
302307

303308
// Add to grid if this is a newly inserted cell after initial setup.
304309
// A new cell may exist as a result of inserting a new row/column
305310
// or splitting an existing merged cell
306-
if c.contentView.superview == nil {
307-
addSubview(c.contentView)
308-
c.topAnchorConstraint = c.contentView.topAnchor.constraint(equalTo: topAnchor, constant: frame.minY)
309-
c.leadingAnchorConstraint = c.contentView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: frame.minX)
311+
if cell.contentView.superview == nil {
312+
addSubview(cell.contentView)
313+
cell.topAnchorConstraint = cell.contentView.topAnchor.constraint(equalTo: topAnchor, constant: frame.minY)
314+
cell.leadingAnchorConstraint = cell.contentView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: frame.minX)
310315
} else {
311-
c.topAnchorConstraint?.constant = frame.minY
312-
c.leadingAnchorConstraint?.constant = frame.minX
316+
cell.topAnchorConstraint?.constant = frame.minY
317+
cell.leadingAnchorConstraint?.constant = frame.minX
313318
}
314319

315-
freezeColumnCellIfRequired(c)
316-
freezeRowCellIfRequired(c)
317-
gridContentViewDelegate?.gridContentView(self, didLayoutCell: c)
320+
freezeColumnCellIfRequired(cell)
321+
freezeRowCellIfRequired(cell)
322+
gridContentViewDelegate?.gridContentView(self, didLayoutCell: cell)
318323
}
319324

320325
boundsObserver?.didChangeBounds(CGRect(origin: bounds.origin, size: frame.size), oldBounds: bounds)
@@ -404,7 +409,7 @@ extension GridContentView: GridCellDelegate {
404409
grid.rowHeights[row].currentHeight = grid.maxContentHeightCellForRow(at: row)?.contentSize.height ?? 0
405410
}
406411

407-
recalculateCellBounds()
412+
recalculateCellBounds(initiatingCell: cell)
408413
gridContentViewDelegate?.gridContentView(self, didChangeBounds: cell.frame, in: cell)
409414
}
410415

Loading
Loading
Loading

0 commit comments

Comments
 (0)