UIView 서브 클래 싱에 대한 올바른 연습?


158

일부 사용자 정의 UIView 기반 입력 컨트롤을 작업 중이며 뷰 설정에 대한 적절한 연습을 확인하려고합니다. UIViewController에 작업 할 때, 그것은 사용하기 매우 간단 loadView관련 viewWill, viewDid방법,하지만 UIView의 서브 클래스 때, 내가 가지고있는 가장 가까운 methosds는 `awakeFromNib, drawRectlayoutSubviews. (설정 및 분해 콜백 측면에서 생각하고 있습니다.) 제 경우에는에서 프레임과 내부보기를 설정하고 layoutSubviews있지만 화면에 아무것도 표시되지 않습니다.

뷰의 높이와 너비가 올바른지 확인하는 가장 좋은 방법은 무엇입니까? (자동 레이아웃을 사용하는지 여부에 관계없이 두 가지 답변이있을 수 있지만 내 질문은 적용됩니다.) 올바른 "모범 사례"는 무엇입니까?

답변:


298

애플 UIView은 문서에서 서브 클래 싱하는 방법을 명확하게 정의했다 .

특히 살펴보고, 아래의 목록을 확인 initWithFrame:하고 layoutSubviews. 전자는 당신 UIView의 프레임을 설정하기위한 것이고 후자는 프레임과 서브 뷰의 레이아웃을 설정하기위한 것입니다.

또한 프로그래밍 방식 initWithFrame:으로 인스턴스화하는 경우에만 호출됩니다 UIView. 펜촉 파일 (또는 스토리 보드)에서로드하는 경우 initWithCoder:사용됩니다. 그리고 initWithCoder:프레임 에서 아직 계산되지 않았으므로 Interface Builder에서 설정 한 프레임을 수정할 수 없습니다. 이 답변에서 제안한 대로 프레임을 설정하기 위해 전화 initWithFrame:를 생각할 수 있습니다 initWithCoder:.

마지막으로 UIView펜촉 (또는 스토리 보드)에서로드하면 awakeFromNib사용자 정의 프레임 및 레이아웃 초기화를 수행 할 수있는 기회가 awakeFromNib있습니다.

의 문서에서 NSNibAwaking(현재 의 문서로 대체 됨 awakeFromNib) :

다른 객체에 대한 메시지는 awakeFromNib 내에서 안전하게 보낼 수 있습니다.이 시간까지 모든 객체가 보관 해제되고 초기화됩니다 (물론 반드시 깨어날 필요는 없습니다).

자동 레이아웃을 사용하면 뷰의 프레임을 명시 적으로 설정해서는 안된다는 점도 주목할 가치가 있습니다. 대신 프레임이 레이아웃 엔진에 의해 자동으로 계산되도록 충분한 구속 조건 세트를 지정해야합니다.

문서 에서 바로 :

재정의하는 방법

초기화

  • initWithFrame:이 방법을 구현하는 것이 좋습니다. 이 방법 외에도 또는이 방법 대신 사용자 정의 초기화 방법을 구현할 수도 있습니다.

  • initWithCoder: Interface Builder nib 파일에서보기를로드하고보기에 사용자 정의 초기화가 필요한 경우이 방법을 구현하십시오.

  • layerClass뷰에서 백업 저장소에 다른 Core Animation 레이어를 사용하려는 경우에만이 방법을 구현하십시오. 예를 들어, OpenGL ES를 사용하여 도면을 작성하는 경우이 메소드를 대체하고 CAEAGLLayer 클래스를 리턴하려고합니다.

그리기 및 인쇄

  • drawRect:뷰에서 사용자 정의 컨텐츠를 그리는 경우이 방법을 구현하십시오. 뷰에서 사용자 정의 도면을 수행하지 않으면이 방법을 재정의하지 마십시오.

  • drawRect:forViewPrintFormatter: 인쇄하는 동안보기의 내용을 다르게 그리려는 경우에만이 방법을 구현하십시오.

제약

  • requiresConstraintBasedLayout 뷰 클래스가 제대로 작동하기 위해 제약 조건이 필요한 경우이 클래스 메서드를 구현하십시오.

  • updateConstraints 뷰에서 하위 뷰간에 사용자 지정 구속 조건을 작성해야하는 경우이 방법을 구현하십시오.

  • alignmentRectForFrame:, frameForAlignmentRect:뷰가 다른 뷰에 정렬되는 방식을 무시하려면이 방법을 구현하십시오.

나열한 것

  • sizeThatFits:뷰 크기 조정 작업 중에 일반적으로 보는 것과 다른 기본 크기를 가지려면이 방법을 구현하십시오. 예를 들어이 방법을 사용하면 뷰가 하위 뷰를 올바르게 표시 할 수없는 지점으로 축소되는 것을 방지 할 수 있습니다.

  • layoutSubviews 구속 조건 또는 자동 크기 조정 동작이 제공하는 것보다 하위 뷰의 레이아웃을보다 정확하게 제어해야하는 경우이 방법을 구현하십시오.

  • didAddSubview:, willRemoveSubview:하위보기의 추가 및 제거를 추적하는 데 필요에 따라 이러한 방법을 구현하십시오.

  • willMoveToSuperview:, didMoveToSuperview뷰 계층에서 현재 뷰의 이동을 추적하는 데 필요에 따라이 메소드를 구현하십시오.

  • willMoveToWindow:, didMoveToWindow보기가 다른 창으로 이동하는 것을 추적하는 데 필요에 따라 이러한 방법을 구현하십시오.

이벤트 처리 :

  • touchesBegan:withEvent:, touchesMoved:withEvent:, touchesEnded:withEvent:, touchesCancelled:withEvent:직접 터치 이벤트를 처리해야하는 경우이 방법을 구현합니다. 제스처 기반 입력의 경우 제스처 인식기를 사용하십시오.

  • gestureRecognizerShouldBegin: 뷰에서 터치 이벤트를 직접 처리하고 연결된 제스처 인식기가 추가 작업을 트리거하지 못하게하려면이 방법을 구현하십시오.


어떻습니까-(void) setFrame : (CGRect) frame?
pfrank

글쎄, 당신은 확실히 그것을 무시할 수 있지만, 어떤 목적으로?
Gabriele Petronella

프레임 크기 또는 위치가 변경 될 때마다 레이아웃 / 그리기를 변경하는 방법
pfrank

1
무엇에 대해 layoutSubviews?
Gabriele Petronella

에서 stackoverflow.com/questions/4000664/... , "이 가진 문제는 파단은 자신의 크기를 변경할 수 없지만 그 크기 변경에 애니메이션을 적용 할 수있다. UIView의 애니메이션을 실행하면 때마다 layoutSubviews를 호출하지 않습니다." 그래도 개인적으로 테스트하지 않았습니다
pfrank

38

이것은 여전히 ​​구글에서 높다. 다음은 신속한 업데이트를위한 예입니다.

didLoad기능을 사용하면 모든 사용자 정의 초기화 코드를 넣을 수 있습니다. 다른 사람들이 언급했듯이 didLoad프로그래밍 방식으로 뷰를 만들 init(frame:)거나 XIB 디시리얼라이저가 XIB 템플릿을 통해 뷰에 XIB 템플릿을 병합 할 때 호출됩니다init(coder:)

제외 : layoutSubviewsupdateConstraints전망의 대부분을 여러 번 호출된다. 이것은 뷰의 경계가 변경 될 때 고급 다중 패스 레이아웃 및 조정을위한 것입니다. 개인적으로, 나는 CPU 사이클을 태우고 모든 것을 두통으로 만들기 때문에 가능한 경우 다중 패스 레이아웃을 피합니다. 또한, 거의 무효화하지 않기 때문에 이니셜 라이저 자체에 제약 조건 코드를 넣었습니다.

import UIKit

class MyView: UIView {
  //-----------------------------------------------------------------------------------------------------
  //Constructors, Initializers, and UIView lifecycle
  //-----------------------------------------------------------------------------------------------------
  override init(frame: CGRect) {
      super.init(frame: frame)
      didLoad()
  }

  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    didLoad()
  }

  convenience init() {
    self.init(frame: CGRectZero)
  }

  func didLoad() {
    //Place your initialization code here

    //I actually create & place constraints in here, instead of in
    //updateConstraints
  }

  override func layoutSubviews() {
     super.layoutSubviews()

     //Custom manually positioning layout goes here (auto-layout pass has already run first pass)
  }

  override func updateConstraints() {
    super.updateConstraints()

    //Disable this if you are adding constraints manually
    //or you're going to have a 'bad time'
    //self.translatesAutoresizingMaskIntoConstraints = false

    //Add custom constraint code here
  }
}

init 프로세스에서 호출 된 메소드와 layoutSubviews 및 updateConstraints 사이에 레이아웃 제약 조건 코드를 언제 / 왜 분할 해야하는지 설명 할 수 있습니까? 레이아웃 코드를 배치 할 수있는 3 개의 가능한 후보 위치 인 것 같습니다. 그렇다면 세 가지 레이아웃 코드를 언제 / 무엇 / 왜 분리해야하는지 어떻게 알 수 있습니까?
Clay Ellis

3
나는 updateConstraints를 사용하지 않는다. 뷰 계층 구조가 init에 완전히 설정되어 있기 때문에 updateConstraints가 좋을 수 있으므로 계층 구조에없는 두 개의 뷰 사이에 제약 조건을 추가하여 예외를 발생시킬 수는 없습니다. 레이아웃 단계에서 제약 조건이 '무효화'되면 layoutSubviews가 호출되므로 무한 재귀가 쉽게 발생할 수 있습니다. 수동 레이아웃 설정 (프레임을 직접 설정하는 것과 같이 성능상의 이유가 없다면 더 이상 할 필요가 거의 없음)은 layoutSubviews에 들어갑니다. 개인적으로, 나는 제약 조건 생성을 init에 배치합니다

사용자 정의 렌더링 코드의 경우 draw메서드 를 재정의해야 합니까?
Petrus Theron

14

Apple 문서 에는 괜찮은 요약 이 있으며, 이것은 iTunes 의 무료 Stanford 과정 에서 잘 다룹니다 . 내 TL; DR 버전을 여기에 제시하십시오.

클래스가 대부분 서브 뷰로 구성되어있는 경우 클래스를 할당 할 수있는 적절한 위치는 init메소드입니다. 뷰의 init경우 뷰가 코드 또는 펜촉 / 스토리 보드에서 인스턴스화되는지에 따라 호출 될 수있는 두 가지 방법이 있습니다. 내가하는 일은 내 자신의 setup메서드를 작성한 다음 initWithFrame:and initWithCoder:메서드 에서 호출하는 것입니다 .

사용자 정의 도면을 수행하는 경우 실제로 drawRect:보기에서 재 지정하려고합니다 . 그러나 사용자 정의보기가 대부분 하위보기의 컨테이너 인 경우에는 그렇게하지 않아도됩니다.

layoutSubViews세로 또는 가로 방향인지 여부에 따라 하위보기 추가 또는 제거와 같은 작업을 수행하려는 경우 에만 재정의 하십시오. 그렇지 않으면 혼자 둘 수 있습니다.


귀하의 답변을 사용하여 뷰 (awakeFromNib)의 subView 프레임을 변경 layoutSubViews하면 제대로 작동합니다.
항공기

1

layoutSubviews 뷰 자체가 아닌 자식 뷰에 프레임을 설정하는 것을 의미합니다.

의 경우 UIView지정된 생성자는 일반적으로 프레임 값을 전달한 것을 무시하고 initWithFrame:(CGRect)frame프레임을 설정해야 initWithCoder:합니다. 다른 생성자를 제공하고 프레임을 설정할 수도 있습니다.


좀 더 자세히 설명해 주시겠습니까? 뷰의 subView 프레임을 설정하는 평균을 어떻게 알았습니까? 보기awakeFromNib
항공기

2016 년으로 가려면 프레임을 전혀 설정하지 말고 자동 레이아웃 (제약)을 사용하지 않아야합니다. 뷰가 XIB (또는 스토리 보드)에서 온 경우 하위 뷰가 이미 설정되어 있어야합니다.
proxi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.