Tải một UIView từ ngòi trong Swift


148

Đây là mã Objective-C mà tôi đang sử dụng để tải ngòi cho tùy chỉnh của mình UIView:

-(id)init{

    NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"myXib" owner:self options:nil];
    return [subviewArray objectAtIndex:0];

}

Mã tương đương trong Swift là gì?

Câu trả lời:


172

Giải pháp gốc

  1. Tôi đã tạo một XIB và một lớp có tên là someView (được sử dụng cùng tên để thuận tiện và dễ đọc). Tôi dựa trên cả hai UIView.
  2. Trong XIB, tôi đã thay đổi lớp "Chủ sở hữu tệp" thành Một số Chế độ xem (trong trình kiểm tra danh tính).
  3. Tôi đã tạo một ổ cắm UIView trong someView.swift, liên kết nó với chế độ xem cấp cao nhất trong tệp XIB (đặt tên là "chế độ xem" để thuận tiện). Sau đó tôi đã thêm các cửa hàng khác vào các điều khiển khác trong tệp XIB khi cần.
  4. trong someView.swift, tôi đã tải XIB bên trong trình khởi tạo "init with code". Không cần gán bất cứ thứ gì cho "bản thân". Ngay khi XIB được tải, tất cả các cửa hàng đều được kết nối, bao gồm cả chế độ xem cấp cao nhất. Điều duy nhất còn thiếu, là thêm chế độ xem trên cùng vào cấu trúc phân cấp chế độ xem:

.

class SomeView: UIView {
   required init(coder aDecoder: NSCoder) {
      super.init(coder: aDecoder)
      NSBundle.mainBundle().loadNibNamed("SomeView", owner: self, options: nil)
      self.addSubview(self.view);    // adding the top level view to the view hierarchy
   }
   ...
}

Lưu ý rằng theo cách này tôi có được một lớp tự tải từ ngòi. Sau đó tôi có thể sử dụng someView như một lớp bất cứ khi nào UIView có thể được sử dụng trong dự án (trong trình xây dựng giao diện hoặc lập trình).

Cập nhật - sử dụng cú pháp Swift 3

Tải một xib trong phần mở rộng sau đây được viết dưới dạng phương thức thể hiện, sau đó có thể được sử dụng bởi một trình khởi tạo như cách trên:

extension UIView {

    @discardableResult   // 1
    func fromNib<T : UIView>() -> T? {   // 2
        guard let contentView = Bundle(for: type(of: self)).loadNibNamed(String(describing: type(of: self)), owner: self, options: nil)?.first as? T else {    // 3
            // xib not loaded, or its top view is of the wrong type
            return nil
        }
        self.addSubview(contentView)     // 4
        contentView.translatesAutoresizingMaskIntoConstraints = false   // 5 
        contentView.layoutAttachAll(to: self)   // 6 
        return contentView   // 7
    }
}
  1. Sử dụng giá trị trả lại có thể loại bỏ vì chế độ xem được trả lại hầu như không được người gọi quan tâm khi tất cả các cửa hàng đã được kết nối.
  2. Đây là một phương thức chung trả về một đối tượng tùy chọn có kiểu UIView. Nếu nó không tải được view, nó sẽ trả về nil.
  3. Cố gắng tải một tệp XIB có cùng tên với thể hiện của lớp hiện tại. Nếu thất bại, nil được trả lại.
  4. Thêm chế độ xem cấp cao nhất vào hệ thống phân cấp chế độ xem.
  5. Dòng này giả định rằng chúng tôi đang sử dụng các ràng buộc để bố trí chế độ xem.
  6. Phương pháp này thêm các ràng buộc trên, dưới, dẫn và theo dõi - gắn chế độ xem vào "bản thân" ở tất cả các mặt (Xem: https://stackoverflow.com/a/46279424/2274829 để biết chi tiết)
  7. Trở lại chế độ xem cấp cao nhất

Và phương thức người gọi có thể trông như thế này:

final class SomeView: UIView {   // 1.
   required init?(coder aDecoder: NSCoder) {   // 2 - storyboard initializer
      super.init(coder: aDecoder)
      fromNib()   // 5.
   }
   init() {   // 3 - programmatic initializer
      super.init(frame: CGRect.zero)  // 4.
      fromNib()  // 6.
   }
   // other methods ...
}
  1. Một số lớp là một lớp con UIView tải nội dung của nó từ tệp someClass.xib. Từ khóa "cuối cùng" là tùy chọn.
  2. Trình khởi tạo khi chế độ xem được sử dụng trong bảng phân cảnh (hãy nhớ sử dụng someClass làm lớp tùy chỉnh của chế độ xem bảng phân cảnh của bạn).
  3. Trình khởi tạo khi chế độ xem được tạo theo chương trình (ví dụ: "let myView = someView ()").
  4. Sử dụng khung toàn bộ số 0 do chế độ xem này được bố trí bằng cách sử dụng bố cục tự động. Lưu ý rằng phương thức "init (frame: CGRect) {..}" không được tạo độc lập, vì bố cục tự động được sử dụng riêng trong dự án của chúng tôi.
  5. & 6. Tải tập tin xib bằng phần mở rộng.

Tín dụng: Sử dụng một phần mở rộng chung trong giải pháp này được lấy cảm hứng từ câu trả lời của Robert bên dưới.

Chỉnh sửa Thay đổi "chế độ xem" thành "nội dung xem" để tránh nhầm lẫn. Cũng thay đổi mảng con thành ".first".


7
Đặt tên lớp để File's Ownerđạt điểm ... Cảm ơn!
Aviel Gross 4/2/2015

13
UIView không có chế độ xem thuộc tính, vì vậy việc gọi self.view gây ra lỗi
Nastya Gorban

4
@NastyaGorban self.view trong trường hợp này thực sự đề cập đến thuộc tính ổ cắm (có tên là "view) mà GK100 được liên kết từ chế độ xem cấp cao nhất trong .xib sang someView.swift. Không thêm ổ cắm đó sẽ gây ra lỗi cho bạn vì không có chế độ xem" "thuộc tính trong các lớp NSView như bạn nói.
Ausiàs

3
Tôi đang gặp sự cố khi tải nib (loadNibNamed). Sử dụng Xcode 6.3 và Swift
karthikPrabhu Alagu

11
gọi fromNib()từ bên trong init(coder aDecoder: NSCoder)tạo ra một vòng lặp vô hạn khi tải Nib bên trong fromNib()phương thức thực hiện cuộc gọi tới:init(coder aDecoder: NSCoder)
Matthew Cawley

334

Đóng góp của tôi:

extension UIView {
    class func fromNib<T: UIView>() -> T {
        return Bundle(for: T.self).loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
    }
}

Sau đó gọi nó như thế này:

let myCustomView: CustomView = UIView.fromNib()

.. hoặc thậm chí:

let myCustomView: CustomView = .fromNib()

20
Câu trả lời tốt nhất cho đến nay.
CodyMace

7
Câu trả lời tốt nhất ở đây. Sạch sẽ và đơn giản
Marquavious Draggon

3
@YuchenZhong - Tôi thích [0] hơn .first vì điều đó sẽ trả lại một tùy chọn. Nếu bạn buộc mở khóa, nó sẽ an toàn hơn. ... Và điều này đặt ra câu hỏi: Tại sao không trả lại tùy chọn như một số giải pháp trên? Trả lời: Bạn có thể. Không có gì sai với điều đó. Nhưng ... nếu nó trở lại con số không, tên của xib / class không khớp. Đây là một lỗi của nhà phát triển và cần được bắt gặp ngay lập tức và không bao giờ được đưa vào sản xuất. Ở đây tôi muốn có sự cố ứng dụng hơn là để nó ở trạng thái kỳ lạ. Chỉ cần 2 xu / sở thích của tôi.
Robert Gummesson

1
@allenlinli - Phương thức này là một phần mở rộng tĩnh của UIView như được cho là CustomView. Nó hoạt động vì trình biên dịch xâm nhập kiểu sử dụng chú thích kiểu rõ ràng. Vì CustomView là một lớp con của UIView và loại đã được suy ra, nên chúng ta không cần phải suy luận lại, do đó, UIView có thể được bỏ qua như trong ví dụ thứ hai của tôi. Nói như vậy, rõ ràng bạn cũng có thể thực hiện cuộc gọi theo cách bạn đặt nó xuống.
Robert Gummesson

3
Giải pháp này không phù hợp với tôi trong trường hợp khi có chế độ xem tùy chỉnh bên trong .xib. Tôi sẽ đề nghị sửa phần này thành: return Bundle.main.loadNibNamed (Chuỗi (mô tả: tự), chủ sở hữu: nil, tùy chọn: nil)! [0] như! T
Nadzeya

79

Bây giờ có thể trở lại -> Selftrong swift giúp đơn giản hóa điều này một chút. Xác nhận lần cuối trên Swift 5.

extension UIView {
    class func fromNib(named: String? = nil) -> Self {
        let name = named ?? "\(Self.self)"
        guard
            let nib = Bundle.main.loadNibNamed(name, owner: nil, options: nil)
            else { fatalError("missing expected nib named: \(name)") }
        guard
            /// we're using `first` here because compact map chokes compiler on
            /// optimized release, so you can't use two views in one nib if you wanted to
            /// and are now looking at this
            let view = nib.first as? Self
            else { fatalError("view of type \(Self.self) not found in \(nib)") }
        return view
    }
}

Nếu .xibtệp và lớp con của bạn chia sẻ cùng tên, bạn có thể sử dụng:

let view = CustomView.fromNib()

Nếu bạn có một tên tùy chỉnh, sử dụng:

let view = CustomView.fromNib(named: "special-case")

GHI CHÚ:

Nếu bạn gặp lỗi "không tìm thấy kiểu YourType trong .." thì bạn đã không đặt lớp của chế độ xem trong .xib tệp

Chọn chế độ xem của bạn trong .xibtệp, và nhấn cmd + opt + 4và trong classđầu vào, nhập lớp của bạn


1
Tôi không thể làm cho nó hoạt động theo XCode 7.1 beta 3 - không chắc đó có phải là bản beta hay không nhưng về cơ bản tôi đã thử mọi cách để tạo chế độ xem tùy chỉnh trực tiếp từ ngòi trong Swift và tôi luôn nhận được kết quả tương tự: lớp nó đang tạo không phù hợp với KVC với các cửa hàng. Không chắc chắn nếu đó là điều tôi đang làm sai nhưng lớp học của tôi khá đơn giản và Chủ sở hữu tệp là chính xác. Tôi đã từng làm điều này mọi lúc trong Mục tiêu-C.
Echelon

1
@Logan nó không thực sự liên quan đến mã của bạn, nhưng các chế độ xem tùy chỉnh imo sẽ hỗ trợ tải từ Storyboard / XIB. Nhận xét của tôi chỉ là một thông báo cho những người muốn tạo ra những quan điểm như vậy
Nikita Took

1
Lưu ý Tôi vẫn gặp sự cố khi sử dụng hình thức gọi hàm thứ hai này, cụ thể là let myCustomView = UIView.fromNib() as? CustomView. Trong trường hợp này, T.selfgiải quyết UIViewthay vì CustomViewvà không tìm thấy ngòi. Tôi không chắc tại sao điều này - có thể là loại suy ra cho letphương tiện mà hàm được gọi là a UIView?
Echelon

2
Điều quan trọng là chỉ ra rằng việc cố gắng sử dụng Chủ sở hữu tệp để kết nối các cửa hàng (như chúng ta đã làm trong những ngày xưa tốt đẹp) sẽ khiến điều này bị sập. Trong IB, Chủ sở hữu tệp phải không / trống và các cửa hàng nên được nối với chế độ xem thay thế.
Echelon

1
@Echelon bạn đã cứu ngày của tôi !!! Tôi đã kết nối các cửa hàng của mình bằng Chủ sở hữu tệp và nó không hoạt động, thay vào đó, sử dụng chế độ xem.
Jan Schlorf

19

thử mã sau.

var uiview :UIView?

self.uiview = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)[0] as? UIView

Biên tập:

import UIKit

class TestObject: NSObject {

     var uiview:UIView?

    init()  {
        super.init()
       self.uiview = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)[0] as? UIView
    }


}

Tôi cần gọi phương thức này bên trong phương thức khởi tạo đối tượng là init () trong Swift.
Bagusflyer

12

Phần mở rộng giao thức Swift 4

public protocol NibInstantiatable {

    static func nibName() -> String

}

extension NibInstantiatable {

    static func nibName() -> String {
        return String(describing: self)
    }

}

extension NibInstantiatable where Self: UIView {

    static func fromNib() -> Self {

        let bundle = Bundle(for: self)
        let nib = bundle.loadNibNamed(nibName(), owner: self, options: nil)

        return nib!.first as! Self

    }

}

Con nuôi

class MyView: UIView, NibInstantiatable {

}

Việc triển khai này giả định rằng Nib có cùng tên với lớp UIView. Ví dụ. MyView.xib. Bạn có thể sửa đổi hành vi này bằng cách triển khai nibName () trong MyView để trả về một tên khác với triển khai mở rộng giao thức mặc định.

Trong xib, chủ sở hữu tệp là MyView và lớp xem gốc là MyView.

Sử dụng

let view = MyView.fromNib()

3
Đây là giải pháp đơn giản, thanh lịch nhất và tôi không biết tại sao nó không phải là câu trả lời được chấp nhận!
móng ngựa7

@ horseshoe7 vì nó được viết 4 năm sau câu hỏi.
Freeubi

11

Tôi đã đạt được điều này với Swift bằng mã sau:

class Dialog: UIView {
    @IBOutlet var view:UIView!

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.frame = UIScreen.mainScreen().bounds
        NSBundle.mainBundle().loadNibNamed("Dialog", owner: self, options: nil)
        self.view.frame = UIScreen.mainScreen().bounds
        self.addSubview(self.view)
    }

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

Đừng quên kết nối ổ cắm xem XIB của bạn để xem ổ cắm được xác định trong swift. Bạn cũng có thể đặt Phản hồi đầu tiên cho tên lớp tùy chỉnh của mình để bắt đầu kết nối bất kỳ cửa hàng bổ sung nào.

Hi vọng điêu nay co ich!


10

Đã thử nghiệm trong Xcode 7 beta 4, Swift 2.0 và iOS9 SDK. Đoạn mã sau sẽ gán xib cho uiview. Bạn có thể sử dụng chế độ xem xib tùy chỉnh này trong bảng phân cảnh và cũng truy cập vào đối tượng IBOutlet.

import UIKit

@IBDesignable class SimpleCustomView:UIView
{
    var view:UIView!;

    @IBOutlet weak var lblTitle: UILabel!

   @IBInspectable var lblTitleText : String?
        {
        get{
            return lblTitle.text;
        }
        set(lblTitleText)
        {
            lblTitle.text = lblTitleText!;
        }
    }

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

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        loadViewFromNib ()
    }
    func loadViewFromNib() {
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: "SimpleCustomView", bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
        view.frame = bounds
        view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        self.addSubview(view);



    }


}

Truy cập tùy chỉnh theo chương trình

self.customView =  SimpleCustomView(frame: CGRectMake(100, 100, 200, 200))
        self.view.addSubview(self.customView!);

Mã nguồn - https://github.com/karthikprabhuA/CustomXIBSwift


9

Nếu bạn có nhiều chế độ xem tùy chỉnh trong dự án của mình, bạn có thể tạo lớp như UIViewFromNib

Swift 2.3

class UIViewFromNib: UIView {

    var contentView: UIView!

    var nibName: String {
        return String(self.dynamicType)
    }

    //MARK:
    override init(frame: CGRect) {
        super.init(frame: frame)

        loadViewFromNib()
    }

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

        loadViewFromNib()
    }

    //MARK:
    private func loadViewFromNib() {
        contentView = NSBundle.mainBundle().loadNibNamed(nibName, owner: self, options: nil)[0] as! UIView
        contentView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        contentView.frame = bounds
        addSubview(contentView)
    }
}

Swift 3

class UIViewFromNib: UIView {

    var contentView: UIView!

    var nibName: String {
        return String(describing: type(of: self))
    }

    //MARK:
    override init(frame: CGRect) {
        super.init(frame: frame)

        loadViewFromNib()
    }

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

        loadViewFromNib()
    }

    //MARK:
    func loadViewFromNib() {
        contentView = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)?[0] as! UIView
        contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        contentView.frame = bounds
        addSubview(contentView)
    }
}

Và trong mỗi lớp chỉ được thừa kế từ UIViewFromNib, bạn cũng có thể ghi đè thuộc nibNametính nếu .xibtệp có tên khác nhau:

class MyCustomClass: UIViewFromNib {

}

8

Xây dựng các giải pháp trên.

Điều này sẽ hoạt động trên tất cả các gói dự án và không cần chung chung khi gọi từNib ().

Swift 2

extension UIView {

    public class func fromNib() -> Self {
        return fromNib(nil)
    }

    public class func fromNib(nibName: String?) -> Self {

        func fromNibHelper<T where T : UIView>(nibName: String?) -> T {
            let bundle = NSBundle(forClass: T.self)
            let name = nibName ?? String(T.self)
            return bundle.loadNibNamed(name, owner: nil, options: nil)?.first as? T ?? T()
        }
        return fromNibHelper(nibName)
    }
}

Swift 3

extension UIView {

    public class func fromNib() -> Self {
        return fromNib(nibName: nil)
    }

    public class func fromNib(nibName: String?) -> Self {
        func fromNibHelper<T>(nibName: String?) -> T where T : UIView {
            let bundle = Bundle(for: T.self)
            let name = nibName ?? String(describing: T.self)
            return bundle.loadNibNamed(name, owner: nil, options: nil)?.first as? T ?? T()
        }
        return fromNibHelper(nibName: nibName)
    }
}

Có thể được sử dụng như thế này:

let someView = SomeView.fromNib()

Hoặc như thế này:

let someView = SomeView.fromNib("SomeOtherNibFileName")

6

Swift 4

Đừng quên viết ".first as? CustomView".

if let customView = Bundle.main.loadNibNamed("myXib", owner: self, options: nil)?.first as? CustomView {    
    self.view.addSubview(customView)
    }

Nếu bạn muốn sử dụng bất cứ nơi nào

Giải pháp tốt nhất là câu trả lời của Robert Gummesson .

extension UIView {
    class func fromNib<T: UIView>() -> T {
        return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
    }
}

Sau đó gọi nó như thế này:

let myCustomView: CustomView = UIView.fromNib()

5
let subviewArray = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)
return subviewArray[0]

Nhưng trong init () của Swift, không có giá trị trả về. Tôi quên đề cập đến việc tôi cần gọi loadNibNamed trong quá trình khởi tạo UIView.
Bagusflyer

Bạn có ý nghĩa gì "không có giá trị trả lại"? selfđược ngầm trả lại từ tất cả các initphương thức ...
Grimxn

1
Ý tôi là tôi gọi loadNibNamed bên trong phương thức init. UIView được tải được gán cho chính nó trong ObjC. Nhưng trong nhanh chóng, nó không phải là.
Bagusflyer

5

Một cách hay để làm điều này với Swift là sử dụng enum.

enum Views: String {
    case view1 = "View1" // Change View1 to be the name of your nib
    case view2 = "View2" // Change View2 to be the name of another nib

    func getView() -> UIView? {
        return NSBundle.mainBundle().loadNibNamed(self.rawValue, owner: nil, options: nil).first as? UIView
    }
}

Sau đó, trong mã của bạn, bạn có thể chỉ cần sử dụng:

let view = Views.view1.getView()

2
Lưu ý rằng nếu bạn thực hiện việc này với tệp nib trống hoặc tệp nib không có nút gốc UIView, bạn sẽ gặp sự cố vì bạn không kiểm tra kích thước mảng hoặc phần tử ở vị trí 0.
Matthew Cawley

4

Tôi thích giải pháp này (dựa trên câu trả lời nếu @ GK100):

  1. Tôi đã tạo một XIB và một lớp có tên là someView (được sử dụng cùng tên để thuận tiện và dễ đọc). Tôi dựa trên cả hai UIView.
  2. Trong XIB, tôi đã thay đổi lớp "Chủ sở hữu tệp" thành Một số Chế độ xem (trong trình kiểm tra danh tính).
  3. Tôi đã tạo một ổ cắm UIView trong someView.swift, liên kết nó với chế độ xem cấp cao nhất trong tệp XIB (đặt tên là "chế độ xem" để thuận tiện). Sau đó tôi đã thêm các cửa hàng khác vào các điều khiển khác trong tệp XIB khi cần.
  4. Trong someView.swift, tôi đã tải XIB bên trong inithoặc trình init:frame: CGRectkhởi tạo. Không cần gán bất cứ thứ gì cho "bản thân". Ngay khi XIB được tải, tất cả các cửa hàng đều được kết nối, bao gồm cả chế độ xem cấp cao nhất. Điều duy nhất còn thiếu, là thêm chế độ xem trên cùng vào cấu trúc phân cấp chế độ xem:

    class SomeView: UIView {
      override init(frame: CGRect) {
        super.init(frame: frame)
        NSBundle.mainBundle().loadNibNamed("SomeObject", owner: self, options: nil)
        self.addSubview(self.view);    // adding the top level view to the view hierarchy
      }
    
      required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        NSBundle.mainBundle().loadNibNamed("SomeObject", owner: self, options: nil)
        self.addSubview(self.view);    // adding the top level view to the view hierarchy
      }
    
    
      ...
    }

Tôi thích sử dụng init với khung vì vậy tôi đã kích hoạt nó! một điều cần lưu ý ... thêm self.view.frame = frame nếu bạn muốn chế độ xem khớp với khung bạn vượt qua
Mike E

3

Phiên bản Swift 3 của câu trả lời của Logan

extension UIView {
    public class func fromNib(nibName: String? = nil) -> Self {
        return fromNib(nibName: nibName, type: self)
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T {
        return fromNib(nibName: nibName, type: T.self)!
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T? {
        var view: T?
        let name: String

        if let nibName = nibName {
            name = nibName
        } else {
            name = self.nibName
        }

        if let nibViews = Bundle.main.loadNibNamed(name, owner: nil, options: nil) {
            for nibView in nibViews {
                if let tog = nibView as? T {
                    view = tog
                }
            }
        }

        return view
    }

    public class var nibName: String {
        return "\(self)".components(separatedBy: ".").first ?? ""
    }

    public class var nib: UINib? {
        if let _ = Bundle.main.path(forResource: nibName, ofType: "nib") {
            return UINib(nibName: nibName, bundle: nil)
        } else {
            return nil
        }
    }
}

3

Dưới đây là một cách rõ ràng và khai báo về cách tải chương trình bằng cách sử dụng tiện ích mở rộng giao thức và giao thức (Swift 4.2):

protocol XibLoadable {
    associatedtype CustomViewType
    static func loadFromXib() -> CustomViewType
}

extension XibLoadable where Self: UIView {
    static func loadFromXib() -> Self {
        let nib = UINib(nibName: "\(self)", bundle: Bundle(for: self))
        guard let customView = nib.instantiate(withOwner: self, options: nil).first as? Self else {
            // your app should crash if the xib doesn't exist
            preconditionFailure("Couldn't load xib for view: \(self)")
        }
        return customView
    }
}

Và bạn có thể sử dụng như thế này:

// don't forget you need a xib file too
final class MyView: UIView, XibLoadable { ... }

// and when you want to use it
let viewInstance = MyView.loadFromXib()

Một số cân nhắc bổ sung :

  1. Đảm bảo tệp xib của chế độ xem tùy chỉnh của bạn có bộ của chế độ xem Custom Class(và các đầu ra / hành động được đặt từ đó), chứ không phải của Chủ sở hữu tệp.
  2. Bạn có thể sử dụng giao thức / tiện ích mở rộng bên ngoài này để xem tùy chỉnh hoặc nội bộ của bạn. Bạn có thể muốn sử dụng nó trong nội bộ nếu bạn có một số công việc thiết lập khác khi khởi tạo chế độ xem của mình.
  3. Lớp xem và tệp xib tùy chỉnh của bạn cần phải có cùng tên.

2

Tôi chỉ làm theo cách này:

if let myView = UINib.init(nibName: "MyView", bundle: nil).instantiate(withOwner: self)[0] as? MyView {
// Do something with myView
}

Mẫu này sử dụng chế độ xem đầu tiên trong nib "MyView.xib" trong gói chính. Nhưng bạn có thể thay đổi chỉ mục, tên nib hoặc gói (chính theo mặc định).

Tôi đã từng đánh thức các khung nhìn vào phương thức init hoặc tạo các phương thức chung như trong các giải pháp ở trên (bằng cách này là thông minh), nhưng tôi không làm điều đó nữa.

Bằng cách này, tôi có thể sử dụng các bố cục hoặc đặc điểm khác nhau trong khi vẫn giữ nguyên logic và mã xem.

Tôi thấy dễ dàng hơn khi để một đối tượng nhà máy (thường là viewContoder sẽ sử dụng khung nhìn) tạo ra nó khi nó cần. Đôi khi bạn cần một chủ sở hữu (Thông thường khi chế độ xem được tạo có một ổ cắm được kết nối với người tạo), đôi khi không ..

Đó có lẽ là lý do tại sao Apple không bao gồm một initFromNibphương thức trong lớp UIView của mình ...

Lấy một ví dụ cấp độ mặt đất, bạn không biết bạn sinh ra như thế nào. Bạn vừa được sinh ra. Các quan điểm cũng vậy;)


2

Tất cả bạn phải làm là gọi phương thức init trong UIViewlớp của bạn .

Làm theo cách đó:

class className: UIView {

    @IBOutlet var view: UIView!

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

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

    func setup() {
        UINib(nibName: "nib", bundle: nil).instantiateWithOwner(self, options: nil)
        addSubview(view)
        view.frame = self.bounds
    }
}

Bây giờ, nếu bạn muốn thêm chế độ xem này dưới dạng chế độ xem phụ trong trình điều khiển chế độ xem, hãy thực hiện theo cách đó trong chế độ xem tệp file.swift:

self.view.addSubview(className())

Đó là câu trả lời tuyệt vời nhưng có gì đó không đúng, tôi sẽ chỉnh sửa nó.
C0mrade

Đó là cách tôi thực hiện. Nhưng bạn có thể ứng biến nó. Cảm ơn trước @ C0mrade
Alap Anerao

1

Tương tự như một số câu trả lời ở trên nhưng phần mở rộng Swift3 UIView phù hợp hơn:

extension UIView {
    class func fromNib<A: UIView> (nibName name: String, bundle: Bundle? = nil) -> A? {
        let bundle = bundle ?? Bundle.main
        let nibViews = bundle.loadNibNamed(name, owner: self, options: nil)
        return nibViews?.first as? A
    }

    class func fromNib<T: UIView>() -> T? {
        return fromNib(nibName: String(describing: T.self), bundle: nil)
    }
}

Điều này mang lại sự thuận tiện cho việc có thể tải lớp từ một ngòi tự đặt tên mà còn từ các ngòi / bó khác.


1

Bạn có thể làm điều này thông qua bảng phân cảnh, chỉ cần thêm các ràng buộc thích hợp để xem. Bạn có thể thực hiện việc này một cách dễ dàng bằng cách phân lớp bất kỳ chế độ xem nào từ chính bạn hãy nói BaseView:

Mục tiêu-C

BaseView.h


/*!
 @class BaseView
 @discussion Base View for getting view from xibFile
 @availability ios7 and later
 */
@interface BaseView : UIView

@end


BaseView.m


#import "BaseView.h"

@implementation BaseView

#pragma mark - Public

- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        [self prepareView];
    }
    return self;
}

#pragma mark - LifeCycle

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self prepareView];
    }
    return self;
}

#pragma mark - Private

- (void)prepareView
{
    NSArray *nibsArray = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
    UIView *view = [nibsArray firstObject];

    view.translatesAutoresizingMaskIntoConstraints = NO;
    [self addSubview:view];
    [self addConstraintsForView:view];
}

#pragma mark - Add constraints

- (void)addConstraintsForView:(UIView *)view
{
    [self addConstraints:@[[NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeBottom
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeBottom
                                                       multiplier:1.0
                                                         constant:0],
                           [NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeTop
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeTop
                                                       multiplier:1.0
                                                         constant:0],
                           [NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeLeft
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeLeft
                                                       multiplier:1.0
                                                         constant:0],
                           [NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeRight
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeRight
                                                       multiplier:1.0
                                                         constant:0]
                           ]];
}

@end

Swift 4

import UIKit

class BaseView : UIView {

    // MARK: - LifeCycle

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

        prepareView()
    }

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

        prepareView()
    }

    internal class func xibName() -> String {
        return String(describing: self)
    }

    // MARK: - Private
    fileprivate func prepareView() {
        let nameForXib = BaseView.xibName()
        let nibs = Bundle.main.loadNibNamed(nameForXib, owner: self, options: nil)
        if let view = nibs?.first as? UIView {
            view.backgroundColor = UIColor.clear
            view.translatesAutoresizingMaskIntoConstraints = false
            addSubviewWithConstraints(view, offset: false)
        }
    }
}

UIView+Subview


public extension UIView {
    // MARK: - UIView+Extensions

    public func addSubviewWithConstraints(_ subview:UIView, offset:Bool = true) {
        subview.translatesAutoresizingMaskIntoConstraints = false
        let views = [
            "subview" : subview
        ]
        addSubview(subview)

        var constraints = NSLayoutConstraint.constraints(withVisualFormat: offset ? "H:|-[subview]-|" : "H:|[subview]|", options: [.alignAllLeading, .alignAllTrailing], metrics: nil, views: views)
        constraints.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: offset ? "V:|-[subview]-|" : "V:|[subview]|", options: [.alignAllTop, .alignAllBottom], metrics: nil, views: views))
        NSLayoutConstraint.activate(constraints)
    }
}

Tôi cung cấp 2 biến thể cách thêm các ràng buộc - phổ biến và trong ngôn ngữ định dạng trực quan - chọn bất kỳ biến thể nào bạn muốn :)

Ngoài ra, theo mặc định, xibtên đó có cùng tên với tên lớp thực hiện. Nếu không - chỉ cần thay đổixibName tham số.

Nếu bạn phân lớp chế độ xem của mình từ BaseView- bạn có thể dễ dàng đặt bất kỳ chế độ xem nào và chỉ định lớp trong IB.


1
class func loadFromNib<T: UIView>() -> T {
    let nibName = String(describing: self)
    return Bundle.main.loadNibNamed(nibName, owner: nil, options: nil)![0] as! T
}

0

Phiên bản mạnh mẽ hơn dựa trên câu trả lời của Logan

extension UIView {
    public class func fromNib(nibName: String? = nil) -> Self {
        return fromNib(nibName: nibName, type: self)
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T {
        return fromNib(nibName: nibName, type: T.self)!
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T? {
        var view: T?
        let name: String

        if let nibName = nibName {
            name = nibName
        } else {
            name = self.nibName
        }

        if let nibViews = nibBundle.loadNibNamed(name, owner: nil, options: nil) {
            if nibViews.indices.contains(nibIndex), let tog = nibViews[nibIndex] as? T {
                view = tog
            }
        }

        return view
    }

    public class var nibName: String {
        return "\(self)".components(separatedBy: ".").first ?? ""
    }

    public class var nibIndex: Int {
        return 0
    }

    public class var nibBundle: Bundle {
        return Bundle.main
    }
}

Và bạn có thể sử dụng như

class BaseView: UIView {
    override class var nibName: String { return "BaseView" }
    weak var delegate: StandardStateViewDelegate?
}

class ChildView: BaseView {
    override class var nibIndex: Int { return 1 }
}

0

Việc thực hiện thuận tiện nhất. Ở đây bạn cần hai phương thức, để trả về trực tiếp đối tượng của lớp bạn chứ không phải UIView.

  1. viewId được đánh dấu là một lớp , cho phép ghi đè
  2. .Xib của bạn có thể chứa nhiều hơn một chế độ xem cấp cao nhất, tình huống này cũng được xử lý chính xác.

extension UIView {

class var viewId: String {
    return String(describing: self)
}

static func instance(from bundle: Bundle? = nil, nibName: String? = nil,
                    owner: Any? = nil, options: [AnyHashable : Any]? = nil) -> Self? {

    return instancePrivate(from: bundle ?? Bundle.main,
                           nibName: nibName ?? viewId,
                           owner: owner,
                           options: options)
}

private static func instancePrivate<T: UIView>(from bundle: Bundle, nibName: String,
                                              owner: Any?, options: [AnyHashable : Any]?) -> T? {

    guard
        let views = bundle.loadNibNamed(nibName, owner: owner, options: options),
        let view = views.first(where: { $0 is T }) as? T else { return nil }

    return view
}
}

Thí dụ:

guard let customView = CustomView.instance() else { return }

//Here customView has CustomView class type, not UIView.
print(customView is CustomView) // true

0

Nếu bạn muốn lớp con Swift UIView hoàn toàn khép kín và có khả năng được khởi tạo bằng init hoặc init (frame :) mà không để lộ chi tiết triển khai sử dụng Nib, thì bạn có thể sử dụng tiện ích mở rộng giao thức để đạt được điều này. Giải pháp này tránh được hệ thống phân cấp UIView lồng nhau theo đề xuất của nhiều giải pháp khác.

public class CustomView: UIView {

    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var valueLabel: UILabel!

    public convenience init() {
        self.init(frame: CGRect.zero)
    }

    public override convenience init(frame: CGRect) {
        self.init(internal: nil)
        self.frame = frame
    }

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

    fileprivate func commonInit() {
    }
}

fileprivate protocol _CustomView {
}

extension CustomView: _CustomView {
}

fileprivate extension _CustomView {

    // Protocol extension initializer - has the ability to assign to self, unlike
    // class initializers. Note that the name of this initializer can be anything
    // you like, here we've called it init(internal:)

    init(internal: Int?) {
        self = Bundle.main.loadNibNamed("CustomView", owner:nil, options:nil)![0] as! Self;
    }
}

0
  let bundle = Bundle(for: type(of: self))
   let views = bundle.loadNibNamed("template", owner: self, options: nil)
    self.view.addSubview(views?[0] as! UIView)

Mã chỉ trả lời được khuyến khích. Vui lòng thêm một số giải thích về cách giải quyết vấn đề này hoặc cách giải quyết vấn đề này khác với các câu trả lời hiện có. Từ đánh giá
Nick

0

Tôi thích phần mở rộng dưới đây

extension UIView {
    class var instanceFromNib: Self {
        return Bundle(for: Self.self)
            .loadNibNamed(String(describing: Self.self), owner: nil, options: nil)?.first as! Self
    }
}

Sự khác biệt giữa phần mở rộng được trả lời hàng đầu này là bạn không cần lưu trữ hằng số hoặc biến.

class TitleView: UIView { }

extension UIView {
    class var instanceFromNib: Self {
        return Bundle(for: Self.self)
            .loadNibNamed(String(describing: Self.self), owner: nil, options: nil)?.first as! Self
    }
}

self.navigationItem.titleView = TitleView.instanceFromNib

Phiên bản Xcode nào bạn đang sử dụng? Vui lòng đảm bảo rằng bạn đang sử dụng phiên bản XCode mới nhất. Hoạt động tốt với tôi với XCode 11.5 (phiên bản mới nhất vào ngày).
iCoder

0
    let nibs = Bundle.main.loadNibNamed("YourView", owner: nil, options: nil)
    let shareView = nibs![0] as! ShareView
    self.view.addSubview(shareView)
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.