Tải chế độ xem từ tệp xib bên ngoài trong bảng phân cảnh


131

Tôi muốn sử dụng chế độ xem trong suốt nhiều chế độ xem trong bảng phân cảnh. Vì vậy, tôi đã nghĩ về việc thiết kế khung nhìn trong một xib bên ngoài để những thay đổi được phản ánh trong mỗi bộ điều khiển khung nhìn. Nhưng làm thế nào người ta có thể tải chế độ xem từ xib bên ngoài trong bảng phân cảnh và thậm chí còn có thể? Nếu đó không phải là trường hợp, những lựa chọn thay thế khác có sẵn để phù hợp với tình huống abouve?



Câu trả lời:


132

Ví dụ đầy đủ của tôi là ở đây , nhưng tôi sẽ cung cấp một bản tóm tắt dưới đây.

Bố trí

Thêm một tệp .swift và .xib mỗi tệp có cùng tên vào dự án của bạn. Tệp .xib chứa bố cục chế độ xem tùy chỉnh của bạn (tốt nhất là sử dụng các ràng buộc bố cục tự động).

Tạo tập tin nhanh chóng là chủ sở hữu của tập tin xib.

nhập mô tả hình ảnh ở đây

Thêm mã sau vào tệp .swift và nối các đầu ra và hành động từ tệp .xib.

import UIKit
class ResuableCustomView: UIView {

    let nibName = "ReusableCustomView"
    var contentView: UIView?

    @IBOutlet weak var label: UILabel!
    @IBAction func buttonTap(_ sender: UIButton) {
        label.text = "Hi"
    }

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

        guard let view = loadViewFromNib() else { return }
        view.frame = self.bounds
        self.addSubview(view)
        contentView = view
    }

    func loadViewFromNib() -> UIView? {
        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: nibName, bundle: bundle)
        return nib.instantiate(withOwner: self, options: nil).first as? UIView
    }
}

Sử dụng nó

Sử dụng chế độ xem tùy chỉnh của bạn ở bất cứ đâu trong bảng phân cảnh của bạn. Chỉ cần thêm một UIViewvà đặt tên lớp cho tên lớp tùy chỉnh của bạn.

nhập mô tả hình ảnh ở đây


3
Không phải là loadNibNamed gọi init (coder :) sao? Tôi gặp sự cố khi cố gắng thích nghi với cách tiếp cận của bạn.

@Fishman, nếu bạn cố tải chế độ xem theo chương trình (chứ không phải từ bảng phân cảnh), nó sẽ bị sập vì hiện tại nó không có init(frame:). Xem hướng dẫn này để biết thêm chi tiết.
Suragch

7
Một nguyên nhân phổ biến khác của sự cố là không đặt chế độ xem tùy chỉnh cho chủ sở hữu tệp . Xem vòng tròn màu đỏ trong câu trả lời của tôi.
Suragch

5
Vâng, tôi đã thiết lập lớp của chế độ xem gốc thay vì chủ sở hữu tệp và nó đã gây ra một vòng lặp vô hạn.
devios1

2
Sau khi thêm view.autoresizingMask = [.flexibleWidth, .flexibleHeight] trước self.addSubview (view) nó hoạt động hoàn hảo.
Pramod Tapaniya

69

Trong một thời gian , cách tiếp cận của Christopher Swasey là cách tiếp cận tốt nhất mà tôi đã tìm thấy. Tôi đã hỏi một vài nhà phát triển cấp cao trong đội của tôi về điều đó và một trong số họ đã có giải pháp hoàn hảo ! Nó đáp ứng tất cả các mối quan tâm mà Christopher Swasey đã giải quyết một cách hùng hồn và nó không yêu cầu mã lớp con soạn sẵn (mối quan tâm chính của tôi với cách tiếp cận của anh ta). Có một gotcha , nhưng khác hơn là nó khá trực quan và dễ thực hiện.

  1. Tạo một lớp UIView tùy chỉnh trong tệp .swift để kiểm soát xib của bạn. I EMyCustomClass.swift
  2. Tạo một tệp .xib và tạo kiểu theo ý muốn. I EMyCustomClass.xib
  3. Đặt File's Ownertệp .xib thành lớp tùy chỉnh của bạn ( MyCustomClass)
  4. GOTCHA: để lại classgiá trị (bên dưới identity Inspector) cho chế độ xem tùy chỉnh của bạn trong tệp .xib trống. Vì vậy, chế độ xem tùy chỉnh của bạn sẽ không có lớp được chỉ định, nhưng nó sẽ có Chủ sở hữu tệp được chỉ định.
  5. Kết nối các cửa hàng của bạn như bạn thường sử dụng Assistant Editor.
    • LƯU Ý: Nếu bạn nhìn vào, Connections Inspectorbạn sẽ nhận thấy rằng Cửa hàng tham chiếu của bạn không tham chiếu lớp tùy chỉnh của bạn (nghĩa là MyCustomClass), mà là tham chiếu File's Owner. Vì File's Ownerđược chỉ định là lớp tùy chỉnh của bạn, các cửa hàng sẽ kết nối và hoạt động đúng.
  6. Hãy chắc chắn rằng lớp tùy chỉnh của bạn có @IBDesignable trước câu lệnh của lớp.
  7. Làm cho lớp tùy chỉnh của bạn phù hợp với NibLoadablegiao thức được tham chiếu bên dưới.
    • LƯU Ý: Nếu .swifttên tệp lớp tùy chỉnh của bạn khác với .xibtên tệp của bạn , thì đặt thuộc nibNametính là tên .xibtệp của bạn .
  8. Thực hiện required init?(coder aDecoder: NSCoder)override init(frame: CGRect)gọi setupFromNib()như ví dụ dưới đây.
  9. Thêm một UIView vào bảng phân cảnh mong muốn của bạn và đặt lớp thành tên lớp tùy chỉnh của bạn (nghĩa là MyCustomClass).
  10. Xem IBDesignable hoạt động vì nó thu hút .xib của bạn trong bảng phân cảnh với tất cả sự kinh ngạc và ngạc nhiên.

Đây là giao thức bạn sẽ muốn tham khảo:

public protocol NibLoadable {
    static var nibName: String { get }
}

public extension NibLoadable where Self: UIView {

    public static var nibName: String {
        return String(describing: Self.self) // defaults to the name of the class implementing this protocol.
    }

    public static var nib: UINib {
        let bundle = Bundle(for: Self.self)
        return UINib(nibName: Self.nibName, bundle: bundle)
    }

    func setupFromNib() {
        guard let view = Self.nib.instantiate(withOwner: self, options: nil).first as? UIView else { fatalError("Error loading \(self) from nib") }
        addSubview(view)
        view.translatesAutoresizingMaskIntoConstraints = false
        view.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 0).isActive = true
        view.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
        view.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true
        view.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
    }
}

Và đây là một ví dụ về MyCustomClassviệc thực hiện giao thức (với tệp .xib được đặt tên MyCustomClass.xib):

@IBDesignable
class MyCustomClass: UIView, NibLoadable {

    @IBOutlet weak var myLabel: UILabel!

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

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

}

LƯU Ý: Nếu bạn bỏ lỡ Gotcha và đặt classgiá trị bên trong tệp .xib thành lớp tùy chỉnh của bạn, thì nó sẽ không vẽ trong bảng phân cảnh và bạn sẽ gặp EXC_BAD_ACCESSlỗi khi chạy ứng dụng vì nó bị kẹt trong một vòng lặp vô hạn của cố gắng khởi tạo lớp từ ngòi bằng cách sử dụng init?(coder aDecoder: NSCoder)phương thức sau đó gọi Self.nib.instantiatevà gọi initlại.


2
Đây là một cách tiếp cận tuyệt vời khác, nhưng tôi cảm thấy cách trên vẫn tốt hơn: Medium.com/zenchef-tech-and-product/ Khăn
Ben Patch

4
Cách tiếp cận được bạn đề cập ở trên hoạt động hoàn hảo và áp dụng xem trước trực tiếp ngay trong bảng phân cảnh. Nó hoàn toàn tiện dụng và tuyệt vời, lớn lên!
Igor Leonovich

1
FYI: giải pháp này, sử dụng định nghĩa ràng buộc trong setupFromNib(), dường như khắc phục một số vấn đề bố cục tự động lạ với các ô xem kích thước bảng tự động chứa các khung nhìn được xử lý XIB.
Gary

1
Cho đến nay giải pháp tốt nhất! Tôi @IBDesignablethích sự tương thích. Không thể tin được tại sao Xcode hoặc UIKit không cung cấp một cái gì đó như thế này làm mặc định khi thêm tệp UIView.
fl034

1
Dường như không thể làm cho cái này hoạt động được. Mỗi lần tôi đặt Chủ sở hữu tệp trong tệp .xib của mình, nó cũng đặt lớp tùy chỉnh.
vẽ

32

Giả sử rằng bạn đã tạo một xib mà bạn muốn sử dụng:

1) Tạo một lớp con tùy chỉnh của UIView (bạn có thể đi tới Tệp -> Mới -> Tệp ... -> Lớp cảm ứng ca cao. Hãy đảm bảo "Lớp con của:" là "UIView").

2) Thêm chế độ xem dựa trên xib dưới dạng chế độ xem phụ cho chế độ xem này khi khởi tạo.

Trong Obj-C

-(id)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super initWithCoder:aDecoder]) {
        UIView *xibView = [[[NSBundle mainBundle] loadNibNamed:@"YourXIBFilename"
                                                              owner:self
                                                            options:nil] objectAtIndex:0];
        xibView.frame = self.bounds;
        xibView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        [self addSubview: xibView];
    }
    return self;
}

Trong Swift 2

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    let xibView = NSBundle.mainBundle().loadNibNamed("YourXIBFilename", owner: self, options: nil)[0] as! UIView
    xibView.frame = self.bounds
    xibView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
    self.addSubview(xibView)
}

Trong Swift 3

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    let xibView = Bundle.main.loadNibNamed("YourXIBFilename", owner: self, options: nil)!.first as! UIView
    xibView.frame = self.bounds
    xibView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    self.addSubview(xibView)
}

3) Bất cứ nơi nào bạn muốn sử dụng nó trong bảng phân cảnh của mình, hãy thêm một UIView như bình thường, chọn chế độ xem mới được thêm vào, đi đến Trình kiểm tra danh tính (biểu tượng thứ ba ở phía trên bên phải trông giống như một hình chữ nhật có các dòng trong đó), và nhập tên lớp con của bạn vào dưới dạng "Lớp" trong "Lớp tùy chỉnh".


xibView.frame = self.frame;nên xibView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);, nếu không xibView sẽ có phần bù khi chế độ xem được thêm vào bảng phân cảnh.
BabyPanda

Đến bữa tiệc muộn nhưng có vẻ như anh ấy đã đổi nó thành xibView.frame = self.bound, đó là một khung không có bù
Heavy_Bullets

20
Kết quả trong một vụ tai nạn do đệ quy vô hạn. Tải ngòi tạo một thể hiện khác của lớp con.
David

2
Lớp của khung nhìn xib không được giống với lớp con mới này. Nếu xib là MyClass, bạn có thể tạo lớp MyClassContainer mới này.
dùng1021430

các giải pháp trên sẽ sụp đổ để đệ quy vô hạn.
JBarros35

27

Tôi luôn thấy giải pháp "thêm nó dưới dạng một cuộc phỏng vấn" không đạt yêu cầu, khi thấy nó bắt vít với (1) tự động thanh toán, (2) @IBInspectablevà (3) các cửa hàng. Thay vào đó, hãy để tôi giới thiệu cho bạn về phép thuật của awakeAfter:một NSObjectphương pháp.

awakeAftercho phép bạn trao đổi đối tượng thực sự thức dậy từ NIB / Storyboard với một đối tượng hoàn toàn khác. Đó là sau đó đối tượng được đưa qua quá trình hydrat hóa, đã awakeFromNibđược gọi vào nó, được thêm vào như một cái nhìn, vv

Chúng tôi có thể sử dụng điều này trong một lớp con "cắt bìa cứng" theo quan điểm của chúng tôi, mục đích duy nhất sẽ là tải chế độ xem từ NIB và trả lại để sử dụng trong Bảng phân cảnh. Lớp con có thể nhúng sau đó được chỉ định trong trình kiểm tra danh tính của khung nhìn Storyboard, thay vì lớp gốc. Nó không thực sự phải là một lớp con để nó hoạt động, nhưng làm cho nó trở thành một lớp con là điều cho phép IB thấy bất kỳ thuộc tính IBInutect / IBOutlet nào.

Bản tóm tắt bổ sung này có vẻ như tối ưu, và theo một nghĩa nào đó, bởi vì lý tưởng là UIStoryboardsẽ xử lý một cách liền mạch này nhưng nó có lợi thế là để NIB ban đầu và UIViewlớp con hoàn toàn không bị thay đổi. Vai trò của nó về cơ bản là của một bộ điều hợp hoặc lớp cầu nối, và hoàn toàn hợp lệ, thiết kế khôn ngoan, như một lớp bổ sung, ngay cả khi nó đáng tiếc. Mặt khác, nếu bạn thích phân tích kỹ hơn với các lớp của mình, giải pháp của @ BenPatch hoạt động bằng cách triển khai một giao thức với một số thay đổi nhỏ khác. Câu hỏi về giải pháp nào tốt hơn là giải quyết vấn đề về phong cách lập trình viên: liệu người ta thích thành phần đối tượng hay nhiều kế thừa.

Lưu ý: lớp được đặt trên dạng xem trong tệp NIB vẫn giữ nguyên. Lớp con có thể nhúng chỉ được sử dụng trong bảng phân cảnh. Lớp con không thể được sử dụng để khởi tạo khung nhìn trong mã, vì vậy nó không nên có bất kỳ logic bổ sung nào. Nó chỉ nên chứa awakeAftermóc.

class MyCustomEmbeddableView: MyCustomView {
  override func awakeAfter(using aDecoder: NSCoder) -> Any? {
    return (UIView.instantiateViewFromNib("MyCustomView") as MyCustomView?)! as Any
  }
}

Một nhược điểm đáng kể ở đây là nếu bạn xác định các ràng buộc về chiều rộng, chiều cao hoặc tỷ lệ khung hình trong bảng phân cảnh không liên quan đến chế độ xem khác thì chúng phải được sao chép thủ công. Các ràng buộc liên quan đến hai chế độ xem được cài đặt trên tổ tiên chung gần nhất và các chế độ xem được đánh thức từ bảng phân cảnh từ trong ra ngoài, do đó, tại thời điểm các ràng buộc đó được ngậm nước trên giám sát, việc hoán đổi đã xảy ra. Các ràng buộc chỉ liên quan đến chế độ xem trong câu hỏi được cài đặt trực tiếp trên chế độ xem đó và do đó được đưa ra khi hoán đổi xảy ra trừ khi chúng được sao chép.

Lưu ý rằng những gì đang xảy ra ở đây là các ràng buộc được cài đặt trên chế độ xem trong bảng phân cảnh được sao chép sang chế độ xem mới được khởi tạo , có thể có các ràng buộc của chính nó, được xác định trong tệp nib của nó. Những người không bị ảnh hưởng.

class MyCustomEmbeddableView: MyCustomView {
  override func awakeAfter(using aDecoder: NSCoder) -> Any? {
    let newView = (UIView.instantiateViewFromNib("MyCustomView") as MyCustomView?)!

    for constraint in constraints {
      if constraint.secondItem != nil {
        newView.addConstraint(NSLayoutConstraint(item: newView, attribute: constraint.firstAttribute, relatedBy: constraint.relation, toItem: newView, attribute: constraint.secondAttribute, multiplier: constraint.multiplier, constant: constraint.constant))
      } else {
        newView.addConstraint(NSLayoutConstraint(item: newView, attribute: constraint.firstAttribute, relatedBy: constraint.relation, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: constraint.constant))
      }
    }

    return newView as Any
  }
}  

instantiateViewFromNiblà một phần mở rộng an toàn loại UIView. Tất cả những gì nó làm là lặp qua các đối tượng của NIB cho đến khi tìm thấy một đối tượng phù hợp với loại. Lưu ý rằng loại chung là giá trị trả về , vì vậy loại phải được chỉ định tại trang web cuộc gọi.

extension UIView {
  public class func instantiateViewFromNib<T>(_ nibName: String, inBundle bundle: Bundle = Bundle.main) -> T? {
    if let objects = bundle.loadNibNamed(nibName, owner: nil) {
      for object in objects {
        if let object = object as? T {
          return object
        }
      }
    }

    return nil
  }
}

Đó là tâm trí. Nếu tôi không nhầm, điều này chỉ hoạt động "trên bảng phân cảnh" - nếu bạn cố gắng tạo một lớp mã như vậy trong thời gian chạy, tôi không nghĩ nó hoạt động. Tôi tin.
Fattie

Lớp con nên hoạt động theo mã cũng như lớp gốc cho tất cả ý định và mục đích. Nếu bạn muốn tải chế độ xem từ một ngòi trong mã, bạn chỉ cần khởi tạo trực tiếp nó bằng cách sử dụng kỹ thuật tương tự. Tất cả các lớp con làm là lấy mã để khởi tạo khung nhìn từ ngòi và đặt nó vào một cái móc để bảng phân cảnh sử dụng.
Christopher Swasey

Thật ra tôi đã sai, nó cũng hoạt động tốt nếu bạn có thể khởi tạo nó, nhưng bạn không thể, bởi vì khung nhìn trong NIB sẽ có siêu lớp như kiểu của nó nên instantiateViewFromNibsẽ không trả lại bất cứ thứ gì. IMO cũng không phải là vấn đề lớn, lớp con chỉ là một mục đích để nối vào bảng phân cảnh, tất cả các mã phải nằm trên lớp gốc.
Christopher Swasey

1
Tuyệt quá! Một điều khiến tôi bực mình vì tôi có ít kinh nghiệm với xibs (tôi chỉ từng làm việc với bảng phân cảnh và cách tiếp cận theo chương trình), để nó ở đây trong trường hợp nó giúp được ai đó: trong tệp .xib, bạn cần chọn chế độ xem cấp cao nhất và đặt loại lớp của nó để MyCustomView. Trong xib của tôi, thanh bên trong bên trái bị thiếu theo mặc định; để bật nó lên, có một nút bên cạnh điều khiển tính năng "Xem dưới dạng: iPhone 7" ở gần cạnh dưới / bên trái.
xaphod

5
Nó phanh hạn chế khi nó được thay thế bởi một đối tượng khác. :(
invoodoo

6

Giải pháp tốt nhất hiện nay là chỉ sử dụng bộ điều khiển chế độ xem tùy chỉnh với chế độ xem được xác định trong xib và chỉ cần xóa thuộc tính "chế độ xem" mà Xcode tạo bên trong bảng phân cảnh khi thêm bộ điều khiển chế độ xem vào nó (đừng quên đặt tên của các lớp tùy chỉnh mặc dù).

Điều này sẽ làm cho bộ thực thi tự động tìm kiếm xib và tải nó. Bạn có thể sử dụng thủ thuật này cho bất kỳ loại chế độ xem container hoặc chế độ xem nội dung nào.


5

Tôi nghĩ về alternativeviệc sử dụng XIB viewsđể được sử dụng View Controller trong bảng phân cảnh riêng biệt .

Sau đó, trong kịch bản chính thay cho giao diện tùy chỉnh sử dụng container viewvới Embed Seguevà có StoryboardReferencetới này xem điều khiển tùy chỉnh mà xem nên được đặt bên trong cái nhìn khác trong kịch bản chính.

Sau đó, chúng ta có thể thiết lập phân quyền và liên lạc giữa ViewContoder nhúng này và bộ điều khiển khung nhìn chính thông qua việc chuẩn bị cho segue . Cách tiếp cận này khác với cách hiển thị UIView, nhưng đơn giản và hiệu quả hơn nhiều (từ góc độ lập trình) có thể được sử dụng để đạt được cùng một mục tiêu, tức là có chế độ xem tùy chỉnh có thể sử dụng lại có thể nhìn thấy trong bảng phân cảnh chính

Ưu điểm bổ sung là bạn có thể triển khai logic của mình trong lớp CustomViewContaptor và ở đó thiết lập tất cả sự phân chia và chuẩn bị xem mà không tạo các lớp điều khiển riêng (khó tìm trong dự án) và không đặt mã soạn sẵn trong UIViewContoder chính bằng Thành phần. Tôi nghĩ rằng điều này là tốt cho các thành phần tái sử dụng ex. Thành phần Music Player (tiện ích như) có thể nhúng trong các chế độ xem khác.


Thật đáng buồn là giải pháp đơn giản hơn nhiều so với sử dụng chế độ xem tùy chỉnh xib :(
Wilson

1
sau khi đi theo vòng tròn với chuỗi tạo vòng đời xib của apple trên UIView tùy chỉnh, đây là cách tôi đã kết thúc.
LightningStryk

Trong trường hợp nếu bạn muốn thêm chế độ xem tùy chỉnh một cách linh hoạt, chúng tôi vẫn phải sử dụng bộ điều khiển chế độ xem riêng biệt (tốt nhất là XIB)
Satyam

5

Mặc dù các câu trả lời phổ biến nhất hàng đầu hoạt động tốt, nhưng chúng sai về mặt khái niệm. Tất cả đều sử dụng File's ownernhư kết nối giữa các cửa hàng của lớp và các thành phần UI. File's ownerđược cho là chỉ được sử dụng cho các đối tượng cấp cao nhất không phải UIViewlà s. Kiểm tra tài liệu của nhà phát triển Apple . Có UIView File's ownerdẫn đến những hậu quả không mong muốn này.

  1. Bạn buộc phải sử dụng contentViewnơi bạn phải sử dụng self. Nó không chỉ xấu, mà còn sai về cấu trúc vì chế độ xem trung gian giữ cấu trúc dữ liệu truyền tải cấu trúc UI của nó. Nó giống như đối diện với UI khai báo.
  2. Bạn chỉ có thể có một UIView mỗi Xib. Một Xib được cho là có nhiều UIView.

Có cách thanh lịch để làm điều đó mà không cần sử dụng File's owner. Vui lòng kiểm tra bài blog này . Nó giải thích làm thế nào để làm điều đó đúng cách.


điều đó không quan trọng chút nào bạn không giải thích tại sao nó xấu. Tôi không thể hiểu tại sao. Không có tác dụng phụ.
JBarros35

1

Đây là câu trả lời bạn muốn tất cả cùng. Bạn chỉ có thể tạo CustomViewlớp của mình , có phiên bản chính của nó trong xib với tất cả các mục phỏng vấn và cửa hàng. Sau đó, bạn có thể áp dụng lớp đó cho bất kỳ trường hợp nào trong bảng phân cảnh của bạn hoặc các xibs khác.

Không cần phải nghịch ngợm với Chủ sở hữu tệp hoặc kết nối các cửa hàng với proxy hoặc sửa đổi xib theo cách đặc biệt hoặc thêm một phiên bản của chế độ xem tùy chỉnh của bạn dưới dạng một khung nhìn phụ của chính nó.

Chỉ cần làm điều này:

  1. Nhập khung BFWControls
  2. Thay đổi lớp cha của bạn từ UIViewđến NibView(hoặc từ UITableViewCellphải NibTableViewCell)

Đó là nó!

Nó thậm chí còn hoạt động với IBDesignable để giới thiệu chế độ xem tùy chỉnh của bạn (bao gồm cả các cuộc phỏng vấn từ xib) tại thời điểm thiết kế trong bảng phân cảnh.

Bạn có thể đọc thêm về nó ở đây: https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155

Và bạn có thể lấy khung BFWControls mã nguồn mở tại đây: https://github.com/BareFeetWare/BFWControls

Và đây là một trích xuất NibReplaceablemã đơn giản để điều khiển nó, trong trường hợp bạn tò mò:
 https://gist.github.com/barefeettom/f48f6569100415e0ef1fd530ca39f5b4

Tom


Giải pháp tiện lợi, cảm ơn!
avee

Tôi không muốn cài đặt một khung công tác khi nó cần được cung cấp nguyên bản.
JBarros35

0

Giải pháp này có thể được sử dụng ngay cả khi lớp của bạn không có cùng tên với XIB. Ví dụ: nếu bạn có một trình điều khiển lớp trình điều khiển cơ sởA có trình điều khiển tên XIBA.xib và bạn đã phân lớp này với trình điều khiểnB và muốn tạo một phiên bản của trình điều khiểnB trong bảng phân cảnh, thì bạn có thể:

  • tạo bộ điều khiển xem trong bảng phân cảnh
  • đặt lớp của bộ điều khiển thành bộ điều khiểnB
  • xóa chế độ xem của bộ điều khiểnB trong bảng phân cảnh
  • ghi đè chế độ xem tải trong bộ điều khiểnA để:

*

- (void) loadView    
{
        //according to the documentation, if a nibName was passed in initWithNibName or
        //this controller was created from a storyboard (and the controller has a view), then nibname will be set
        //else it will be nil
        if (self.nibName)
        {
            //a nib was specified, respect that
            [super loadView];
        }
        else
        {
            //if no nib name, first try a nib which would have the same name as the class
            //if that fails, force to load from the base class nib
            //this is convenient for including a subclass of this controller
            //in a storyboard
            NSString *className = NSStringFromClass([self class]);
            NSString *pathToNIB = [[NSBundle bundleForClass:[self class]] pathForResource: className ofType:@"nib"];
            UINib *nib ;
            if (pathToNIB)
            {
                nib = [UINib nibWithNibName: className bundle: [NSBundle bundleForClass:[self class]]];
            }
            else
            {
                //force to load from nib so that all subclass will have the correct xib
                //this is convenient for including a subclass
                //in a storyboard
                nib = [UINib nibWithNibName: @"baseControllerXIB" bundle:[NSBundle bundleForClass:[self class]]];
            }

            self.view = [[nib instantiateWithOwner:self options:nil] objectAtIndex:0];
       }
}

0

Giải pháp cho Objective-C theo các bước được mô tả trong phản hồi của Ben Patch .

Sử dụng tiện ích mở rộng cho UIView:

@implementation UIView (NibLoadable)

- (UIView*)loadFromNib
{
    UIView *xibView = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil] firstObject];
    xibView.translatesAutoresizingMaskIntoConstraints = NO;
    [self addSubview:xibView];
    [xibView.topAnchor constraintEqualToAnchor:self.topAnchor].active = YES;
    [xibView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor].active = YES;
    [xibView.leftAnchor constraintEqualToAnchor:self.leftAnchor].active = YES;
    [xibView.rightAnchor constraintEqualToAnchor:self.rightAnchor].active = YES;
    return xibView;
}

@end

Tạo tập tin MyView.h, MyView.mMyView.xib.

Đầu tiên chuẩn bị của bạn MyView.xibnhư phản ứng Bến Patch nói như vậy bộ lớp MyViewcho chủ sở hữu của tập tin thay vì giao diện chính bên trong XIB này.

MyView.h:

#import <UIKit/UIKit.h>

IB_DESIGNABLE @interface MyView : UIView

@property (nonatomic, weak) IBOutlet UIView* someSubview;

@end

MyView.m:

#import "MyView.h"
#import "UIView+NibLoadable.h"

@implementation MyView

#pragma mark - Initializers

- (id)init
{
    self = [super init];
    if (self) {
        [self loadFromNib];
        [self internalInit];
    }
    return self;
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self loadFromNib];
        [self internalInit];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self loadFromNib];
    }
    return self;
}

- (void)awakeFromNib
{
    [super awakeFromNib];
    [self internalInit];
}

- (void)internalInit
{
    // Custom initialization.
}

@end

Và sau này chỉ cần tạo chế độ xem của bạn theo chương trình:

MyView* view = [[MyView alloc] init];

Cảnh báo! Bản xem trước của chế độ xem này sẽ không được hiển thị trong Storyboard nếu bạn sử dụng Tiện ích mở rộng WatchKit vì lỗi này trong Xcode> = 9.2: https://forums.developer.apple.com/thread/95616


Nên đã cung cấp phiên bản Swift. Rất ít người có thể sử dụng ObjC vào thời điểm này
Satyam
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.