◎위챗 : speedseoul
https://zeddios.tistory.com/359
코딩하거나 예제 코드를 보면 보이는
setNeedsDisplay()
이게 정확히 뭘 해주는 코드일까 검색하다가 또 여러가지 메소드들을 알게 되었지만 더 헷갈리게 되었습니다 ㅎ;
setNeedsDisplay()말고도
setNeedsLayout()
displayIfNeeded()
layoutIfNeeded()
....
뭔가 정말 뭔가 비슷하지만 다르죠 근데 이게 뭔지도 모르겠
그래서 오늘은 위 4가지 메소드들을 알아보겠습니댜
시작!!
하나씩 알아볼거지만 일단 두개를 같이 봅시다. 우리가 보기에 비슷한거끼리요.
setNeedsDisplay()
setNeedsLayout()
이 두개. setNeeds로 시작하니 뭔가 둘이 연관성이 있을 것 같아요.
하나는 Display고, 하나는 Layout이네요.
일단 두 메소드는 UIView의 하위 메소드입니다.
그러니 뭔가 UIView와 관련이 있겠죠?
일단 이 두 메소드가 UIView와 관련있다는 것을 알았으니!!!
이제 하나씩 보도록 합시다.
● setNeedsDisplay
역시 모를때는 Apple문서를 보는 것이 확실하죠.
UIView문서를 보면, 이렇게 나와있습니다.
간단하게 해석해보면,
View의 실제 컨텐츠가 변경될 때, View를 다시 그려야함을 시스템에 알리는 것은 당신(개발자)의 책임입니다.
View의 setNeedsDisplay () 또는 setNeedsDisplay (_ :) 메소드를 호출하여 이 작업을 수행할 수 있습니다.
이 메소드는 다음 드로잉 사이클(next drawing cycle)동안 View를 업데이트해야 함을 시스템에 알립니다. View를 업데이트하기 위해 다음 드로잉 사이클때까지 기다리기 때문에, 여러 View에서 이 메소드를 호출하여 동시에 업데이트 할 수 있습니다.
라고 하네요!
그러니까 setNeedsDisplay()메소드는 View의 컨텐츠가 변하면 이 View가 변했다는 사실을 시스템에 알려주기 위해서 사용하는거네요.
그리고 이 setNeedsDisplay()메소드는 시스템에게
"야!1!!! 다음 드로잉 사이클때 View업데이트해;;"라고 한다고 하네요.
그럼 여기서 든 궁금증.
드로잉 사이클(drawing cycle)이 뭔데 ㅎ;
흐음 알아봅시다. 먼저, View의 생명주기 (View Life cycle)가 있었죠? View가 나타났다 사라지는 그 사이클...
그럼 Drawing Cycle은? 뭔가 그리는 주기?그런거 같네요. 때려맞추는건 여기까지 하고 Apple의 설명을 봅시다.
View Programming Guide for iOS와 Drawing and Printing Guide for iOS에 Drawing Cycle에 대한 설명이 나옵니다.
Drawing Cycle이 뭔지 알아봅시다 :) 후하후하
UIView클래스는 컨텐츠를 표시할 때, on-demand 드로잉 모델을 사용합니다.
View가 처음 화면에 나타나면, 시스템은 컨텐츠를 그려달라고 요청합니다.
시스템은 이 컨텐츠의 스냅샷을 캡쳐하고, 해당 스냅샷을 View의 시각적 표현(visual representation)으로 사용합니다.
View의 내용을 변경하지 않으면 View의 드로잉 코드를 다시 호출 할 수 없습니다. 스냅샷 이미지는 View와 관련된 대부분의 작업에 다시 사용됩니다.
컨텐츠를 변경하면 View가 변경되었음을 시스템에 알립니다. 그런 다음 View는 View를 그리고(drawing) 새 결과의 스냅샷을 캡쳐하는 프로세스를 반복합니다.
View의 내용이 변경되면 해당 변경사항을 직접 다시 그리지 않습니다. 대신 setNeedsDisplay메소드를 사용하여 View를 무효화합니다.
이 메소드는 View의 내용이 변경되어 다음 기회에 다시 그려질 필요가 있음을 시스템에 알립니다. 시스템은 드로잉 작업을 시작하기전에 현재 실행루프가 끝날때까지 대기합니다.
이 지연은 여러 View를 무효화하고, 계층구조에서 View를 추가 또는 제거하고, View를 숨기고, View의 크기를 조정하고, View의 위치를 한번에 재조정 할 수 있는 기회를 제공합니다.
변경사항은 모두 동시에 반영됩니다.
이까지 읽고
ㅇ
ㅡ
ㅁ........드로잉 사이클이 이런거군요.
조금 더 첨가하자면, View를 처음 표시하거나 View의 일부를 다시 그려야하는 경우, iOS는 View의 drawRect:메소드를 호출해서 View에 컨텐츠를 그려달라고 요청합니다.
View업데이트를 트리거 할 수 있는 몇가지 작업이 있는데요.
1. View를 부분적으로 가리고 있던 다른 View이동 또는 제거
2. hidden 프로퍼티를 No로 설정하여, 이전에 숨겨진 View를 다시 볼 수 있게 만들기
3. View를 화면밖으로 스크롤한다음, 화면으로 다시 이동하기
4. View의 setNeedsDisplay 또는 setNeedsDisplayInRect: 메소드를 명시적으로 호출하기
그러니까, 정리해보면!
View를 업데이트하려면 야!!!!업데이트해!!!하고 응!!!!!!할게!!!!!하는게 아니라 다음 드로잉사이클때까지 기다렸다가 한꺼번에 되는것인데!!
시스템이 현재 View의 스냅샷 캡쳐(업데이트 전) -> 컨텐츠가 변경되었다?(시스템에게 View업데이트 요청) -> 요청받았다고 바로 그리는게 아니라, 현재 실행루프를 끝날때까지 대기하고==다음 드로잉 사이클때까지 대기한다는 뜻 -> 다음 드로잉 사이클이 오면, 이때 View업데이트를 요청받은 View를 전부 업데이트함
이 되겠네요.
근데 View를 어떻게 업데이트하느냐?? ==> drawRect:메소드를 호출해서 View를 업데이트 하는것!!!!!!
그럼 이제 drawRect:가 뭔지 알아야하겠네요........크흠
근데 이때까지 읽어본거로 조금 추측하자면, drawRect:메소드는 뭔가 뭔가 뭔가를 그려주는 기능을 하는거 같죠?
네! 그렇습니다.
머여 drawRect:래매
네..아직 문서들이 다 업데이트가 안됐나..? rect는 파라미터로 옮겨갔나봐요. 그럼 이제 draw라고 부를게요.
draw의 정의는
"전달된 사각형 내에서 receiver(수신자)의 이미지를 그립니다."라고 하네요. 여기서 전달된 사각형은 파라미터로 받은 CGRect타입을 의미하겠죠?
매개변수인 rect를 다시 한번 봐봅시다.
업데이트해야하는 View의 bounds일부입니다. 처음으로 View를 그릴 때, 이 사각형은 일반적으로 View의 전체 범위입니다. 그러나 이후의 그리기 작업중에서의 사각형은 View의 일부만 지정할 수 있습니다.
이 draw메소드의 디폴트 구현에서는 아무 기능이 없습니다.
Core Graphics 및 UIKit과 같은 기술을 사용하여 뷰의 내용을 그리는 하위 클래스는이 메서드를 재정의하고 해당 드로잉 코드를 구현해야합니다.
이 외의 방법으로는, 위 메소드를 재정의 할 필요가 없습니다. 예를들어, View가 배경색만 표시하거나, View가 기본 레이어 객체를 사용하여 직접 컨텐츠를 설정하느는 경우에는 draw메소드를 재정의 할 필요가 없습니다.
.
.
UIView를 직접 하위클래스로 만들면, 이 메소드를 구현할 때 super를 호출 할 필요가 없습니다. 그러나 다른 View클래스를 하위 클래스화 하는 경우, 구현의 어느 시점에서 super를 호출해야합니다.
이 메소드는 View가 처음 표시될 때, 또는 View의 보이는 부분을 무효화하는 이벤트가 발생할 때 호출됩니다.
직접 이 메소드를 호출하면 안됩니다. View의 일부분을 무효화하고 해당 부분을 다시 그려지게 하려면 대신 setNeedsDisplay () 또는 setNeedsDisplay (_ :) 메서드를 호출하십시오.
자..설명 읽어보니까!!! draw메소드는 정말 뭔가 View에 컨텐츠를 그려주는 역할을 하네요.
또!!!!!!! 더 첨가해서 이때까지의 정리를 해보자면,
View가 처음 로드됨 -> 모든 View들이 준비됨(viewDidLoad. 즉 View객체들이 메모리에 올라감.) -> 컨텐츠를 그려줄때 이때!!!! 처음 draw메소드가 불림 -> View업데이트가 발생함 -> View업데이트해야하니 draw메소드를 호출해볼까?ㅋㄹㅋㅎㅋㅎ ==> XXX!!!! 절대 이 메소드를 직접 호출하면 안됨. View업데이트가 필요하면 setNeedsDisplay () 또는 setNeedsDisplay (_ :) 메소드를 호출해. -> ㅇㅋㅇㅋ요청받았어! 다음 드로잉 사이클에 업데이트 해줄게 -> 다음 드로잉 사이클때 View가 업데이트 됨(우리가 직접 draw메소드를 호출하진 않았지만, setNeedsDisplay () 또는 setNeedsDisplay (_ :) 메소드를 호출했기 때문에 draw메소드가 불림.)
이네요. 제가 정리를 그냥 해봤는데 혹시 틀린부분이 있다면..꼭 말씀해주세요!!!
아무튼 이런 식이네요!!
이제 드로잉사이클도 알고 draw메소드도 아니까 진짜로!!!진짜로 이제 진짜로 우리가 보고있었던 setNeedsDisplay()메소드를 봅시다..
근데 이때까지 그냥 다 설명한게 다에요..
Apple이 설명하는 setNeedsDisplay()를 봅시다.
setNeedsDisplay()메소드 또는 setNeedsDisplay (_ :)를 사용하여 View의 내용을 다시 그려야 함을 시스템에 알릴 수 있습니다.
이 메소드는 요청을 기록하고, 즉시 리턴합니다. View는 다음 드로잉 사이클까지 실제로 다시 그려지지는 않습니다. 이 시점에서 모든 무효화된 View가 업데이트됩니다.
(왜 즉시 리턴하는지는 밑에서 설명합니다.)
Note :
View가 CAEAGLLayer객체 뒤에 있는 경우, setNeedsDisplay()메소드는 아무 효과가 없습니다. 이 메소드는 내용을 렌더링하기 위해 native drawing technologies (예 : UIKit 및 Core Graphics)을 사용하는 View에서만 사용할 수 있습니다.
이 메소드를 사용하면, View의 내용이나 모양이 변경된 경우에만 View를 다시 그립니다. 단순히 View의 기하(geometry)를 변경하면, View는 일반적으로 다시 그려지지 않습니다. 대신 기존 내용은 View의 contentMode 속성 값을 기반으로 조정됩니다. 기존 내용을 다시 표시하면, 변경되지 않은 내용을 다시 그리지 않아도 되므로 성능이 향상됩니다.
그러니까 한마디로;;;;;;;; 계속 똑같은 말 하고 있는것 같은데.....그냥 View가 업데이트되면 직접 draw를 호출하는 대신 setNeedsDisplay () 또는 setNeedsDisplay (_ :) 메소드를 호출해라. 그러면 시스템이 니 View를 다음 드로잉 사이클 때 변경해줄거다. 이거죠.
그럼 여기서 궁금증이 하나 들죠
Q : 엥;;;;나는 setNeedsDisplay()호출한적 없는뎅; 근데 View업데이트 다 잘되던데?
A : 내가 안해도!!!!다 setNeedsDisplay()가 불리고 있습니다.
거의 모든 표준 UI 구성요소. View의 프로퍼티가 수정될 때마다 내부적으로 setNeedsDisplay()가 트리거되고, 영향을 받는 영역이 다시 그려집니다.
Q : 그럼 언제setNeedsDisplay()써야해?
A : 나 자신만의 View를 만들고, 자체 draw메소드를 구현하고, 무언가가 변경되면 "명시적으로" 업데이트 하게 하려면 setNeedsDisplay()를 "명시적으로" 호출해야합니다.
이제 아시겠죠? 왜 내가 setNeedsDisplay()를 호출안해도 View가 잘 업데이트 되었는지 ㅎ
이제 저~~위에서 말한
1. View를 부분적으로 가리고 있던 다른 View이동 또는 제거
2. hidden 프로퍼티를 No로 설정하여, 이전에 숨겨진 View를 다시 볼 수 있게 만들기
3. View를 화면밖으로 스크롤한다음, 화면으로 다시 이동하기
4. View의 setNeedsDisplay 또는 setNeedsDisplayInRect: 메소드를 명시적으로 호출하기
View업데이트를 트리거 할 수 있는 작업들이 왜 이렇게 4가지가 있는지 아시겠죠?
사실 1~3번 내부적으로 setNeedsDisplay()가 호출되고 있는겁니다..
ㅇㅋㅇㅋ
이제 드디어 다음걸 할 수 있겠네요.
뭐가 있는지 안까먹으셨죠?
setNeedsDisplay() ok
setNeedsLayout()
displayIfNeeded()
layoutIfNeeded()
이제 setNeedsLayout()을 해봅시다.
● setNeedsLayout()
흐음 setNeedsDisplay()는 정말 Display라는 말에 걸맞게 View를 다시 그린다든지 아무튼 정말 딱 "Display"였다면 setNeedsLayout()은 뭔가 레이아웃을 잡아주는 걸까요?
같이 봅시다. 위에서 말했듯이 setNeedsLayout()도 UIView의 인스턴스 메소드였어요.
setNeedsLayout()의 정의를 볼까요?
receiver(수신자)의 현재 레이아웃을 무효화하고, 다음 업데이트 주기 동안 레이아웃 업데이트를 트리거합니다.
아하 뭔가 다음 업데이트 주기, 트리거 이런말들을 보니 setNeedsDisplay()와 비슷하네요.
그런데 "레이아웃"이라고 하네요.
View의 하위View의 레이아웃을 조정하려면, App의 main쓰레드에서 이 메소드를 호출하세요.
이 메소드를 요청을 기록하고 즉시 리턴합니다.이 메소드는 즉각적인 업데이트를 강제하지는 않지만, 다음 업데이트 주기를 기다리기 때문에 View를 업데이트하기 전에 여러 View의 레이아웃을 무효화하는데 사용할 수 있습니다.
이러한 행동은 모든 레이아웃 업데이트를 하나의 업데이트 주기로 통합할 수 있으며, 일반적으로 성능향상에 도움이 됩니다.
엥..뭔가 이해가 될 것 같기도 하고 아닌것 같기도하고
하지만 이 사실은 알겠네요. setNeedsLayout()도 setNeedsDisplay()처럼 바로 업데이트가 되는 것이 아니라, 다음 주기를 기다리고 한꺼번에 업데이트를 한다는 거죠.
setNeedsDisplay()메소드에서 draw메소드를 알아야했듯이 setNeedsLayout()메소드에서 하나 알아야 하는 메소드가 있습니다. 바로...
UIView의 인스턴스 메소드인 layoutSubViews()입니다.
정의가 상당히 간단한데요;;;;;;
"SubView들을 배치합니다"
그래서....layoutSubViews()랑 setNeedsLayout()랑 뭔상관인데ㅡㅡ
layoutSubViews()의 기본구현은 iOS 5.1이전 버전에서는 아무것도 수행하지 않습니다.
그렇지 않으면, 기본 구현은 하위 View의 크기와 위치를 결정하기 위해 설정한 제약조건(constraint)를 사용합니다. SubClass는 SubView(하위View)보다 정확한 레이아웃을 실시하기 위해 필요에 따라 이 메소드를 오버라이드 할 수 있습니다.
하위 View의 자동 크기 조정 및 제약 조건 기반 동작이, 원하는 동작을 제공하지 않은 경우에만 이 메소드를 재정의해야합니다.
이 메소드를 구현하여 하위View의 프레임 사각형을 직접 설정할 수 있습니다.
이 메소드를 직접 호출하면 안됩니다. 강제로 레이아웃을 업데이트 하려면 다음 드로잉 업데이트 전에 setNeedsLayout()메소드를 호출하세요. View의 레이아웃을 즉시 업데이트하려면, layoutIfNeeded()메소드를 호출합니다.
으아아가ㅏ아아악 이게 무슨소리인지 정리해봅시다.
역시나 draw메소드와 마찬가지로 이 layoutSubViews()도 직접 호출하면 안된대요. 대신!!!!!!1!!!! 대신에 setNeedsLayout()를 호출하면 다음 setNeedsLayout()가 내부적으로 layoutSubViews()를 호출해서 다음 업데이트 주기 때 모든 레이아웃을 업데이트 해주는거죠.
setNeedsDisplay()를 잘 알고 설명을 보니 조금 이해가 잘 가네요 :) 키키
그런데..........마지막줄에 뭔가 익숙한게 보이죠.
"View의 레이아웃을 즉시 업데이트하려면, layoutIfNeeded()메소드를 호출합니다. "
setNeedsDisplay()
setNeedsLayout()
displayIfNeeded()
⭐️layoutIfNeeded()⭐️
아앗
모야 왜 지금나와.......
일단 setNeedsLayout()을 좀 더 살펴보고 바로 layoutIfNeeded()이 뭔지 알아봅시다.
setNeedsLayout()은 간단하게 정리하면!!!! 업데이트 주기에 해당 View와 모든 하위 View를 레이아웃하고 다시그려야한다는 것으 시스템에 알려주는 역할을 하는겁니다. 또 새롭게 안 사실이 있는데, setNeedsLayout()은 비동기 액티비티라고 해요. 메소드가 완료되어 즉시 반환되기 때문이죠. 그러나 레이아웃과 다시 그리는 작업이 실제로 발생하기 까지는 아직 이른 상태이며, 업데이트 주기가 언제일지도 모르는 상태랍니다.
자연스럽게 setNeedsDisplay()도 호출 즉시 반환된다고 했으므로 비동기 작업이 되겠네요.
layoutIfNeeded()를 이제 설명드리겠습니다.
● layoutIfNeeded()
위에서 말했듯이
setNeedsLayout()은 레이아웃 업데이트 해줄ㄹㅐ..? ==> 아;; 다음 업데이트 주기까지 기다려;..라면
layoutIfNeeded()은 레이아웃 업데이트해. ==> 네.
이라는 느낌?
"View의 레이아웃을 즉시 업데이트하려면, layoutIfNeeded()메소드를 호출합니다. "라고 하니까요.
즉시!!!
layoutIfNeeded()에 대한 Apple설명을 봐봅시다.
레이아웃 업데이트가 보류중인 경우, 하위 View를 즉시 레이아웃(배치)합니다.
이 메소드를 사용하면 View가 레이아웃을 즉시 업데이트 할 수 있습니다. autoLayout을 사용할 때, 레이아웃 엔진은 필요에 따라 View의 위치를 업데이트하여 제약조건의 변경사항을 충족시킵니다. 이 메소드는 메세지를 루트View로 수신하는 View를 사용하여 루트에서 시작하는 View 하위 트리를 배치합니다.
보류 중의 레이아웃 갱신이 없는 경우, 이 메소드는 레이아웃을 변경하거나 레이아웃 관련 콜백을 호출하지 않고 종료합니다.
흐음
그럼 setNeedsLayout ()와 layoutIfNeeded()의 차이점은 즉시냐 아니냐밖에 없는 걸까요?
한가지 더 차이점이 있습니다.
바로 layoutIfNeeded()는 비동기 호출이 아니라!!! 동기호출입니다. 즉, 업데이트주기를 기다리지 않고 즉시 완료하려고 하죠. 이 메소드에 대한 호출이 완료되면, 메소드 호출 이전에 기록된 모든 변경내용을 기반으로 레이아웃이 이미 조정되고 그려져있습니다.
ㅇㅏ ㅎㅏ
그렇군
하지만 여전히 이 두 메소드 간의 차이점을 모르시는 분들이 계실텐데요,
여기에 정말 잘 이해되는 설명이 있습니다. 영어지만......
이 설명에 사용된 예제가 github에 있는데!! 링크 걸어도 되겠죠..? 아니 진짜 좋은 예제라..
위 사이트안에서도 github링크가 있으니 참고하시길 바랍니다.
움짤로 간단하게 setNeedsLayout()와 layoutIfNeeded()의 차이점에 대해 보여드리자면
일단 오른쪽이 뭔가 "즉시"되는것 같죠? 그렇습니다. 오른쪽이 setNeedsLayout()를 적용한거고
왼쪽이 layoutIfNeeded()을 적용한거에요 :)
이렇게 어쩌다보니 마지막까지 왔네요.
setNeedsDisplay() ok
setNeedsLayout() ok
displayIfNeeded()
layoutIfNeeded() ok
● displayIfNeeded()
UIView의 인스턴스 메소드가 아니었단 말이야...?
그리고 짱많음....모야...
아니 그리고 어떤것의 인스턴스 메소드인가에 따라 설명이 달라서 zzz
뭔가 displayIfNeeded()만 따로 정리하는게 나을 것 같네요...
아무튼 오늘 좀 이해가 가셨나요? 저도 이게 도대체 뭘 해주는 코드 인가..한 궁금증에서 벗어나서 좋네요 XD
그리고 오늘부터 심사를 받을 수 있어서 너무 좋ㅠㅠㅠㄹㅇ남ㅇ;ㄴ망ㄴㅁ두근두근두근
오늘 한 것 들 중에서 궁금하거나 틀린부분!!!! 틀린 부분 있을 수 있어요. 그러니까 발견하시면 댓글이나 PC화면 오른쪽 하단에서 볼 수 있는 채널서비스를 이용해서 메세지 주시면 정말 감사하겠습니댜
오늘도 도움이 되었길 바래요