<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>소라미 개발로그</title>
        <link>https://velog.io/</link>
        <description>가벼운 글을 쓰는 부캐</description>
        <lastBuildDate>Thu, 02 Jan 2025 11:25:19 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>소라미 개발로그</title>
            <url>https://velog.velcdn.com/images/soeun_0823/profile/72aeb81b-3d89-445f-8f69-543ff8fc1cc5/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 소라미 개발로그. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/soeun_0823" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Swift) setNeedsLayout vs layoutIfNeeded]]></title>
            <link>https://velog.io/@soeun_0823/Swift-setNeedsLayout-vs-layoutIfNeeded</link>
            <guid>https://velog.io/@soeun_0823/Swift-setNeedsLayout-vs-layoutIfNeeded</guid>
            <pubDate>Thu, 02 Jan 2025 11:25:19 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요 소라미입니다 :D
사실 이 포스팅이 오늘 주제는 아니고.. 다른 포스팅을 작성하고 있었는데
갑자기 막혔는데 도저히 모르겠더라구요??
그래서 노선을 틀어버림 ㅋㅁㅋ</p>
<p>이번엔 UI 개발을 할 때 어쩌면 자주 사용하는 
**
setNeedsLayout
layoutIfNeeded**</p>
<p>이 두 가지 메서드에 대해서 알아보려고 합니당!
가볍게 개념을 맛만 볼 정도예요
왜냐면 깊게 들어가면 너무 깊어지므로.. 그건 나중에 본 블로그에서나 다루겠습니다!!</p>
<h2 id="ui의-life-cycle">UI의 Life Cycle</h2>
<p>우리가 개발을 하다보면요
어떤 애니메이션을 그리거나, 혹은 뷰의 크기를 변경하거나, constraints를 업데이트 하는 등 화면을 다시 그려야 할 때가 있죠??</p>
<p>이런 식으로 어떤 UI가 다시 그려져야 할 때,
iOS는 <strong>야!!! UI 바껴랏!!</strong> 한다고 즉시 다시 그리진 않아요!</p>
<p>무슨 말이냐면,
iOS는 앱이 실행되면_** Main Run Loop**_라는 것을 돌리는데,
(이 루프에 대해 잘 모르겠으면 <a href="https://babbab2.tistory.com/68">이 포스팅</a>을 보고 오시면 되구요 :0)</p>
<p>이 Main Run Loop라는 것은, 아래와 같이 업데이트 사이클이란 것을 돈답니당</p>
<p><img src="https://velog.velcdn.com/images/soeun_0823/post/e4f41f3e-f10c-49b0-aa66-a8f2c16d4b7b/image.png" alt=""></p>
<p>한 번의 사이클이 다 돌았을 때, 그제야 바뀌어야 되는 UI들을 한꺼번에 업데이트 한단 말이에요</p>
<p>좀 더 쉽게 얘기하자면,
내가 바로 <strong>어이!! 애니메이션 좀 그려봐!</strong> 라고 즉시 명령을 한들,
현재 돌고있는 Main Run Loop의 사이클이 다 돌 때까지, 업데이트가 되지 않는다는 말입니당</p>
<p>근데 보통.. 이 Main Run Loop의 사이클 주기가 긴 편ㅇㅣ 아니어라.. (매우 빠른 편 ...)
사람의 눈에는 그렇게 크게 차이를 느끼진 못하거덩여?
근데 만약 시시각각 바로바로 0.1초 단위로 프레임이 바뀌어야 하는 애니메이션 같은 경우엔 문제가 될 수도 있어요</p>
<pre><code class="language-swift">topAnchor = animationView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 200)</code></pre>
<p>자 이렇게 topAnchor가 safeArea부터 <strong>200만큼</strong> 떨어져있는 빨간색 공 뷰가 있다고 해요</p>
<p><img src="https://velog.velcdn.com/images/soeun_0823/post/fe4353f1-10d2-4c93-8b24-cf9caee17e33/image.png" alt=""></p>
<p>저희는 애니메이션 시작! 버튼을 눌렀을 때
3초 동안</p>
<p><img src="https://velog.velcdn.com/images/soeun_0823/post/134e1273-39b4-4c61-a111-1ed16cfa4030/image.png" alt=""></p>
<p>이렇게 safeArea부터 <strong>500만큼</strong> 떨어지는 애니메이션을 주고 싶어요
그러면 이렇게 코드를 짜겠죠??</p>
<pre><code class="language-swift">@objc func animation() {
    UIView.animate(withDuration: 3) { [weak self] in
        self?.topAnchor.constant = 500
    }
}</code></pre>
<p>근데 이렇게 짜면 애니메이션이 어떻게 되냐면여 </p>
<p><img src="https://velog.velcdn.com/images/soeun_0823/post/9cb7c9c9-3d4d-4c3c-b931-efbb73503891/image.gif" alt=""></p>
<p>네 이렇게 레전드 멍미가 됩ㄴㅣ다
이는 위에서 얘기 했듯이,
시시각각 View의 Frame을 업데이트 해야하는 애니메이션의 의도와는 달리,
Main Run Loop의 라이프 사이클이 한바퀴 돌때까지 기다렸다가
돌아왔을 때 애니메이션 블락안의 코드를 실행해버려서 그럽니다..ㅠㅠㅠㅠ
(제가 업데이트 하고 싶은 시점과, 실제 View의 Frame이 다시 그려지는 시점이 달라서 생기는 문제옝요)</p>
<p>따라서 먼 조치를 취해야 겟져?? 옙옙</p>
<h2 id="뷰를-다시-그리는-layoutsubviews">뷰를 다시 그리는 layoutSubviews()</h2>
<p>엥 setNeedsLayout vs layoutIfNeeded 차이라면서 갑자기 웬 layoutSubvies() 메서드인가 싶죠???</p>
<p>사실 위의 애니메이션처럼 뷰의 프레임이 바뀌는 것 처럼,
<strong>뷰의 위치나 사이즈를 재계산 해서 배치</strong>해야 할 경우,
UIView는 클래스 내부의 인스턴스 메서드인 <strong>layoutSubView라는</strong> 메서드를 반드시 호출해야 해여
그러면 이 메서드는 내 subView들의 layoutSubView라는를 재귀적으로 호출해서
내 subView들까지 줄줄이 재배치가 되는 거거든요
(따라서 뷰의 레이아웃과 관련된 로직을 viewDidAppear같은 곳이 아닌 viewDidLayoutSubVies()에서 하는 겁니당)</p>
<p>그럼 결국, 우리가 UI를 다시 그려야 하는 시점이 오면
이 layoutSubView라는 메서드를 호출시켜야 한다는 말이자나여???
근데 사실 layoutSubView 메서드는 <strong>직접 호출하는 것이 금지</strong>되어 있슴니당
<strong>시스템에 의해서 재배치 되어야 하는 경우, 자동으로 호출되기 때문이에요</strong></p>
<p>아ㅏ- 여기까지 왔으면 헷갈릴 수 있는데!
시원하게 정리한번 하구 가져</p>
<hr>
<p>UI의 레이아웃을 다시 그리는 메서드는 layoutIfNeeded 메서드예요
하지만 이 메서드는, 시스템적으로 호출이 되기 때문에 우리가 직접 호출할 수 없어요</p>
<p>시스템적으로 어떻게 호출이 되느냐면,
우리가 만약 A라는 뷰의 레이아웃을 바꿔주고 싶다면,
시스템은 현재 Main Run Loop의 사이클이 한 바퀴 다 돌때까지 기다리구여. 그리고 사이클이 다 돌았을 때 그때 layoutIfNeeded 메서드를 실행한답니당</p>
<p>저희가 위에서 UIView.animate 안에서 레이아웃이 바뀌어야 하는 코드를 호출했잖아ㅕ??
따라서 animate 메서드에서 (아마) Main Run Loop의ㅣ 한 사이클이 다 돌고 난 후에 이 layoutSubView가 호출되었을 거예요</p>
<p>이해가 갔죠??</p>
<p>그러면 만약 내가 View가 변해서 직접적으로 layoutIfNeeded를 호출하고 싶을 때가 있잖아요
그럴 때 사용하는ㄴ 메서드가 바로 </p>
<p>**
setNeedsLayout
layoutIfNeeded**</p>
<p>이 두가지 메서드입니다!</p>
<h2 id="setneedslayout-vs-layoutifneeded">setNeedsLayout vs layoutIfNeeded</h2>
<h3 id="setneedslayout">setNeedsLayout</h3>
<p><strong>난(개발자가) 지금 직접 레이아웃을 변경</strong>시켜야겠습니다!
근데 참을성이 조으니 <strong>기존 시스템 방식처럼, 다음 UI Update Cycle까지는 기다려주겠슴니다</strong>!
하고 호출하는 것이 setNeedsLayout임니다</p>
<p>따라서, </p>
<pre><code class="language-swift">@objc func animation() {
    UIView.animate(withDuration: 3) { [weak self] in
        self?.topAnchor.constant = 500
        self?.view.setNeedsLayout()
    }
}</code></pre>
<p>이렇게 작성해주먄</p>
<p><img src="https://velog.velcdn.com/images/soeun_0823/post/9cb7c9c9-3d4d-4c3c-b931-efbb73503891/image.gif" alt=""></p>
<p>여전히 공이 공뚝떨
그것은 시스템적으로 호출되던 방법과 같이 다음 Update Cycle까지 기다리기 때문인데요
따라서 다음 사이클이고 나발이고 바로 시시각각 업데이트가 필요한 경우에느</p>
<h3 id="layoutifneeded">layoutIfNeeded</h3>
<p>위 메서드를 사용해주시먄 됨니다
이 메서드를 사용할 경우,
<strong>다음 Update Cycle이 도는 것을 기다리지 않고 바로 layoutSubView 메서드를 호출</strong>해버려요</p>
<pre><code class="language-swift">@objc func animation() {
    UIView.animate(withDuration: 3 { [weak self] in
        self?.topAnchor.constant = 500
        self?.view.layoutIfNeeded()
    }
}</code></pre>
<p>그럼 </p>
<p><img src="https://velog.velcdn.com/images/soeun_0823/post/93863d71-a36b-4985-bae0-7f97aa2455bb/image.gif" alt=""></p>
<p>이렇게 시시각각 layoutSubView 메서드를 호출하기 때문에
우리가 원하는 애니메이션이 나오는 겁니다!!</p>
<h2 id="정리">정리</h2>
<hr>
<p>우리가 뷰의 레이아웃을 바꾸는 작업을 했을 때
layoutSubviews()를 호출해야 뷰가 재배치 된다
근데 이 메서드는 직접 호출할 수 없다
시스템적으로 사이클에 의해 호출이 된다</p>
<hr>
<p>따라서 시스템 사이클이고 나발이고 어떤 레이아웃을 즉시 업데이트 하고 싶을 때,
layoutSubviews()를 간접 호출할 수 있는 메서드가 있는데
그것이 </p>
<p><strong>setNeedsLayout
layoutIfNeeded</strong></p>
<p>이 두가지 메서드가</p>
<p>🟣<strong><em>setNeedsLayout</em></strong> 는
호출 즉시 <strong>다음 업데이트 사이클까지 기다렸다가 뷰를 갱신(layoutSubviews를 
호출)하는 것</strong>이고,</p>
<p>🟣<em><strong>layoutIfNeeded</strong></em> 는
호출 즉시 참을성이 부족해 <strong>업데이트 사이클이고 나발이고 바로 뷰를 갱신(layoutSubviews를 호출)</strong>해버린다</p>
<p>끗</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] 스크롤이 되는 StackView 만들기]]></title>
            <link>https://velog.io/@soeun_0823/Swift-%EC%8A%A4%ED%81%AC%EB%A1%A4%EC%9D%B4-%EB%90%98%EB%8A%94-StackView-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@soeun_0823/Swift-%EC%8A%A4%ED%81%AC%EB%A1%A4%EC%9D%B4-%EB%90%98%EB%8A%94-StackView-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Sun, 15 Dec 2024 09:46:19 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요! 
가벼운 주제의 글을 쓰는 소라미입니다🥹
오늘은 Swift에서 <strong>StackView를 사용할 때 스크롤이 가능하게 하는 방법</strong>에 대해 알아볼게요 :)</p>
<p>자, 다음과 같은 스택뷰가 하나 있다구 합시다</p>
<pre><code class="language-swift">class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let stackView = UIStackView()
        stackView.axis = .vertical
        stackView.backgroundColor = .yellow

        stackView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(stackView)

        NSLayoutConstraint.activate([
            stackView.topAnchor.constraint(equalTo: view.topAnchor),
            stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            stackView.heightAnchor.constraint(equalToConstant: 100)
        ])
    }
}</code></pre>
<p>이 StackView에</p>
<p>11111111
22222222
33333333
44444444
55555555</p>
<p>라는 텍스트를 가진 UILabel 5개를 addArrangedSubview를 통해 넣으면
어떻게 되냐면용</p>
<p><img src="https://velog.velcdn.com/images/soeun_0823/post/8a10f2b6-693e-49f4-a002-4508a9832511/image.mov" alt=""></p>
<p>StackView는 기본적으로 스크롤이 되지 않기 때문에,
컨텐츠가 다 표시되지 못하고 짤리게 됩니다ㅠㅁㅠ</p>
<p>따라서 이때 스택뷰를 스크롤 되게 하려면,
ScrollView로 스택뷰를 한번 감싸야하는데요</p>
<p>그 방법을 알아보겠습니다</p>
<h3 id="scroll되는-stackview-만들기vertical">Scroll되는 StackView 만들기(Vertical)</h3>
<pre><code class="language-swift">class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let scrollView = UIScrollView()
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(scrollView)

        NSLayoutConstraint.activate([
            scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            scrollView.heightAnchor.constraint(equalToConstant: 50)
        ])
    }</code></pre>
<p>StackView를 만들기 전에, 먼저 ScrollView를 만들어주고
기존 <strong>StackView의 AutoLayout을 이번엔 ScrollView로 잡아</strong>주셔야 합니다!</p>
<p>그리고 다시 StackView를 만드는데,
이번엔 ScrollView로 감싸줘야 하기 때문에 addSubView를 ScrollView에 해줍니다</p>
<pre><code class="language-swift">        let stackView = UIStackView()
        stackView.axis = .vertical
        stackView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(stackView) ✅</code></pre>
<p>그리고 이제 가장 중요한, StackView의 AutoLayout 설정 인데요</p>
<pre><code class="language-swift">        let stackView = UIStackView()
        stackView.axis = .vertical
        stackView.backgroundColor = .yellow
        stackView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(stackView) 

        NSLayoutConstraint.activate([
            stackView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            stackView.leftAnchor.constraint(equalTo: scrollView.leftAnchor),
            stackView.rightAnchor.constraint(equalTo: scrollView.rightAnchor),
            stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor)  🔥
        ])</code></pre>
<p>ScrollView의 내부 사이즈에 맞게 감싸는 것은 당연하지만,
여기서 유의해야 할 것은 <strong>StackView의 widthAnchor를 scrollView의 widthAnchor로 맞춰주셔야 합니다</strong>!!! 
heightAnchor는 맞출 필요 없어요</p>
<p>이렇게 하면</p>
<p><img src="https://velog.velcdn.com/images/soeun_0823/post/31fb8675-902a-48d3-8bdc-67f28ba163ce/image.mov" alt=""></p>
<p>짜잔! 스크롤이 가능한 스택뷰가 됩니다
물론, 스택뷰 크기가 스크롤뷰 크기보다 작을 경우엔 스크롤이 되지 않습니다😇</p>
<h3 id="scroll되는-stackview-만들기horizontal">Scroll되는 StackView 만들기(Horizontal)</h3>
<p>horizontal 스택뷰의 경우엔, 설정이 한 가지 달라지는데요</p>
<pre><code>       let scrollView = UIScrollView()
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(scrollView)

        NSLayoutConstraint.activate([
            scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            scrollView.heightAnchor.constraint(equalToConstant: 50)
        ])

        let stackView = UIStackView()
        stackView.axis = .horizontal ✅
        stackView.backgroundColor = .yellow
        stackView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(stackView)

        NSLayoutConstraint.activate([
            stackView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            stackView.leftAnchor.constraint(equalTo: scrollView.leftAnchor),
            stackView.rightAnchor.constraint(equalTo: scrollView.rightAnchor),
            stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            stackView.heightAnchor.constraint(equalTo: scrollView.heightAnchor) 🔥
        ])</code></pre><p>아까 Vertical StackView에선 
StackView의 widthAnchor를 scrollView의 widthAnchor로 맞춰주셨잖아요?
Horizontal StackView일 경우엔 <strong>StackView의 heightAnchor를 scrollView의 heightAnchor 맞춰주셔야 합니다</strong></p>
<p><img src="https://velog.velcdn.com/images/soeun_0823/post/bda76ed9-edc2-41b6-a85c-fef2ca675af5/image.mov" alt=""></p>
<p>이렇게 하면,
스크롤이 되는 Horizontal StackView도 완성!! </p>
<h3 id="전체-코드">전체 코드</h3>
<pre><code class="language-swift">import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let scrollView = UIScrollView()
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(scrollView)

        NSLayoutConstraint.activate([
            scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            scrollView.heightAnchor.constraint(equalToConstant: 50)
        ])

        let stackView = UIStackView()
        stackView.axis = .vertical
        stackView.backgroundColor = .yellow
        stackView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(stackView)

        NSLayoutConstraint.activate([
            stackView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            stackView.leftAnchor.constraint(equalTo: scrollView.leftAnchor),
            stackView.rightAnchor.constraint(equalTo: scrollView.rightAnchor),
            stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
        ])


        let lable1 = UILabel()
        lable1.text = &quot;11111111&quot;
        stackView.addArrangedSubview(lable1)

        let lable2 = UILabel()
        lable2.text = &quot;22222222&quot;
        stackView.addArrangedSubview(lable2)

        let lable3 = UILabel()
        lable3.text = &quot;33333333&quot;
        stackView.addArrangedSubview(lable3)

        let lable4 = UILabel()
        lable4.text = &quot;44444444&quot;
        stackView.addArrangedSubview(lable4)

        let lable5 = UILabel()
        lable5.text = &quot;55555555&quot;
        stackView.addArrangedSubview(lable5)
    }
}

</code></pre>
<h3 id="마무리">마무리</h3>
<p>뭐 사실 CollectionView 쓰면 되지 않음? 깔깔 할 수 있지만
가끔은 스택뷰가 더 편할 때도 있거덩여
CollectionView는 사이클 관리도 해조야 하구 셀두 만드렁야 하구 쪼매 귀찬ㄴㅎ
다음 포스팅에서는 <strong>스크롤이 되는 StackView의 가운데 정렬 하는 방법</strong>을 알아보겠읍니다</p>
<p>그럼 굿밤</p>
]]></description>
        </item>
    </channel>
</rss>