UIKit

viewDidLayoutSubviews

Phililip
728x90

안녕하세요.

 

다들 알고 계셨겠지만... 저는 이번에 삽질하면서 처음 알게 된 viewDidLayoutSubviews에 대해서 간단하게 알아보려고 해요.

 

 


 

우선.. 제가 삽질하게 된 상황 설명하면서 글을 이어나갈게요. (나중에 똑같은 삽질은 하지 말쟈..)

 

 

저는 화면에 UIImageView를 원형으로 출력시키고 싶었어요.

 

그래서 스토리보드에 UIImageView를 넣고 AutoLayout을 걸어줬어요.

  • 화면 정중앙
  • UIImageView의 width : height = 1 : 1 (정사각형 모양)
  • UIImageView의 width는 현재 ViewController width의 1/2

 

 

 

그다음 UIImageView를 원형으로 만들기 위해서, ViewController에서 아래처럼 cornerRadius를 설정해줬어요.

class ViewController: UIViewController {
    @IBOutlet weak var imageView: UIImageView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        imageView.layer.cornerRadius = imageView.frame.width / 2.0
    }
}

 

 

시뮬레이터에서 확인하니 잘 나오더라구요.

 

 

 

근데 문제가 여기서 발생합니다.

 

제가 AutoLayout을 UIImageView의 width를 현재 ViewController width의 1/2로 주었던 이유는 화면 크기가 작은 기기에서도 적절한 크기로 출력되게 하고 싶었기 때문인데요.

 

 

그래서 가벼운 마음으로 iPod touch로 확인을 해봤더니..

 

 

엌.............. 

 

아늬... UIImageView의 width도 기기 크기에 맞춰졌는데... cornerRadius도 잘 설정되야 하는거 아님???

 

 

그래서 cornerRadius를 설정하는 부분에 로그를 찍어봤어요.

override func viewDidLoad() {
    super.viewDidLoad()
    imageView.layer.cornerRadius = imageView.frame.width / 2.0
    print("ViewController.view frame: \(view.frame)")
    print("ImageView frame: \(imageView.frame)")
}
// iPhone 11 Simulator
ViewController.view frame: (0.0, 0.0, 414.0, 896.0)
ImageView frame: (103.5, 344.5, 207.0, 207.0)

// iPod touch Simulator
ViewController.view frame: (0.0, 0.0, 320.0, 568.0)
ImageView frame: (103.5, 344.5, 207.0, 207.0)

 

음??

 

iPod touch의 width는 320인데 UIImageView의 width가 207...??

 

왜 고정값으로 나왔나... 고민을 해봤는데, 왠지 207이란 값이 현재 iPhone 11로 설정된 스토리보드의 UIImageView width인 것 같더라구요.

 

 

 

 

오호라.... 

 

그래서 viewDidLoad의 공식 가이드를 봤어요.

This method is called after the view controller has loaded its view hierarchy into memory. This method is called regardless of whether the view hierarchy was loaded from a nib file or created programmatically in the 
loadView() method. You usually override this method to perform additional initialization on views that were loaded from nib files.

 

ViewController가 메모리에서 View hierarchy(계층)으로 로드된 이후에 호출된다.

 

 

오호... viewDidLoad가 호출되는 시점은 ViewController가 뷰 계층에 올라갈 때 호출될 뿐, 스토리보드에서 설정한 것들이 subView에 아직 반영되지 않은 것인가 보네요.

 

 

 

 

흠... 그럼 지금 상태에서 해결할 수 있는 방법은 뭐가 있을까요?

 

가장 먼저 떠오른 방법은, viewDidLoad에서 UIImageView의 cornerRadius 값을 UIImageView의 width가 아닌 현재 ViewController.view의 width로 계산하는 것이에요.

override func viewDidLoad() {
    super.viewDidLoad()
    imageView.layer.cornerRadius = (view.frame.width / 2.0) / 2.0
}

 

지금은 현재 화면의 1/2 크기만큼의 UIImageView width를 설정해줬지만, 만약 비율이 달라지도록(ex. 이미지 너비를 화면 너비의 1/3으로 변경) 스펙이 변경되면 위 코드도 직접 수정해줘야 하기 때문에 유지보수에는 좋아 보이지 않네요..

 

 

 

다른 방법은 viewDidLayoutSubviews method를 사용하는 것입니다.

 

Your view controller can override this method to make changes after the view lays out its subviews. The default implementation of this method does nothing.

 

 

subView들의 layout이 설정된 이후에 호출되는 method입니다.

 

즉, 아래처럼 구현하면 되겠죠??

class ViewController: UIViewController {
    @IBOutlet weak var imageView: UIImageView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func viewDidLayoutSubviews() {  ✅
        super.viewDidLayoutSubviews()
        imageView.layer.cornerRadius = imageView.frame.width / 2.0 
    }
}

 

 

결과도 잘 나오네요ㅎㅎ

 

 

 

 

## 참고

- https://developer.apple.com/documentation/uikit/uiviewcontroller/1621398-viewdidlayoutsubviews

- https://stackoverflow.com/questions/44500340/view-frame-width-is-correct-in-viewdidload-with-storyboard-why

 

 


이번 글은 여기서 마무리.

 

 

 

반응형