Sử dụng lại xib uiview trong bảng phân cảnh


81

Tôi thường thích tạo và thiết kế uiview của mình trong trình tạo giao diện. Đôi khi tôi cần tạo một chế độ xem duy nhất trong xib có thể được sử dụng lại trong nhiều bộ điều khiển chế độ xem trong bảng phân cảnh.

Câu trả lời:


132

Sử dụng lại và hiển thị xib trong bảng phân cảnh.

Đã thử nghiệm với Swift 2.2 & Xcode 7.3.1

1 ---- Tạo một UIView mới có tên 'DesignableXibView'

  • Tệp> Mới> Tệp> Nguồn> Lớp cảm ứng ca cao> UIView

2 ---- Tạo tệp xib phù hợp có tên 'DesignableXibView'

  • Tệp> Mới> Tệp> Giao diện Người dùng> Xem

3 ---- Đặt chủ sở hữu tệp của xib

  1. chọn xib
  2. chọn chủ sở hữu của tệp
  3. đặt lớp tùy chỉnh thành 'DesignableXibView' trong Trình kiểm tra danh tính.

Đặt chủ sở hữu của Xib thành một lớp tùy chỉnh

  • Lưu ý: Không đặt lớp tùy chỉnh của chế độ xem trên xib. Chỉ chủ sở hữu tệp!

4 ---- Triển khai DesignableXibView

//  DesignableXibView.swift

import UIKit

@IBDesignable

class DesignableXibView: UIView {

    var contentView : UIView?

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

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

    func xibSetup() {
        contentView = loadViewFromNib()

        // use bounds not frame or it'll be offset
        contentView!.frame = bounds

        // Make the view stretch with containing view
        contentView!.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]

        // Adding custom subview on top of our view (over any custom drawing > see note below)
        addSubview(contentView!)
    }

    func loadViewFromNib() -> UIView! {

        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: String(self.dynamicType), bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView

        return view
    }

}

5 ---- Kiểm tra chế độ xem có thể sử dụng lại của bạn trong bảng phân cảnh

  1. Mở bảng phân cảnh của bạn
  2. Thêm chế độ xem
  3. Đặt Lớp tùy chỉnh của chế độ xem đó
  4. đợi một chút ... BOOM !!

Xib Rendering Bên trong Bộ điều khiển Chế độ xem Bảng phân cảnh


1
Cảm ơn! Bạn có thể muốn thêm một ví dụ bằng cách sử dụng các ràng buộc với Swift, như bạn đã làm trong câu trả lời obj-C của mình (có và không có VFL).
Evan R

3
Tôi đã trả lời câu hỏi của riêng mình, có vẻ như tôi đã thiết lập các cửa hàng TRƯỚC KHI tôi đọc các bài báo SO này nói rằng đặt Chủ sở hữu tệp, không phải chế độ xem. Nó hoạt động sau khi tôi ngắt kết nối tất cả các cửa hàng, đảm bảo chủ sở hữu tệp là chính xác và sau đó thêm lại tất cả các cửa hàng.
SuperDuperTango

4
Sự khác biệt giữa thiết lập chủ sở hữu tệp và lớp tùy chỉnh là gì?
Paul Brewczynski

9
Tôi cũng không thấy bản xem trước trong trình tạo giao diện. Xcode 9
Jaap Weijland

3
Mã hoạt động khi chạy nhưng trong Xcode 9 của tôi, nó hiển thị 'Xây dựng có thể thiết kế không thành công' dưới dòng mô-đun
ManuQiao

76

MỚI! câu trả lời được cập nhật với khả năng hiển thị trực tiếp trong bảng phân cảnh (và nhanh chóng!)

Hoạt động trong Xcode 6.3.1

Tạo một UIView mới có tên là 'ReusableView'

  • Tệp> Mới> Tệp> Nguồn> Lớp cảm ứng ca cao> UIView

Tạo một tệp xib phù hợp có tên là 'ReusableView'

  • Tệp> Mới> Tệp> Giao diện Người dùng> Xem

Đặt chủ sở hữu tệp của xib

  1. chọn xib
  2. chọn chủ sở hữu của tệp
  3. đặt lớp tùy chỉnh thành 'ReusableView' trong Trình kiểm tra danh tính. nhập mô tả hình ảnh ở đây

    • Lưu ý: Không đặt lớp tùy chỉnh của chế độ xem trên xib. Chỉ chủ sở hữu tệp!

Tạo lối thoát từ chế độ xem trong ReusableView.xib sang giao diện ReusableView.h của bạn

  1. Mở Trình chỉnh sửa Trợ lý
  2. Control + Kéo từ chế độ xem vào giao diện của bạn

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

Thêm triển khai initWithCoder để tải chế độ xem và thêm làm chế độ xem phụ.

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

        // 1. load the interface
        [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
        // 2. add as subview
        [self addSubview:self.view];
        // 3. allow for autolayout
        self.view.translatesAutoresizingMaskIntoConstraints = NO;
        // 4. add constraints to span entire view
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:0 metrics:nil views:@{@"view":self.view}]];
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:0 metrics:nil views:@{@"view":self.view}]];

    }
    return self;
}

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

Kiểm tra chế độ xem có thể sử dụng lại của bạn trong bảng phân cảnh

  1. Mở bảng phân cảnh của bạn
  2. Thêm chế độ xem
  3. Đặt Lớp tùy chỉnh của chế độ xem đó

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

Chạy và quan sát! nhập mô tả hình ảnh ở đây


@Garfbargle Thêm vào đầu trang này ... có thể hiển thị trong bảng phân cảnh các thành phần phụ của xib không? Trong bước cuối cùng của bạn, chế độ xem đã thêm xuất hiện giống như một hình vuông màu xám, có cách nào để khắc phục không? Cám ơn!
Andres C

2
@ andres.cianio Có tuy nhiên bạn phải thực hiện một cách tiếp cận hoàn toàn khác. Bạn phải tạo chế độ xem theo chương trình và sử dụng lệnh IBDesignable. Điều này đặc biệt dành cho những người muốn xây dựng chế độ xem một cách trực quan. Tôi không biết cách xây dựng chế độ xem trong xib một cách trực quan và để nó được hiển thị trong bảng phân cảnh. Tôi sẽ không ngạc nhiên nếu điều này sẽ có thể xảy ra rất sớm mặc dù nếu nó chưa phải đã.
Garfbargle

@Garfbargle cảm ơn ... Điều này đã cứu mạng tôi, nhưng để nó hiển thị trong SB khi chèn chế độ xem sẽ rất đẹp;)
Andres C

2
Lời giải thích tuyệt vời! Nhưng tôi có một gợi ý cho bạn: Sử dụng UINib(nibName: nibName, bundle: nil).instantiateWithOwner(nil, options: nil)phiên bản này nhanh hơn Phiên bản NSBundle.
blackjacx,

@Garfbargle Điều này dường như đáp ứng yêu cầu mà bạn có thể thiết kế trong IB và hiển thị nó trong bảng phân cảnh. Về cơ bản, bạn làm chính xác những gì bạn đã làm cho XIB và đặt @IBDesignable on the class.Bạn cũng phải triển khai init(frame:)- nhưng khác với việc nó hoạt động khá tốt! supereasyapps.com/blog/2014/12/15/…
wyu

53

Cập nhật Swift 3 & 4 cho câu trả lời được chấp nhận

1. Tạo một UIView mới có tên 'DesignableXibView'

  • Tệp> Mới> Tệp> Nguồn> Lớp cảm ứng ca cao> UIView

2. Tạo tệp xib phù hợp có tên 'DesignableXibView'

  • Tệp> Mới> Tệp> Giao diện Người dùng> Xem

3. Đặt chủ sở hữu tệp của xib

Chọn "DesignableXibView.xib"> "Chủ sở hữu tệp"> đặt "Lớp tùy chỉnh" thành 'DesignableXibView' trong Trình kiểm tra danh tính.

  • Lưu ý: Không đặt lớp tùy chỉnh của chế độ xem trên xib. Chỉ chủ sở hữu tệp!

4. Triển khai DesignableXibView

    import UIKit

@IBDesignable

class DesignableXibView: UIView {

    var contentView : UIView!

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

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

    func xibSetup() {
        contentView = loadViewFromNib()

        // use bounds not frame or it'll be offset
        contentView.frame = bounds

        // Make the view stretch with containing view
        contentView.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]

        // Adding custom subview on top of our view 
        addSubview(contentView)
    }

    func loadViewFromNib() -> UIView! {

        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
        let view = nib.instantiate(withOwner: self, options: nil).first as! UIView

        return view
    }
} 

5 Kiểm tra chế độ xem có thể sử dụng lại của bạn trong bảng phân cảnh

  • Mở bảng phân cảnh của bạn

  • Thêm chế độ xem

  • Đặt Lớp tùy chỉnh của chế độ xem đó


2
Tôi không thể làm cho nó hoạt động trong Xcode8 / Swift3. Hoạt động tốt khi chạy ứng dụng nhưng không hiển thị trong bảng phân cảnh.
cạnThought

Hoạt động tốt cho tôi. Nếu bạn đang cố gắng xem các thay đổi trên xib, nó sẽ không hiển thị. Thêm một dạng xem bên trong bộ điều khiển khác và sau đó đặt lớp của dạng xem đó thành DesignableXibView. Xây dựng dự án để xem các thay đổi.
harsh_v

1
Làm việc cho tôi quá. Chỉ cần để thêm DesignableXibView vào bảng phân cảnh hiện tại của tôi như được mô tả trong Bước 5 Garfbargle bài
Tinkerbell

Chỉ tôi nhận được thông báo "Không thể tải NIB trong gói" tại thời điểm chạy?
Jonny

1
@Jonny Tôi nghĩ rằng String(describing: type(of: self))nên làm điều này một cách an toàn
harsh_v

11

Hàm initWithCoder trong swift 2 nếu ai đó gặp sự cố khi dịch nó:

required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        UINib(nibName: String(self.dynamicType), bundle: NSBundle.mainBundle()).instantiateWithOwner(self, options: nil)
        self.addSubview(view)
        self.view.translatesAutoresizingMaskIntoConstraints = false
        self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[view]|", options: NSLayoutFormatOptions.AlignAllCenterY , metrics: nil, views: ["view": self.view]))
        self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[view]|", options: NSLayoutFormatOptions.AlignAllCenterX , metrics: nil, views: ["view": self.view]))
    }

Cảm ơn! Bạn có thể muốn thêm một ví dụ không sử dụng VFL, cho những người không muốn sử dụng nó.
Evan R

3

Đối với bất kỳ ai đang cố gắng điều chỉnh câu trả lời được chấp nhận (của @Garfbargle) thành Objective-C

Chỉ chuyển đổi Swiftthành Objective-Ckhông đủ để làm cho nó hoạt động. Tôi đã gặp khó khăn khi cho phép hiển thị trực tiếp trong Storyboard.

Sau khi dịch toàn bộ mã, chế độ xem được tải tốt khi chạy trên thiết bị (hoặc trình mô phỏng), nhưng hiển thị trực tiếp trong Storyboard không hoạt động. Lý do cho điều này là tôi đã sử dụng [NSBundle mainBundle]trong khi Trình tạo giao diện không có quyền truy cập vào mainBundle. Những gì bạn phải sử dụng thay thế là [NSBundle bundleForClass:self.classForCoder]. BOOM, kết xuất trực tiếp hoạt động ngay bây giờ!

Lưu ý: nếu bạn gặp sự cố với Bố cục Tự động, hãy thử tắt Safe Area Layout Guidestrong Xib.

Để thuận tiện cho bạn, tôi để lại toàn bộ mã của tôi ở đây, để bạn chỉ cần sao chép / dán (đối với tất cả quá trình, hãy làm theo câu trả lời gốc ):

BottomBarView.h

#import <UIKit/UIKit.h>

IB_DESIGNABLE
@interface BottomBarView : UIView

@end

BottomBarView.m

#import "BottomBarView.h"

@interface BottomBarView() {
    UIView *contentView;
}

@end


@implementation BottomBarView

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

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

-(void) xibSetup {
    contentView = [self loadViewFromNib];

    contentView.frame = self.bounds;

    contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

    [self addSubview:contentView];
}

-(UIView*) loadViewFromNib {
    NSBundle *bundle = [NSBundle bundleForClass:self.classForCoder]; //this is the important line for view to render in IB
    UINib *nib = [UINib nibWithNibName:NSStringFromClass([self class]) bundle:bundle];
    UIView *view = [nib instantiateWithOwner:self options:nil][0];

    return view;
}

@end

Hãy cho tôi biết nếu bạn gặp phải một số vấn đề nhưng nó gần như sẽ hoạt động tốt :)


0

Nếu ai đó quan tâm, đây là phiên bản Xamarin.iOS của mã Bước 4 của @Garfbargle

public partial class CustomView : UIView
    {
        public ErrorView(IntPtr handle) : base(handle)
        {

        }

        [Export("awakeFromNib")]
        public override void AwakeFromNib()
        {
            var nibObjects = NSBundle.MainBundle.LoadNib("CustomView", this, null);
            var view = (UIView)Runtime.GetNSObject(nibObjects.ValueAt(0));
            view.Frame = Bounds;
            view.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;

            AddSubview(rootView);
        }
    }

0

Đây là câu trả lời mà bạn mong muốn. Bạn chỉ có thể tạo CustomViewlớp của mình , có phiên bản chính của nó trong một xib với tất cả các chế độ xem phụ 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 mình hoặc các xib khác.

Không cần phải loay hoay 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 làm chế độ xem 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ừ UIViewthành NibView(hoặc từ UITableViewCellthành NibTableViewCell)

Đó là nó!

Nó thậm chí hoạt động với IBDesignable để hiển thị chế độ xem tùy chỉnh của bạn (bao gồm các chế độ xem phụ 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ó tại đây: https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155

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

Tom 👣

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.