Skip to content

Instantly share code, notes, and snippets.

@audrl1010
Last active May 15, 2024 07:09
Show Gist options
  • Select an option

  • Save audrl1010/dd41f7d5910f3e4ead747ea73c5f6a6e to your computer and use it in GitHub Desktop.

Select an option

Save audrl1010/dd41f7d5910f3e4ead747ea73c5f6a6e to your computer and use it in GitHub Desktop.

Revisions

  1. audrl1010 revised this gist Oct 15, 2022. 1 changed file with 11 additions and 11 deletions.
    22 changes: 11 additions & 11 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -1,23 +1,23 @@
    # LPLazyDisplayStackScrollView
    # LazyDisplayStackScrollView
    ## What?
    - ScrollView에 StackView를 감싸서, 많은 양의 subviews들을 처음에 추가할 때 rendering 과부하가 걸리게 됩니다. scrolling시 화면에 보이는 영역의 subviews만 render할 수 있다면 굉장히 퍼포먼스가 좋을 것입니다. SwiftUI에서는 이러한 문제점을 인식했는 지 LazyStack이라는 것을 제공합니다. 하지만 UIKit에서는 그러한 것을 제공하지 않습니다. 이 라이브러리는 해당 이슈를 해결하기 위해 만들어졌습니다.

    ## Description
    - on-demand subviews render를 지원합니다.
    - Note) subview(LPLazyDisplayView)를 추가할 때 estimatedHeight(추측 높이)값을 대략적으로 정확히 설정해주지 않으면,
    LPLazyDisplayStackScrollView가 좋은 성능을 내기 힘듭니다.
    - Note) subview(LazyDisplayView)를 추가할 때 estimatedHeight(추측 높이)값을 대략적으로 정확히 설정해주지 않으면,
    LazyDisplayStackScrollView가 좋은 성능을 내기 힘듭니다.


    ## Implement

    ```swift

    class LPLazyDisplayStackScrollView: UIView {
    class LazyDisplayStackScrollView: UIView {

    // 위 아래, 허용
    var distance: CGFloat = 100

    private(set) var lazyDisplayViews: [LPLazyDisplayView] = []
    private(set) var lazyDisplayViews: [LazyDisplayView] = []

    private lazy var scrollView = UIScrollView()

    @@ -68,21 +68,21 @@ class LPLazyDisplayStackScrollView: UIView {
    }
    }

    func addArrangedSubview(_ view: LPLazyDisplayView) {
    func addArrangedSubview(_ view: LazyDisplayView) {
    self.lazyDisplayViews.append(view)
    self.contentStack.addArrangedSubview(view)
    self.contentStack.setNeedsLayout()
    self.contentStack.layoutIfNeeded()
    }

    func insertArrangedSubview(_ view: LPLazyDisplayView, at index: Int) {
    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: LPLazyDisplayView) {
    func removeArrangedSubview(_ view: LazyDisplayView) {
    let index = self.lazyDisplayViews.firstIndex { $0 === view }
    if let index = index {
    self.lazyDisplayViews.remove(at: index)
    @@ -125,7 +125,7 @@ class LPLazyDisplayStackScrollView: UIView {
    }
    }

    class LPLazyDisplayView: UIView {
    class LazyDisplayView: UIView {

    fileprivate(set) var isPending: Bool = true

    @@ -165,7 +165,7 @@ class LPLazyDisplayView: UIView {
    ```swift
    class ViewController: UIViewController {

    private let contentView = LPLazyDisplayStackScrollView()
    private let contentView = LazyDisplayStackScrollView()

    override func viewDidLoad() {
    super.viewDidLoad()
    @@ -219,7 +219,7 @@ import UIKit
    import SnapKit
    import Then

    class ItemView: LPLazyDisplayView {
    class ItemView: LazyDisplayView {

    let label = UILabel()

  2. audrl1010 revised this gist Oct 15, 2022. No changes.
  3. audrl1010 revised this gist Oct 15, 2022. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -7,6 +7,7 @@
    - Note) subview(LPLazyDisplayView)를 추가할 때 estimatedHeight(추측 높이)값을 대략적으로 정확히 설정해주지 않으면,
    LPLazyDisplayStackScrollView가 좋은 성능을 내기 힘듭니다.


    ## Implement

    ```swift
  4. audrl1010 revised this gist Oct 15, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    # LPLazyDisplayStackScrollView
    ## What?
    - ScrollView에 StackView를 감싸서 사용할 때, 많은 양의 subviews들을 처음에 추가할 때 rendering 과부하가 걸리게 됩니다. scrolling시 화면에 보이는 영역의 subviews만 render할 수 있다면 굉장히 퍼포먼스가 좋을 것입니다. SwiftUI에서는 이러한 문제점을 인식했는 지 LazyStack이라는 것을 제공합니다. 하지만 UIKit에서는 그러한 것을 제공하지 않습니다. 이 라이브러리는 해당 이슈를 해결하기 위해 만들어졌습니다.
    - ScrollView에 StackView를 감싸서, 많은 양의 subviews들을 처음에 추가할 때 rendering 과부하가 걸리게 됩니다. scrolling시 화면에 보이는 영역의 subviews만 render할 수 있다면 굉장히 퍼포먼스가 좋을 것입니다. SwiftUI에서는 이러한 문제점을 인식했는 지 LazyStack이라는 것을 제공합니다. 하지만 UIKit에서는 그러한 것을 제공하지 않습니다. 이 라이브러리는 해당 이슈를 해결하기 위해 만들어졌습니다.

    ## Description
    - on-demand subviews render를 지원합니다.
  5. audrl1010 revised this gist Oct 15, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@
    ## What?
    - ScrollView에 StackView를 감싸서 사용할 때, 많은 양의 subviews들을 처음에 추가할 때 rendering 과부하가 걸리게 됩니다. scrolling시 화면에 보이는 영역의 subviews만 render할 수 있다면 굉장히 퍼포먼스가 좋을 것입니다. SwiftUI에서는 이러한 문제점을 인식했는 지 LazyStack이라는 것을 제공합니다. 하지만 UIKit에서는 그러한 것을 제공하지 않습니다. 이 라이브러리는 해당 이슈를 해결하기 위해 만들어졌습니다.

    ## Define
    ## Description
    - on-demand subviews render를 지원합니다.
    - Note) subview(LPLazyDisplayView)를 추가할 때 estimatedHeight(추측 높이)값을 대략적으로 정확히 설정해주지 않으면,
    LPLazyDisplayStackScrollView가 좋은 성능을 내기 힘듭니다.
  6. audrl1010 revised this gist Oct 15, 2022. 1 changed file with 1 addition and 2 deletions.
    3 changes: 1 addition & 2 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -160,9 +160,8 @@ class LPLazyDisplayView: UIView {
    }
    ```


    ## Example
    ```swift
    // Example)
    class ViewController: UIViewController {

    private let contentView = LPLazyDisplayStackScrollView()
  7. audrl1010 revised this gist Oct 15, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    # LPLazyDisplayStackScrollView
    ## What?
    - ScrollView에 StackView를 감싸서 사용할 때, 많은 양의 subviews들을 처음에 추가할 때 rendering 과부하가 걸리게 됩니다. scrolling시 화면에 보이는 영역의 subviews만 render할 수 있다면 굉장히 퍼포먼스가 좋을 것입니다. 이 라이브러리는 해당 이슈를 해결하기 위해 만들어졌습니다.
    - ScrollView에 StackView를 감싸서 사용할 때, 많은 양의 subviews들을 처음에 추가할 때 rendering 과부하가 걸리게 됩니다. scrolling시 화면에 보이는 영역의 subviews만 render할 수 있다면 굉장히 퍼포먼스가 좋을 것입니다. SwiftUI에서는 이러한 문제점을 인식했는 지 LazyStack이라는 것을 제공합니다. 하지만 UIKit에서는 그러한 것을 제공하지 않습니다. 이 라이브러리는 해당 이슈를 해결하기 위해 만들어졌습니다.

    ## Define
    - on-demand subviews render를 지원합니다.
  8. audrl1010 revised this gist Oct 15, 2022. 1 changed file with 5 additions and 1 deletion.
    6 changes: 5 additions & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,9 @@
    # LPLazyDisplayStackScrollView
    ## What?
    - ScrollView에 StackView를 감싸서 사용할 때, 많은 양의 subviews들을 처음에 추가할 때 rendering 과부하가 걸리게 됩니다. scrolling시 화면에 보이는 영역의 subviews만 render할 수 있다면 굉장히 퍼포먼스가 좋을 것입니다. 이 라이브러리는 해당 이슈를 해결하기 위해 만들어졌습니다.

    ## Define
    - LPLazyDisplayStackScrollView는 on-demand subviews render를 지원합니다.
    - on-demand subviews render를 지원합니다.
    - Note) subview(LPLazyDisplayView)를 추가할 때 estimatedHeight(추측 높이)값을 대략적으로 정확히 설정해주지 않으면,
    LPLazyDisplayStackScrollView가 좋은 성능을 내기 힘듭니다.

  9. audrl1010 revised this gist Oct 15, 2022. 1 changed file with 120 additions and 120 deletions.
    240 changes: 120 additions & 120 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -1,126 +1,8 @@
    ## Define
    LPLazyDisplayStackScrollView는 on-demand subviews render를 지원합니다.
    Note) addArrangedSubview 하려는 LPLazyDisplayView의 estimatedHeight(추측 높이)값을 대략적으로 정확히 설정해주지 않으면,
    - LPLazyDisplayStackScrollView는 on-demand subviews render를 지원합니다.
    - Note) subview(LPLazyDisplayView)를 추가할 때 estimatedHeight(추측 높이)값을 대략적으로 정확히 설정해주지 않으면,
    LPLazyDisplayStackScrollView가 좋은 성능을 내기 힘듭니다.


    ```swift
    // Example)
    class ViewController: UIViewController {

    private let contentView = LPLazyDisplayStackScrollView()

    override func viewDidLoad() {
    super.viewDidLoad()

    self.view.backgroundColor = .white

    self.view.addSubview(self.contentView)

    self.contentView.snp.makeConstraints {
    $0.top.equalTo(self.view.safeAreaLayoutGuide)
    $0.left.right.bottom.equalToSuperview()
    }

    let item0 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "0"
    $0.tag = 0
    }
    let item1 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "1"
    $0.tag = 1
    }
    let item2 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "2"
    $0.tag = 3
    }
    let item5 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "3"
    $0.tag = 5
    }
    self.contentView.addArrangedSubview(item0)
    self.contentView.addArrangedSubview(item1)
    self.contentView.addArrangedSubview(item2)
    self.contentView.addArrangedSubview(item5)

    DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [unowned self] in
    self.contentView.insertArrangedSubview(ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "11"
    $0.tag = 11
    }, at: 1)
    }

    }
    }

    import UIKit
    import SnapKit
    import Then

    class ItemView: LPLazyDisplayView {

    let label = UILabel()

    let stack = UIStackView().then {
    $0.axis = .vertical
    $0.alignment = .fill
    $0.distribution = .fill
    }

    let button = UIButton().then {
    $0.setTitle("Toggle", for: .normal)
    }

    override init(frame: CGRect) {
    super.init(frame: frame)
    self.setup()
    }

    required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
    }

    private func setup() {
    self.setEstimatedHeight(500)
    }

    override func display() {
    super.display()

    self.layer.borderColor = UIColor.red.cgColor
    self.layer.borderWidth = 1.0

    print("display ", self.tag)
    self.button.setTitle("\(self.tag)", for: .normal)
    self.addSubview(self.button)
    self.button.addTarget(self, action: #selector(didTap), for: .touchUpInside)
    self.button.snp.makeConstraints {
    $0.height.equalTo(500)
    $0.edges.equalToSuperview()
    }
    }

    var isToggle: Bool = false

    @objc
    func didTap() {
    let generator = UIImpactFeedbackGenerator(style: .light)
    generator.impactOccurred()

    self.isToggle = !self.isToggle

    self.button.snp.updateConstraints {
    $0.height.equalTo(self.isToggle ? 1000 : 250)
    }
    }
    }
    ```

    ## Implement

    ```swift
    @@ -273,3 +155,121 @@ class LPLazyDisplayView: UIView {
    }
    }
    ```


    ```swift
    // Example)
    class ViewController: UIViewController {

    private let contentView = LPLazyDisplayStackScrollView()

    override func viewDidLoad() {
    super.viewDidLoad()

    self.view.backgroundColor = .white

    self.view.addSubview(self.contentView)

    self.contentView.snp.makeConstraints {
    $0.top.equalTo(self.view.safeAreaLayoutGuide)
    $0.left.right.bottom.equalToSuperview()
    }

    let item0 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "0"
    $0.tag = 0
    }
    let item1 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "1"
    $0.tag = 1
    }
    let item2 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "2"
    $0.tag = 3
    }
    let item5 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "3"
    $0.tag = 5
    }
    self.contentView.addArrangedSubview(item0)
    self.contentView.addArrangedSubview(item1)
    self.contentView.addArrangedSubview(item2)
    self.contentView.addArrangedSubview(item5)

    DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [unowned self] in
    self.contentView.insertArrangedSubview(ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "11"
    $0.tag = 11
    }, at: 1)
    }

    }
    }

    import UIKit
    import SnapKit
    import Then

    class ItemView: LPLazyDisplayView {

    let label = UILabel()

    let stack = UIStackView().then {
    $0.axis = .vertical
    $0.alignment = .fill
    $0.distribution = .fill
    }

    let button = UIButton().then {
    $0.setTitle("Toggle", for: .normal)
    }

    override init(frame: CGRect) {
    super.init(frame: frame)
    self.setup()
    }

    required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
    }

    private func setup() {
    self.setEstimatedHeight(500)
    }

    override func display() {
    super.display()

    self.layer.borderColor = UIColor.red.cgColor
    self.layer.borderWidth = 1.0

    print("display ", self.tag)
    self.button.setTitle("\(self.tag)", for: .normal)
    self.addSubview(self.button)
    self.button.addTarget(self, action: #selector(didTap), for: .touchUpInside)
    self.button.snp.makeConstraints {
    $0.height.equalTo(500)
    $0.edges.equalToSuperview()
    }
    }

    var isToggle: Bool = false

    @objc
    func didTap() {
    let generator = UIImpactFeedbackGenerator(style: .light)
    generator.impactOccurred()

    self.isToggle = !self.isToggle

    self.button.snp.updateConstraints {
    $0.height.equalTo(self.isToggle ? 1000 : 250)
    }
    }
    }
    ```
  10. audrl1010 revised this gist Oct 15, 2022. 1 changed file with 62 additions and 0 deletions.
    62 changes: 62 additions & 0 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -57,6 +57,68 @@ class ViewController: UIViewController {

    }
    }

    import UIKit
    import SnapKit
    import Then

    class ItemView: LPLazyDisplayView {

    let label = UILabel()

    let stack = UIStackView().then {
    $0.axis = .vertical
    $0.alignment = .fill
    $0.distribution = .fill
    }

    let button = UIButton().then {
    $0.setTitle("Toggle", for: .normal)
    }

    override init(frame: CGRect) {
    super.init(frame: frame)
    self.setup()
    }

    required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
    }

    private func setup() {
    self.setEstimatedHeight(500)
    }

    override func display() {
    super.display()

    self.layer.borderColor = UIColor.red.cgColor
    self.layer.borderWidth = 1.0

    print("display ", self.tag)
    self.button.setTitle("\(self.tag)", for: .normal)
    self.addSubview(self.button)
    self.button.addTarget(self, action: #selector(didTap), for: .touchUpInside)
    self.button.snp.makeConstraints {
    $0.height.equalTo(500)
    $0.edges.equalToSuperview()
    }
    }

    var isToggle: Bool = false

    @objc
    func didTap() {
    let generator = UIImpactFeedbackGenerator(style: .light)
    generator.impactOccurred()

    self.isToggle = !self.isToggle

    self.button.snp.updateConstraints {
    $0.height.equalTo(self.isToggle ? 1000 : 250)
    }
    }
    }
    ```

    ## Implement
  11. audrl1010 revised this gist Oct 15, 2022. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -59,6 +59,8 @@ class ViewController: UIViewController {
    }
    ```

    ## Implement

    ```swift

    class LPLazyDisplayStackScrollView: UIView {
  12. audrl1010 revised this gist Oct 15, 2022. 1 changed file with 5 additions and 3 deletions.
    8 changes: 5 additions & 3 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,8 @@
    ## Define
    LPLazyDisplayStackScrollView는 on-demand subviews render를 지원합니다.
    Note) addArrangedSubview 하려는 LPLazyDisplayView의 estimatedHeight(추측 높이)값을 대략적으로 정확히 설정해주지 않으면,
    LPLazyDisplayStackScrollView가 좋은 성능을 내기 힘듭니다.


    ```swift
    // Example)
    @@ -55,9 +60,6 @@ class ViewController: UIViewController {
    ```

    ```swift
    // LPLazyDisplayStackScrollView는 on-demand subviews render를 지원합니다.
    // Note) addArrangedSubview 하려는 LPLazyDisplayView의 estimatedHeight(추측 높이)값을 대략적으로 정확히 설정해주지 않으면,
    // LPLazyDisplayStackScrollView가 좋은 성능을 내기 힘듭니다.

    class LPLazyDisplayStackScrollView: UIView {

  13. audrl1010 revised this gist Oct 15, 2022. 1 changed file with 57 additions and 57 deletions.
    114 changes: 57 additions & 57 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,59 @@

    ```swift
    // Example)
    class ViewController: UIViewController {

    private let contentView = LPLazyDisplayStackScrollView()

    override func viewDidLoad() {
    super.viewDidLoad()

    self.view.backgroundColor = .white

    self.view.addSubview(self.contentView)

    self.contentView.snp.makeConstraints {
    $0.top.equalTo(self.view.safeAreaLayoutGuide)
    $0.left.right.bottom.equalToSuperview()
    }

    let item0 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "0"
    $0.tag = 0
    }
    let item1 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "1"
    $0.tag = 1
    }
    let item2 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "2"
    $0.tag = 3
    }
    let item5 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "3"
    $0.tag = 5
    }
    self.contentView.addArrangedSubview(item0)
    self.contentView.addArrangedSubview(item1)
    self.contentView.addArrangedSubview(item2)
    self.contentView.addArrangedSubview(item5)

    DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [unowned self] in
    self.contentView.insertArrangedSubview(ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "11"
    $0.tag = 11
    }, at: 1)
    }

    }
    }
    ```

    ```swift
    // LPLazyDisplayStackScrollView는 on-demand subviews render를 지원합니다.
    // Note) addArrangedSubview 하려는 LPLazyDisplayView의 estimatedHeight(추측 높이)값을 대략적으로 정확히 설정해주지 않으면,
    @@ -115,9 +171,7 @@ class LPLazyDisplayStackScrollView: UIView {
    }
    }
    }
    ```

    ```swift
    class LPLazyDisplayView: UIView {

    fileprivate(set) var isPending: Bool = true
    @@ -152,58 +206,4 @@ class LPLazyDisplayView: UIView {
    }
    }
    }


    // Example)
    class ViewController: UIViewController {

    private let contentView = LPLazyDisplayStackScrollView()

    override func viewDidLoad() {
    super.viewDidLoad()

    self.view.backgroundColor = .white

    self.view.addSubview(self.contentView)

    self.contentView.snp.makeConstraints {
    $0.top.equalTo(self.view.safeAreaLayoutGuide)
    $0.left.right.bottom.equalToSuperview()
    }

    let item0 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "0"
    $0.tag = 0
    }
    let item1 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "1"
    $0.tag = 1
    }
    let item2 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "2"
    $0.tag = 3
    }
    let item5 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "3"
    $0.tag = 5
    }
    self.contentView.addArrangedSubview(item0)
    self.contentView.addArrangedSubview(item1)
    self.contentView.addArrangedSubview(item2)
    self.contentView.addArrangedSubview(item5)

    DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [unowned self] in
    self.contentView.insertArrangedSubview(ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "11"
    $0.tag = 11
    }, at: 1)
    }

    }
    }
    ```
    ```
  14. audrl1010 revised this gist Oct 15, 2022. No changes.
  15. audrl1010 revised this gist Oct 15, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,7 @@

    class LPLazyDisplayStackScrollView: UIView {

    // 허용 거리
    // 위 아래, 허용
    var distance: CGFloat = 100

    private(set) var lazyDisplayViews: [LPLazyDisplayView] = []
  16. audrl1010 revised this gist Oct 15, 2022. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -5,6 +5,7 @@

    class LPLazyDisplayStackScrollView: UIView {

    // 허용 거리
    var distance: CGFloat = 100

    private(set) var lazyDisplayViews: [LPLazyDisplayView] = []
  17. audrl1010 revised this gist Oct 15, 2022. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,8 @@
    ```swift
    // LPLazyDisplayStackScrollView는 on-demand subviews render를 지원합니다.
    // Note) addArrangedSubview 하려는 LPLazyDisplayView의 Height 값을 설정 해주지 않으면, LPLazyDisplayStackScrollView가 잘 작동 되지 않습니다.
    // Note) addArrangedSubview 하려는 LPLazyDisplayView의 estimatedHeight(추측 높이)값을 대략적으로 정확히 설정해주지 않으면,
    // LPLazyDisplayStackScrollView가 좋은 성능을 내기 힘듭니다.

    class LPLazyDisplayStackScrollView: UIView {

    var distance: CGFloat = 100
  18. audrl1010 renamed this gist Oct 15, 2022. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  19. audrl1010 renamed this gist Oct 15, 2022. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  20. audrl1010 revised this gist Oct 15, 2022. 1 changed file with 5 additions and 1 deletion.
    6 changes: 5 additions & 1 deletion gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,4 @@
    ```swift
    // LPLazyDisplayStackScrollView는 on-demand subviews render를 지원합니다.
    // Note) addArrangedSubview 하려는 LPLazyDisplayView의 Height 값을 설정 해주지 않으면, LPLazyDisplayStackScrollView가 잘 작동 되지 않습니다.
    class LPLazyDisplayStackScrollView: UIView {
    @@ -111,7 +112,9 @@ class LPLazyDisplayStackScrollView: UIView {
    }
    }
    }
    ```

    ```swift
    class LPLazyDisplayView: UIView {

    fileprivate(set) var isPending: Bool = true
    @@ -199,4 +202,5 @@ class ViewController: UIViewController {
    }

    }
    }
    }
    ```
  21. audrl1010 created this gist Oct 15, 2022.
    202 changes: 202 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,202 @@
    // LPLazyDisplayStackScrollView는 on-demand subviews render를 지원합니다.
    // Note) addArrangedSubview 하려는 LPLazyDisplayView의 Height 값을 설정 해주지 않으면, LPLazyDisplayStackScrollView가 잘 작동 되지 않습니다.
    class LPLazyDisplayStackScrollView: UIView {

    var distance: CGFloat = 100

    private(set) var lazyDisplayViews: [LPLazyDisplayView] = []

    private lazy var scrollView = UIScrollView()

    private let contentStack = UIStackView().then {
    $0.axis = .vertical
    $0.distribution = .fill
    $0.alignment = .fill
    }

    private var scrollViewBoundsToken: NSKeyValueObservation?

    private var scrollViewContentSizeToken: NSKeyValueObservation?

    deinit {
    self.scrollViewBoundsToken?.invalidate()
    self.scrollViewContentSizeToken?.invalidate()
    }

    override init(frame: CGRect) {
    super.init(frame: frame)
    self.setup()
    }

    required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
    }

    private func setup() {
    self.scrollViewBoundsToken = self.scrollView.observe(\UIScrollView.bounds, options: [.new]) { [weak self] scrollView, change in
    guard let self = self else { return }
    self.displayPendingViewsIfNeeded()
    }

    self.scrollViewContentSizeToken = self.scrollView.observe(\UIScrollView.contentSize, options: [.new]) { [weak self] scrollView, change in
    guard let self = self else { 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: LPLazyDisplayView) {
    self.lazyDisplayViews.append(view)
    self.contentStack.addArrangedSubview(view)
    self.contentStack.setNeedsLayout()
    self.contentStack.layoutIfNeeded()
    }

    func insertArrangedSubview(_ view: LPLazyDisplayView, at index: Int) {
    self.lazyDisplayViews.insert(view, at: index)
    self.contentStack.insertArrangedSubview(view, at: index)
    self.scrollView.setNeedsLayout()
    self.scrollView.layoutIfNeeded()
    }

    func removeArrangedSubview(_ view: LPLazyDisplayView) {
    let index = self.lazyDisplayViews.firstIndex { $0 === view }
    if let index = index {
    self.lazyDisplayViews.remove(at: index)
    self.contentStack.removeArrangedSubview(view)
    view.removeFromSuperview()
    self.contentStack.setNeedsLayout()
    self.contentStack.layoutIfNeeded()
    }
    }

    func removeAllArrangedSubviews() {
    self.lazyDisplayViews.removeAll()
    for subview in self.contentStack.arrangedSubviews {
    self.contentStack.removeArrangedSubview(subview)
    subview.removeFromSuperview()
    }
    self.contentStack.setNeedsLayout()
    self.contentStack.layoutIfNeeded()
    }

    private func displayPendingViewsIfNeeded() {
    let topPendingRect = CGRect(
    origin: .init(x: self.scrollView.contentOffset.x, y: self.scrollView.contentOffset.y - self.distance),
    size: self.scrollView.frame.size
    )

    let bottomPendingRect = CGRect(
    origin: .init(x: self.scrollView.contentOffset.x, y: self.scrollView.contentOffset.y + self.distance),
    size: self.scrollView.frame.size
    )

    for lazyDisplayView in self.lazyDisplayViews {
    if lazyDisplayView.isPending {
    if topPendingRect.intersects(lazyDisplayView.frame) || bottomPendingRect.intersects(lazyDisplayView.frame) {
    lazyDisplayView.isPending = false
    lazyDisplayView.display()
    }
    }
    }
    }
    }

    class LPLazyDisplayView: UIView {

    fileprivate(set) var isPending: Bool = true

    private(set) var estimatedHeight: CGFloat = 50

    override init(frame: CGRect) {
    super.init(frame: frame)
    self.setup()
    }

    required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
    }

    private func 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)
    }
    }
    }


    // Example)
    class ViewController: UIViewController {

    private let contentView = LPLazyDisplayStackScrollView()

    override func viewDidLoad() {
    super.viewDidLoad()

    self.view.backgroundColor = .white

    self.view.addSubview(self.contentView)

    self.contentView.snp.makeConstraints {
    $0.top.equalTo(self.view.safeAreaLayoutGuide)
    $0.left.right.bottom.equalToSuperview()
    }

    let item0 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "0"
    $0.tag = 0
    }
    let item1 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "1"
    $0.tag = 1
    }
    let item2 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "2"
    $0.tag = 3
    }
    let item5 = ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "3"
    $0.tag = 5
    }
    self.contentView.addArrangedSubview(item0)
    self.contentView.addArrangedSubview(item1)
    self.contentView.addArrangedSubview(item2)
    self.contentView.addArrangedSubview(item5)

    DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [unowned self] in
    self.contentView.insertArrangedSubview(ItemView().then {
    $0.backgroundColor = .gray
    $0.label.text = "11"
    $0.tag = 11
    }, at: 1)
    }

    }
    }