Làm cách nào để tôi viết một init tùy chỉnh cho một lớp con UIView trong Swift?


125

Nói rằng tôi muốn initmột UIViewlớp con với a Stringvà an Int.

Làm thế nào tôi có thể làm điều này trong Swift nếu tôi chỉ phân lớp UIView? Nếu tôi chỉ tạo một init()hàm tùy chỉnh nhưng các tham số là Chuỗi và Int, nó sẽ cho tôi biết rằng "super.init () không được gọi trước khi quay lại từ trình khởi tạo".

Và nếu tôi gọi super.init()tôi được bảo là tôi phải sử dụng một trình khởi tạo được chỉ định. Tôi nên sử dụng cái gì ở đó? Phiên bản khung? Phiên bản coder? Cả hai? Tại sao?

Câu trả lời:


207

Các init(frame:)phiên bản là khởi tạo mặc định. Bạn phải gọi nó chỉ sau khi khởi tạo các biến thể hiện của bạn. Nếu chế độ xem này được hoàn nguyên từ Nib thì trình khởi tạo tùy chỉnh của bạn sẽ không được gọi và thay vào đó init?(coder:)phiên bản sẽ được gọi. Vì Swift bây giờ yêu cầu triển khai các yêu cầu init?(coder:), tôi đã cập nhật ví dụ bên dưới và thay đổi letkhai báo biến thành varvà tùy chọn. Trong trường hợp này, bạn sẽ khởi tạo chúng trong awakeFromNib()hoặc một thời gian sau.

class TestView : UIView {
    var s: String?
    var i: Int?
    init(s: String, i: Int) {
        self.s = s
        self.i = i
        super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
    }

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

5
Sau đó, bằng mọi cách làm cho họ var. Nhưng thực tế tốt nhất mặc định trong Swift là khai báo các biến lettrừ khi có lý do để khai báo chúng var. Do đó, không có lý do nào để làm như vậy trong ví dụ mã của tôi ở trên let.
Sói McNally

2
Mã này không biên dịch. Bạn cần phải thực hiện các trình khởi tạo cần thiết init(coder:).
Thập kỷ Mặt trăng

3
thú vị như thế nào điều này được biên soạn từ nhiều năm trước. Ngày nay, nó phàn nàn theo init (coder :) rằng "Thuộc tính self.s không được khởi tạo tại cuộc gọi super.init"
mafiOSo

Đã sửa ví dụ cho Swift 3.1. Biên dịch dưới một sân chơi nhập khẩu UIKit.
Wolf McNally

1
@LightNight Tôi đã thực hiện siTùy chọn để giữ mọi thứ đơn giản ở đây. Nếu chúng không phải là tùy chọn, chúng cũng cần được khởi tạo trong trình khởi tạo được yêu cầu. Làm cho chúng tùy chọn có nghĩa là chúng sẽ được nilkhi super.init()được gọi. Nếu họ không có tùy chọn, họ thực sự cần phải được chỉ định trước khi gọi super.init ().
Wolf McNally

32

Tôi tạo một init chung cho chỉ định và yêu cầu. Để thuận tiện, tôi ủy thác init(frame:)với khung bằng không.

Có khung bằng 0 không phải là vấn đề vì thông thường chế độ xem nằm trong chế độ xem của ViewContoder; chế độ xem tùy chỉnh của bạn sẽ có cơ hội tốt, an toàn để bố trí các cuộc phỏng vấn của nó khi giám sát của nó gọi layoutSubviews()hoặc updateConstraints(). Hai hàm này được hệ thống gọi đệ quy trong toàn bộ phân cấp khung nhìn. Bạn có thể sử dụng updateContstraints()hoặc layoutSubviews(). updateContstraints()được gọi đầu tiên, sau đó layoutSubviews(). Trong updateConstraints()chắc chắn để gọi siêu cuối cùng . Trong layoutSubviews(), gọi siêu đầu tiên .

Đây là những gì tôi làm:

@IBDesignable
class MyView: UIView {

      convenience init(args: Whatever) {
          self.init(frame: CGRect.zero)
          //assign custom vars
      }

      override init(frame: CGRect) {
           super.init(frame: frame)
           commonInit()
      }

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

      override func prepareForInterfaceBuilder() {
           super.prepareForInterfaceBuilder()
           commonInit()
      }

      private func commonInit() {
           //custom initialization
      }

      override func updateConstraints() {
           //set subview constraints here
           super.updateConstraints()
      }

      override func layoutSubviews() {
           super.layoutSubviews()
           //manually set subview frames here
      }

}

1
Nó không hoạt động: Sử dụng 'self' trong phương thức gọi 'commonInit' trước khi super.init khởi tạo self
surfrider

1
Khởi tạo đối số tùy chỉnh sau cuộc gọi self.init. Cập nhật câu trả lời của tôi.
MH175

1
Nhưng điều gì sẽ xảy ra nếu bạn muốn khởi tạo một số thuộc tính trong commonInitphương thức, nhưng bạn không thể đặt nó sau supertrong trường hợp này vì bạn nên khởi tạo tất cả các thuộc tính TRƯỚC supercuộc gọi. Lol nó có vẻ như vòng lặp chết.
surfrider

1
Đây là cách khởi tạo Swift thường hoạt động: tìm kiếm "khởi tạo hai pha". Bạn có thể sử dụng các tùy chọn hoàn toàn chưa được bao bọc, nhưng tôi khuyên bạn nên chống lại nó. Kiến trúc của bạn, đặc biệt là khi xử lý các khung nhìn, nên khởi tạo tất cả các thuộc tính cục bộ. Tôi đã sử dụng phương thức commonInit () này cho hàng trăm lượt xem. Nó hoạt động
MH175

17

Đây là cách tôi làm trên iOS 9 trong Swift -

import UIKit

class CustomView : UIView {

    init() {
        super.init(frame: UIScreen.mainScreen().bounds);

        //for debug validation
        self.backgroundColor = UIColor.blueColor();
        print("My Custom Init");

        return;
    }

    required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented"); }
}

Đây là một dự án đầy đủ với ví dụ:


2
điều này sẽ sử dụng toàn bộ màn hình cho Chế độ xem
RaptoX

1
Vâng! Nếu quan tâm đến một cuộc phỏng vấn một phần, hãy cho tôi biết và tôi cũng sẽ đăng bài này
J-Dizzle

1
Tôi thích câu trả lời này là tốt nhất, bởi vì có fatalError có nghĩa là tôi không phải đặt bất kỳ mã nào trong init bắt buộc.
Carter Medlin

1
@ J-Dizzle, tôi muốn xem giải pháp cho một phần quan điểm.
Ari Lacenski

không phải câu trả lời của bạn nói ngược lại với câu trả lời được chấp nhận? Ý tôi là bạn đang thực hiện việc tùy chỉnh sau đó super.init, nhưng anh ấy nói rằng nó nên được thực hiện trước super.init...
Honey

11

Đây là cách tôi thực hiện Subview trên iOS trong Swift -

class CustomSubview : UIView {

    init() {
        super.init(frame: UIScreen.mainScreen().bounds);

        let windowHeight : CGFloat = 150;
        let windowWidth  : CGFloat = 360;

        self.backgroundColor = UIColor.whiteColor();
        self.frame = CGRectMake(0, 0, windowWidth, windowHeight);
        self.center = CGPoint(x: UIScreen.mainScreen().bounds.width/2, y: 375);

        //for debug validation
        self.backgroundColor = UIColor.grayColor();
        print("My Custom Init");

        return;
    }

    required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented"); }
}

4
Sử dụng tốt cuộc gọi fatalError (). Tôi đã phải sử dụng các tùy chọn chỉ để im lặng cảnh báo từ một trình khởi tạo thậm chí không được sử dụng. Điều này đóng nó ngay lên! Cảm ơn.
Mike Critchley
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.