After updating to Xcode 8.1 our storyboards and xib are as usual. If we create a new xib/view/storyboard we can't see the freshly added elements in the new view.
Both of these are xib files. The left one is created befor the update, the right one after. You cant see the button eventhough that it is on top of everything and has contrains to fill the view. It also has text content and no sized classes.
I know that there are alot questions which explain that this could be sized classes.
why storyboard ui elements not showing on UIViewController in xcode 6?
Storyboard UI Elements not displaying in editor
If we add new elements to it they are directly not visible. Also adding new ViewController to a "old" storyboard it does not show its content if we add elements to it.
What is going on here and how do i solve that?
For example, we have TopView (my CustomView):
TopView.xib, set TopView class in File's Owner
TopView.swift
import UIKit
#IBDesignable
class TopView: UIView {
//MARK:- IB Outlets
var contentView:UIView?
//MARK:- Lifecycle
override func awakeFromNib() {
super.awakeFromNib()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupThisView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setupThisView()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setupThisView()
contentView?.prepareForInterfaceBuilder()
}
//MARK:- Lifecycle methods
private func setupThisView(){
guard let view = loadViewFromNib() else { return }
view.frame = bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(view)
contentView = view
}
func loadViewFromNib() -> UIView? {
let nibName = String(describing: TopView.self)
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: nibName, bundle: bundle)
return nib.instantiate(withOwner: self,options: nil).first as? UIView
}
}
3.Add UIView in Storyboard and set the class of the view as TopView
If the TopView has not yet appeared in Storyboard then (this happens usually):
Enable Editor -> Automatically Refresh Views
Click on Editor -> Refresh All Views
Still?
Clean the project: ⌘ + K
Build the project ⌘ + B
Result in Storyboard:
p.s I just copy this one from my answer here: Custom view (xib) not visible on storyboard
1. Open the Xib/Storyboard
2. Select the file attributes tab (on the far left)
3. click the "opens in" property and change it from Xcode 8 to Xcode 7
Related
I have a custom view and xib. I use this custom view in one of my storyboard's controller views.
I have a use case where I want to be able to hide the custom view (and bring its height to zero). Right now, I set the height in the interface builder and set constraints to the superview's edges:
As you can see, I want its height to be 84 everywhere.
Now here is my custom view's class:
import UIKit
#IBDesignable
class BannerView: UIView {
#IBOutlet var contentView: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
initialize()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialize()
}
override func awakeFromNib() {
super.awakeFromNib()
initialize()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
initialize()
contentView?.prepareForInterfaceBuilder()
}
func initialize() {
guard let view = loadViewFromNib() else { return }
view.frame = self.bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(view)
contentView = view
}
func loadViewFromNib() -> UIView? {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "BannerView", bundle: bundle)
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
func hide() {
// Hide the view and set its height to zero here
}
}
But, now I'm confused... should I also be setting a height constraint on the custom view when I load it into one of my storyboards? Or should its height be 84 everywhere and I shouldn't have to specify it any further?
Also, how would I hide the custom view and set its height to zero in the above hide() function?
There are several ways to do this... here's one.
Give the content of your xib constraints to make its height 84-pts. You haven't shown your xib's layout, but I'll assume you know how to do that.
Then, when you add BannerView to your main view (I'm guessing that's what you're doing), embed it in a Vertical UIStackView with these properties:
Now, when you set bannerView.isHidden = true, the stack view automatically removes it from the height calculations, resulting in the stack view having a height of Zero.
Setting bannerView.isHidden = false will then re-display the banner view along with its height.
As you want the view's height to be 84 everywhere I think you should add a height constraint outlet and set the value 84.
Set the constraint value to 0 to hide the view (I highly suggest not to hide some view by doing this).
I usually use autolayout together with size classes, to optimize the UI for larger screens, like on iPad.
However, sometimes I need a completely different layout for the UI on iPad, still containing the same view controllers, but structured differently.
What is the best strategies in such situation, where autolayout and size classes are not enough? Loading a different storyboard for iPad?
Obviously the goal here is to not introduce any code duplication.
thanks
Loading a different storyboard for iPad? Obviously the goal here is to not introduce any code duplication.
If that's what you want to do, that feature is built-in and expected. You don't need any code at all. Just create your two storyboards and edit the Info.plist so that it has two main storyboard entries that point to them:
The right thing will just happen: on iPad, the second storyboard will load at launch.
I do same thing with size classes on UIViews and NSLayoutConstraints, for an example if i have different views for pad and phone, but i must do same logic for sections, i create two different views in same storyboard and enable/disable views based on size classes
Make Use of 2 Views(xib) with a single class file. The number of connections has to be same in both xibs but the placement of elements can be different for iPad and iPhone.
Here Below is my code to load XIB from a viewcontroller.
class ViewController: UIViewController {
var customView:CustomView?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
loadCustomView()
}
func loadCustomView(){
if let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let window = appDelegate.window {
customView = CustomView(frame: CGRect(x: 0, y: 0, width: window.frame.width, height: window.frame.height))
window.addSubview(customView!)
}
}
}
Then there is the UIView Class
import UIKit
class CustomView: UIView {
#IBOutlet weak var lbl_Title: UILabel!
var view:UIView!
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
setup()
}
func setup() {
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = UIView.AutoresizingMask.flexibleWidth
view.autoresizingMask = UIView.AutoresizingMask.flexibleHeight
addSubview(view)
}
func loadViewFromNib() -> UIView {
if UIDevice.current.userInterfaceIdiom == .phone{
let bundle = Bundle(for:type(of: self))
let nib = UINib(nibName: "CustomView", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}else{
let bundle = Bundle(for:type(of: self))
let nib = UINib(nibName: "CustomViewTab", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}
}
}
You make the outlet connections to both the XIBs from this class and load the views depending on device type.
i did make some custom views to be able to reuse them in different controller .xib files.
I do initialize them with:
var view: UIView!
var nibName = "MyCustomView"
override init(frame: CGRect) {
super.init(frame: frame)
initialize()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialize()
}
private func initialize() {
self.backgroundColor = Theme.colorTransparent
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: self.nibName, bundle: bundle)
self.view = nib.instantiate(withOwner: self, options: nil).first as! UIView
self.view.frame = self.bounds
self.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(self.view)
clearErrors()
}
But everytime i open a controller .xib file which contains at least one custom view, XCode starts recompiling.
Sometimes also the position and size of all views (not only custom) are distorted. I then have to wait for the recompile to finish, before i can click the yellow triangles to fix the misplacements.
This behaviour is very annoying...
Can you help me to avoid the misplacements and the recompiling so that everything runs smooth and fast?
BTW: the custom views do work properly, it's just the waiting for the recompiling and the misplacements, which i was not able to fix
Thank you very much
Try: Editor > Automatically Refresh Views > Uncheck.
This should stop the constant rebuilding, but yes, it appears like just another annoying Xcode bug.
I have a scenario where in I need to create around 10 different prototypes of UITableViewCells. How ever all of these cells have some UI Elements (area marked in black) in common. And there is an area (marked in yellow) which is different for all these prototypes.
Is there a way I can abstract all the common UI Elements like the way it is done for contentView in UITableViewCell?
I tried to create a TableViewCell with all these elements and empty UIView to hold the customizations and planned to programatically load UIView (created in separate xib) into it.
The problem is I am not able to load the UIView into the UITableViewCell without loosing the constraints?
How to load a custom view with constraints into another?
Or Is there a way to create a custom UITableViewCell like the one in IB?
Create LoadableFromXibView subclass
Create a xib file, set the File's owner as your subclass
Drag outlets and design your view as usual
In your cell insert a subview that will be your reusable part of the cell and set its name as the new LoadableFromXibView subclass that you have created.
Source here: https://gist.github.com/DenHeadless/c3d682e7f499113109d6
class LoadableFromXibView: UIView {
var view = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
func xibSetup() {
view = loadViewFromXib()
view.frame = bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
backgroundColor = .clearColor()
addSubview(view)
}
private func loadViewFromXib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: String(self.dynamicType), bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil).first as! UIView
return view
}
}
Is it possible to create reusable stack views on the story board that can be used dynamically to be generated at a later time? Sort of a template/widget/component.
I am aware that I can do this with a class but if I am able to visually generate a set of components that can be re-used at a latter time I may be able to let our designers make changes to storyboards directly.
Yes -- you can do this with any UIView. There are many tutorials for this (e.g. http://onedayitwillmake.com/blog/2013/07/ios-creating-reusable-uiviews-with-storyboard/)
The basic idea is to drag one onto Storyboard or XIB, make a custom class for it, then implement the view's awakeFromNib to load it.
Yes.It is.
Create a empty xib and then add a stack view to it.
Then create a class which extends UIStackView.
class stackView: UIStackView {
var contentView : UIStackView!
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init(coder: NSCoder) {
super.init(coder: coder)
xibSetup() }
func xibSetup() {
contentView = loadViewFromNib()
contentView.frame = bounds
contentView.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
addSubview(contentView)
}
func loadViewFromNib() -> UIStackView! {
let view: UIStackView? = Bundle.main.loadNibNamed("stackView", owner: nil, options: nil)?.first as! UIStackView?
return view
}
Create a viewController.Add a stackView to it.In StackView properties, goto 3rd bar which named as custom class, for class name give stackView class name