You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ScrollView에 StackView를 감싸서, 많은 양의 subviews들을 처음에 추가할 때 rendering 과부하가 걸리게 됩니다. scrolling시 화면에 보이는 영역의 subviews만 render할 수 있다면 굉장히 퍼포먼스가 좋을 것입니다. SwiftUI에서는 이러한 문제점을 인식했는 지 LazyStack이라는 것을 제공합니다. 하지만 UIKit에서는 그러한 것을 제공하지 않습니다. 이 라이브러리는 해당 이슈를 해결하기 위해 만들어졌습니다.
Description
on-demand subviews render를 지원합니다.
Note) subview(LazyDisplayView)를 추가할 때 estimatedHeight(추측 높이)값을 대략적으로 정확히 설정해주지 않으면,
LazyDisplayStackScrollView가 좋은 성능을 내기 힘듭니다.
Implement
classLazyDisplayStackScrollView:UIView{
// 위 아래, 허용
vardistance:CGFloat=100private(set)varlazyDisplayViews:[LazyDisplayView]=[]private lazy varscrollView=UIScrollView()privateletcontentStack=UIStackView().then{
$0.axis =.vertical
$0.distribution =.fill
$0.alignment =.fill
}privatevarscrollViewBoundsToken:NSKeyValueObservation?privatevarscrollViewContentSizeToken:NSKeyValueObservation?deinit{self.scrollViewBoundsToken?.invalidate()self.scrollViewContentSizeToken?.invalidate()}overrideinit(frame:CGRect){
super.init(frame: frame)self.setup()}requiredinit?(coder:NSCoder){fatalError("init(coder:) has not been implemented")}privatefunc setup(){self.scrollViewBoundsToken =self.scrollView.observe(\UIScrollView.bounds, options:[.new]){[weak self] scrollView, change inguardlet self =selfelse{return}self.displayPendingViewsIfNeeded()}self.scrollViewContentSizeToken =self.scrollView.observe(\UIScrollView.contentSize, options:[.new]){[weak self] scrollView, change inguardlet self =selfelse{return}self.displayPendingViewsIfNeeded()}self.addSubview(self.scrollView)self.scrollView.snp.makeConstraints{
$0.edges.equalToSuperview()}self.scrollView.addSubview(self.contentStack)self.contentStack.snp.makeConstraints{
$0.edges.equalToSuperview()
$0.width.equalToSuperview()}}func addArrangedSubview(_ view:LazyDisplayView){self.lazyDisplayViews.append(view)self.contentStack.addArrangedSubview(view)self.contentStack.setNeedsLayout()self.contentStack.layoutIfNeeded()}func insertArrangedSubview(_ view:LazyDisplayView, at index:Int){self.lazyDisplayViews.insert(view, at: index)self.contentStack.insertArrangedSubview(view, at: index)self.scrollView.setNeedsLayout()self.scrollView.layoutIfNeeded()}func removeArrangedSubview(_ view:LazyDisplayView){letindex=self.lazyDisplayViews.firstIndex{ $0 === view }iflet index = index {self.lazyDisplayViews.remove(at: index)self.contentStack.removeArrangedSubview(view)
view.removeFromSuperview()self.contentStack.setNeedsLayout()self.contentStack.layoutIfNeeded()}}func removeAllArrangedSubviews(){self.lazyDisplayViews.removeAll()forsubviewinself.contentStack.arrangedSubviews {self.contentStack.removeArrangedSubview(subview)
subview.removeFromSuperview()}self.contentStack.setNeedsLayout()self.contentStack.layoutIfNeeded()}privatefunc displayPendingViewsIfNeeded(){lettopPendingRect=CGRect(
origin:.init(x:self.scrollView.contentOffset.x, y:self.scrollView.contentOffset.y -self.distance),
size:self.scrollView.frame.size
)letbottomPendingRect=CGRect(
origin:.init(x:self.scrollView.contentOffset.x, y:self.scrollView.contentOffset.y +self.distance),
size:self.scrollView.frame.size
)forlazyDisplayViewinself.lazyDisplayViews {if lazyDisplayView.isPending {if topPendingRect.intersects(lazyDisplayView.frame) || bottomPendingRect.intersects(lazyDisplayView.frame){
lazyDisplayView.isPending =false
lazyDisplayView.display()}}}}}classLazyDisplayView:UIView{fileprivate(set)varisPending:Bool=trueprivate(set)varestimatedHeight:CGFloat=50overrideinit(frame:CGRect){
super.init(frame: frame)self.setup()}requiredinit?(coder:NSCoder){fatalError("init(coder:) has not been implemented")}privatefunc setup(){self.snp.makeConstraints{
$0.height.equalTo(self.estimatedHeight)}}
// Override 해서 사용하세요.
func display(){
// 기본 estimated height 설정 삭제
self.snp.remakeConstraints{ _ in}}func setEstimatedHeight(_ height:CGFloat){self.estimatedHeight = height
self.snp.updateConstraints{
$0.height.equalTo(height)}}}