Reuse a uiview xib in storyboard

前端 未结 7 2172
遇见更好的自我
遇见更好的自我 2020-11-29 15:19

I typically like to create and design my uiviews in interface builder. Sometimes I need to create a single view in a xib that can be reused in multiple view controllers in a

相关标签:
7条回答
  • 2020-11-29 15:31

    Swift 3&4 Update to the accepted answer

    1. Create a new UIView named 'DesignableXibView'

    • File > New > File > Source > Cocoa Touch Class > UIView

    2. Create a matching xib file named 'DesignableXibView'

    • File > New > File > User Interface > View

    3. Set the file owner of the of the xib

    Select the "DesignableXibView.xib" > "File's Owner" > set "Custom Class" to 'DesignableXibView' in the Identity Inspector.

    • Note: Do not set the custom class of the view on the xib. Only the File Owner!

    4. DesignableXibView's Implementation

        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 Test your reusable view in a storyboard

    • Open your storyboard

    • Add a view

    • Set that view's Custom Class

    0 讨论(0)
  • 2020-11-29 15:31

    For anyone trying to adapt accepted answer (by @Garfbargle) to Objective-C

    Just converting Swift to Objective-C isn't enough to make it work. I've had hard time to allow live rendering in Storyboard.

    After translating the whole code, the view is well loaded when running on device (or simulator), but the live rendering in Storyboard doesn't work. The reason for this is that I used [NSBundle mainBundle] whereas Interface Builder hasn't got access to mainBundle. What you have to use instead is [NSBundle bundleForClass:self.classForCoder]. BOOM, live rendering works now !

    Note : if you have having issues with Auto Layout, try disabling Safe Area Layout Guides in the Xib.

    For your convenience, I leave my whole code here, so that you just have to copy/paste (for all process, follow original answer) :

    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
    

    Tell me if you encounter some issues but it should almost work out of the box :)

    0 讨论(0)
  • 2020-11-29 15:32

    The initWithCoder function in swift 2 if anybody is having trouble translating it:

    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]))
        }
    
    0 讨论(0)
  • 2020-11-29 15:34

    If someone is interested, here's the Xamarin.iOS version of the code Step 4 of @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 讨论(0)
  • 2020-11-29 15:47

    Here's the answer you've wanted all along. You can just create your CustomView class, have the master instance of it in a xib with all the subviews and outlets. Then you can apply that class to any instances in your storyboards or other xibs.

    No need to fiddle with File's Owner, or connect outlets to a proxy or modify the xib in a peculiar way, or add an instance of your custom view as a subview of itself.

    Just do this:

    1. Import BFWControls framework
    2. Change your superclass from UIView to NibView (or from UITableViewCell to NibTableViewCell)

    That's it!

    It even works with IBDesignable to render your custom view (including the subviews from the xib) at design time in the storyboard.

    You can read more about it here: https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155

    And you can get the open source BFWControls framework here: https://github.com/BareFeetWare/BFWControls

    Tom

    0 讨论(0)
  • 2020-11-29 15:48

    NEW! updated answer with ability to render directly in the storyboard (and swift!)

    Works in Xcode 6.3.1

    Create a new UIView named 'ReuseableView'

    • File > New > File > Source > Cocoa Touch Class > UIView

    Create a matching xib file named 'ReuseableView'

    • File > New > File > User Interface > View

    Set the file owner of the of the xib

    1. select the xib
    2. select file's owner
    3. set custom class to 'ReusableView' in the Identity Inspector. enter image description here

      • Note: Do not set the custom class of the view on the xib. Only the File Owner!

    Make an outlet from the view in the ReuseableView.xib to your ReuseableView.h interface

    1. Open Assistant Editor
    2. Control + Drag from the view to your interface

    enter image description here

    Add initWithCoder implementation to load view and add as a subview.

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

    enter image description here

    Test your reuseable view in a storyboard

    1. Open your storyboard
    2. Add a view
    3. Set that view's Custom Class

    enter image description here

    Run and observe! enter image description here

    0 讨论(0)
提交回复
热议问题