Swift: Reusable UIView in storyboard and sizing co

2019-03-29 05:49发布

问题:

I'm trying to create a reusable UIView in Swift that I can plug into my Storyboard view controllers. My key issue right now is that the reusable UIView "widget" doesn't fully fit into the UIView box in the storyboard. I followed this tutorial to set up the reusable UIView widget

  1. Created a subclass of UIView and a corresponding .xib -- and connected these:

    import UIKit
    
    class MyWidgetView: UIView {
    
    @IBOutlet var view: UIView!;
    
    required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder);
    
    NSBundle.mainBundle().loadNibNamed("MyWidgetView", owner: self, options: nil);
    self.addSubview(self.view);
    }
    
    }
    
  2. In the XIB, which is the interface file corresponding to the code above, I used UIView with Freeform size under the Simulated Metrics, and Scale to Fill under View mode.

  3. In the main storyboard, I added a UIView block (same rectangular shape) and changed the Class to MyWidgetView

  4. It works, but the components I created in the XIB look squished in the actual app, despite the fact that I used layout constraints in both the XIB and also the main storyboard.

See the screenshot. The pink part isn't supposed to appear, since that is just a color of the UIVIew on the main storyboard that I added to test the sizing. That UIView is actually MyWidgetView (after I changed the class in step 3. So in theory, since MyWidgetView == the UIView on the main storyboard, and that UIView has constraints that make it rectangular in the superview, then why is my widget squished? The blue part below should extend all the way right.

回答1:

The actual view hierarchy loaded from the nib file in your code is added via self.addSubview(self.view). So, the frame of your self.view actually has no relationship with its parent, i.e. MyWidgetView.

You may choose either adding layout constraints through code or just setting its frame after being added as a subview. Personally, I prefer the latter. In my experiment, the following is what works for me. I am using Xcode 6.4, which I think is not the same one as yours.

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

    if let nibsView = NSBundle.mainBundle().loadNibNamed("MyWidgetView", owner: self, options: nil) as? [UIView] {
        let nibRoot = nibsView[0]
        self.addSubview(nibRoot)
        nibRoot.frame = self.bounds
    }
}


回答2:

Alternatively the variable frame can be overridden. This code worked for me when CardImgText was set to files owner for the view.

class CardImgTxt: NSView {

    @IBOutlet var view: NSView!
    override var frame: NSRect{
        didSet{
            view.frame = bounds
        }
    }

    override func drawRect(dirtyRect: NSRect) {
        super.drawRect(dirtyRect)


        // Drawing code here.
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        NSBundle.mainBundle().loadNibNamed("View", owner: self, topLevelObjects: nil)
        addSubview(view)
    }

}

if you are more interested in efficiency than real time updating. Then replace :

    override var frame: NSRect{
        didSet{
            view.frame = bounds
        }
    }

with:

override func viewDidEndLiveResize() {
    view.frame = bounds
}