Thực hành đúng cho phân lớp UIView?


158

Tôi đang làm việc trên một số điều khiển đầu vào dựa trên UIView tùy chỉnh và tôi đang cố gắng xác định thực hành đúng để thiết lập chế độ xem. Khi làm việc với một UIViewController, nó khá đơn giản để sử dụng loadViewvà có liên quan viewWill, viewDidphương pháp, nhưng khi subclassing một UIView, các methosds gần nhất tôi có là `awakeFromNib, drawRect, và layoutSubviews. (Tôi đang suy nghĩ về mặt thiết lập và gọi lại phân tích.) Trong trường hợp của tôi, tôi đang thiết lập khung và khung nhìn bên trong của mình layoutSubviews, nhưng tôi không thấy bất cứ điều gì trên màn hình.

Cách tốt nhất để đảm bảo rằng chế độ xem của tôi có chiều cao và chiều rộng chính xác mà tôi muốn nó có là gì? (Câu hỏi của tôi được áp dụng bất kể tôi có đang sử dụng tính năng tự động thanh toán hay không, mặc dù có thể có hai câu trả lời.) "Cách thực hành tốt nhất" đúng đắn là gì?

Câu trả lời:


298

Apple định nghĩa khá rõ ràng cách phân lớp UIViewtrong tài liệu.

Kiểm tra danh sách dưới đây, đặc biệt hãy xem initWithFrame:layoutSubviews. Cái trước được dự định để thiết lập khung của bạn UIViewtrong khi cái trước được dự định để thiết lập khung và bố cục của các cuộc phỏng vấn của nó.

Cũng nên nhớ rằng initWithFrame:nó chỉ được gọi nếu bạn đang khởi tạo UIViewchương trình của mình . Nếu bạn đang tải nó từ tệp nib (hoặc bảng phân cảnh), initWithCoder:sẽ được sử dụng. Và trong initWithCoder:khung chưa được tính toán, vì vậy bạn không thể sửa đổi khung bạn đã thiết lập trong Trình tạo giao diện. Như được đề xuất trong câu trả lời này, bạn có thể nghĩ đến việc gọi initWithFrame:từ initWithCoder:để thiết lập khung.

Cuối cùng, nếu bạn tải UIViewtừ ngòi (hoặc bảng phân cảnh), bạn cũng có awakeFromNibcơ hội thực hiện khởi tạo khung và bố cục tùy chỉnh, kể từ khi awakeFromNibđược gọi, đảm bảo rằng mọi chế độ xem trong cấu trúc phân cấp đã không được lưu trữ và khởi tạo.

Từ tài liệu của NSNibAwaking(bây giờ được thay thế bởi tài liệu của awakeFromNib):

Tin nhắn đến các đối tượng khác có thể được gửi một cách an toàn từ bên trong awakeFromNib, khi đó nó đảm bảo rằng tất cả các đối tượng không được lưu trữ và khởi tạo (tất nhiên là không nhất thiết phải được đánh thức)

Cũng đáng lưu ý rằng với tính năng tự động thanh toán, bạn không nên đặt khung rõ ràng cho chế độ xem của mình. Thay vào đó, bạn phải chỉ định một tập hợp các ràng buộc đủ, để khung được tự động tính toán bởi công cụ bố trí.

Trực tiếp từ tài liệu :

Phương pháp ghi đè

Khởi tạo

  • initWithFrame:Chúng tôi khuyên bạn nên thực hiện phương pháp này. Bạn cũng có thể thực hiện các phương thức khởi tạo tùy chỉnh ngoài, hoặc thay vì phương thức này.

  • initWithCoder: Thực hiện phương pháp này nếu bạn tải chế độ xem của mình từ tệp nib của Trình tạo giao diện và chế độ xem của bạn yêu cầu khởi tạo tùy chỉnh.

  • layerClassChỉ thực hiện phương pháp này nếu bạn muốn chế độ xem của mình sử dụng lớp Hoạt hình lõi khác cho cửa hàng sao lưu của nó. Ví dụ: nếu bạn đang sử dụng OpenGL ES để thực hiện bản vẽ của mình, bạn sẽ muốn ghi đè phương thức này và trả về lớp CAEAGLLayer.

Vẽ và in

  • drawRect:Thực hiện phương pháp này nếu chế độ xem của bạn vẽ nội dung tùy chỉnh. Nếu chế độ xem của bạn không thực hiện bất kỳ bản vẽ tùy chỉnh nào, hãy tránh ghi đè phương thức này.

  • drawRect:forViewPrintFormatter: Chỉ thực hiện phương pháp này nếu bạn muốn vẽ nội dung của chế độ xem khác nhau trong khi in.

Những ràng buộc

  • requiresConstraintBasedLayout Thực hiện phương thức lớp này nếu lớp xem của bạn yêu cầu các ràng buộc để hoạt động đúng.

  • updateConstraints Thực hiện phương pháp này nếu chế độ xem của bạn cần tạo các ràng buộc tùy chỉnh giữa các lần xem trước của bạn.

  • alignmentRectForFrame:, frameForAlignmentRect:Triển khai các phương thức này để ghi đè cách các chế độ xem của bạn được căn chỉnh với các chế độ xem khác.

Bố trí

  • sizeThatFits:Thực hiện phương pháp này nếu bạn muốn chế độ xem của mình có kích thước mặc định khác với kích thước thông thường trong quá trình thay đổi kích thước. Ví dụ: bạn có thể sử dụng phương pháp này để ngăn chế độ xem của bạn thu nhỏ đến điểm mà các cuộc phỏng vấn không thể được hiển thị chính xác.

  • layoutSubviews Thực hiện phương pháp này nếu bạn cần kiểm soát chính xác hơn bố cục của các cuộc phỏng vấn của mình hơn là các hành vi ràng buộc hoặc tự động hóa cung cấp.

  • didAddSubview:, willRemoveSubview:Thực hiện các phương pháp này khi cần thiết để theo dõi các bổ sung và loại bỏ các cuộc phỏng vấn.

  • willMoveToSuperview:, didMoveToSuperviewTriển khai các phương thức này khi cần thiết để theo dõi chuyển động của chế độ xem hiện tại trong phân cấp chế độ xem của bạn.

  • willMoveToWindow:, didMoveToWindowThực hiện các phương pháp này khi cần thiết để theo dõi chuyển động của chế độ xem của bạn sang một cửa sổ khác.

Xử lý sự kiện:

  • touchesBegan:withEvent:, touchesMoved:withEvent:, touchesEnded:withEvent:, touchesCancelled:withEvent:Thực hiện các phương pháp này nếu bạn cần để xử lý các sự kiện liên lạc trực tiếp. (Đối với đầu vào dựa trên cử chỉ, hãy sử dụng nhận dạng cử chỉ.)

  • gestureRecognizerShouldBegin: Thực hiện phương pháp này nếu chế độ xem của bạn xử lý các sự kiện chạm trực tiếp và có thể muốn ngăn nhận dạng cử chỉ đính kèm kích hoạt các hành động bổ sung.


những gì về - (void) setFrame: (CGRect) khung?
pfrank

tốt, bạn chắc chắn có thể ghi đè lên nó, nhưng cho mục đích gì?
Gabriele Petronella

để thay đổi bố cục / bản vẽ bất cứ lúc nào kích thước khung hình hoặc thay đổi vị trí
pfrank

1
Thế còn layoutSubviews?
Gabriele Petronella

Từ stackoverflow.com/questions/4000664/ , "rắc rối với điều này là các cuộc phỏng vấn không chỉ có thể thay đổi kích thước của chúng, mà chúng còn có thể làm thay đổi kích thước thay đổi. Chưa từng thử nghiệm cá nhân
pfrank

38

Điều này vẫn còn tăng cao trong Google. Dưới đây là một ví dụ cập nhật cho swift.

Các didLoadchức năng cho phép bạn đặt tất cả các mã tùy chỉnh khởi tạo của bạn. Như những người khác đã đề cập, didLoadsẽ được gọi khi một chế độ xem được tạo lập trình thông qua init(frame:)hoặc khi trình giải nén XIB hợp nhất một mẫu XIB vào chế độ xem của bạn thông quainit(coder:)

Ngoài ra : layoutSubviewsupdateConstraintsđược gọi nhiều lần cho phần lớn các lượt xem. Điều này được dành cho các bố cục và điều chỉnh nhiều lượt nâng cao khi giới hạn của chế độ xem thay đổi. Cá nhân, tôi tránh bố cục nhiều lượt khi có thể vì chúng đốt cháy chu kỳ CPU và khiến mọi thứ đau đầu. Ngoài ra, tôi đặt mã ràng buộc trong chính bộ khởi tạo vì tôi hiếm khi làm mất hiệu lực chúng.

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
  }
}

Bạn có thể giải thích khi nào / tại sao bạn lại tách mã ràng buộc bố cục giữa một phương thức được gọi từ quy trình init của bạn và layoutSubview và updateConstraint không? Có vẻ như tất cả ba vị trí ứng cử viên có thể đặt mã bố cục. Vậy làm thế nào để bạn biết khi nào / cái gì / tại sao để phân chia mã bố cục giữa ba?
Clay Ellis

3
Tôi không bao giờ sử dụng updateConstraint; updateConstraint có thể tốt vì bạn biết hệ thống phân cấp chế độ xem của bạn đã được thiết lập đầy đủ trong init, vì vậy bạn không thể đưa ra một ngoại lệ bằng cách thêm một ràng buộc giữa hai chế độ xem không theo thứ bậc :) layoutSubview không bao giờ nên sửa đổi ràng buộc; nó có thể dễ dàng gây ra một đệ quy vô hạn vì layoutSubview được gọi nếu các ràng buộc bị 'vô hiệu hóa' trong quá trình bố trí. Thiết lập bố cục thủ công (như trong cài đặt khung trực tiếp, điều mà bạn hiếm khi cần thực hiện nữa trừ khi vì lý do hiệu suất) đi vào layoutSubview. Cá nhân, tôi đặt các ràng buộc sáng tạo trong init
seo

Đối với mã kết xuất tùy chỉnh, chúng ta có nên ghi đè drawphương thức?
Petrus Theron

14

Có một bản tóm tắt khá hay trong tài liệu của Apple và điều này được đề cập rất tốt trong khóa học Stanford miễn phí có sẵn trên iTunes. Tôi trình bày phiên bản TL; DR của mình tại đây:

Nếu lớp học của bạn chủ yếu bao gồm các cuộc phỏng vấn, đúng nơi để phân bổ chúng là trong các initphương thức. Đối với chế độ xem, có hai initphương thức khác nhau có thể được gọi, tùy thuộc vào việc chế độ xem của bạn được khởi tạo từ mã hay từ bảng phân cảnh / kịch bản. Những gì tôi làm là viết setupphương thức của riêng tôi , và sau đó gọi nó từ cả phương thức initWithFrame:initWithCoder:phương thức.

Nếu bạn đang thực hiện bản vẽ tùy chỉnh, bạn thực sự muốn ghi đè lên drawRect:chế độ xem của mình. Tuy nhiên, nếu chế độ xem tùy chỉnh của bạn chủ yếu là một thùng chứa cho các cuộc phỏng vấn, có lẽ bạn sẽ không cần phải làm điều đó.

Chỉ ghi đè layoutSubViewsnếu bạn muốn làm một cái gì đó như thêm hoặc xóa một khung nhìn phụ tùy thuộc vào việc bạn đang ở hướng dọc hay ngang. Nếu không, bạn sẽ có thể để nó một mình.


Tôi sử dụng câu trả lời của bạn để thay đổi khung nhìn của khung nhìn (là awakeFromNib) layoutSubViews, nó đã hoạt động.
máy bay

1

layoutSubviews có nghĩa là để đặt khung trên chế độ xem con chứ không phải trên chế độ xem.

Đối với UIView, hàm tạo được chỉ định là thông thường initWithFrame:(CGRect)framevà bạn nên đặt khung ở đó (hoặc trong initWithCoder:), có thể bỏ qua giá trị khung được truyền. Bạn cũng có thể cung cấp một hàm tạo khác nhau và đặt khung ở đó.


bạn có thể lấy nó chi tiết hơn? Tôi không biết ý nghĩa của bạn. Làm thế nào để đặt khung của khung nhìn phụ? quan điểm làawakeFromNib
máy bay

Chuyển nhanh đến năm 2016, có lẽ bạn không nên thiết lập khung nào cả và sử dụng tự động thanh toán (các ràng buộc). Nếu chế độ xem đến từ XIB (hoặc bảng phân cảnh), thì chế độ xem phụ đã được thiết lập.
proxi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.