Reputation: 11777
To summarize my problem: when I have many child views with widths equal to the width of their parent, where the parent's width is equal to their parent's width, there is a huge performance hit when the window is horizontally resized.
I am retrieving a large number of comments from an API and decided to create an individual NSView
for each comment and then vertically space them all within an NSScrollView
. Each individual comment grows to the height of the comment's text and matches the width of the container. This works fine when there aren't many loaded comments, but when the comments routinely exceed 100, there is a huge performance hit when the window is horizontally resized.
I create each comment's view:
var lastComment:NSView = documentView // The last view to vertically position around
for (index, comment) in commentsArray.enumerate() {
var data = comment
data["attributedString"] = attributedString
let commentView = NSCommentView(data: data)
documentView.addSubview(commentView)
let verticalSpacing = (index == 0) ? 0 : 10 // Position the first comment without any top spacing
let secondAttribute = (index == 0) ? NSLayoutAttribute.Top : NSLayoutAttribute.Bottom
let widthConstraint = NSLayoutConstraint(item: commentView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: documentView, attribute: NSLayoutAttribute.Width, multiplier: 1, constant: 0)
let topConstraint = NSLayoutConstraint(item: commentView, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: lastComment, attribute: secondAttribute, multiplier: 1, constant: verticalSpacing)
documentView.addConstraints([widthConstraint, topConstraint])
lastComment = commentView
if index == commentsArray.count - 1{
let bottom = NSLayoutConstraint(item: commentView, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: documentView, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 0)
documentView.addConstraint(bottom)
}
}
Where NSCommentView
is defined as:
class NSCommentView: NSView {
func setupFrame(){
self.wantsLayer = true
self.translatesAutoresizingMaskIntoConstraints = false
self.layer?.backgroundColor = NSColor.redColor().CGColor
// Set height of comment, fixed for testing
let heightConstraint = NSLayoutConstraint(item: self, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 50)
self.addConstraint(heightConstraint)
}
func setupTextView(attributedString: NSAttributedString){
let textView = NSTextView()
textView.translatesAutoresizingMaskIntoConstraints = false
textView.verticallyResizable = true
textView.textStorage?.setAttributedString(attributedString)
self.addSubview(textView)
let heightConstraint = NSLayoutConstraint(item: textView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 50)
textView.addConstraint(heightConstraint)
// The performance hit occurs when I set a width equal to the parent width:
let widthConstraint = NSLayoutConstraint(item: textView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Width, multiplier: 1, constant: 0)
self.addConstraint(widthConstraint)
}
convenience init(data:[String: AnyObject?]){
self.init()
setupFrame()
if data["isComment"] as! Bool {
let attributedString = data["attributedString"] as! NSAttributedString
setupTextView(attributedString)
}
}
}
As you can see from the code, the issue is when I attempt to set the width of the NSTextView
to the width of the NSCommentView
. If I simply set it as a constant, there is no real performance degradation.
Is my view hierarchy not setup correctly, or am I overlooking something which is causing this performance issue?
Upvotes: 0
Views: 183
Reputation: 1127
I agree with Ken that you're re-inventing the table view (and all it's associated optimizations). Is there an overriding reason to not use table view or collection view?
The performance hit is because when resizing horizontally you're re-calculating the size of every single subview! Not just the ones that are visible.
If you must do it this way. Some optimization attempts would be to:
I also noticed that you're fixing the height of the comment views right now. Once you enable that to flex, the performance will suffer even more since the views will have to resize across two dimensions, in realtime, impacting all the views below them on the page.
TL;DR - Save yourself a lot of trouble. Use a table or collection view.
Upvotes: 2