Load .xib file from framework - swift

I create a framework with Custom View. When I'm try to load this to simulator, it loads but when there's a bug when load in deferent iPhone simulators.
I set the constraints to 0, but the custom view doesn't load in all view.
framework code:
#IBDesignable
public class SummaryHeaderView: UIView {
/* View */
#IBOutlet public var view: UIView!
public override init(frame: CGRect) {
super.init(frame: frame)
setUpNib()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUpNib()
}
internal func setUpNib() {
view = loadNib()
view.frame = bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(view)
}
internal func loadNib() -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: String(describing: SummaryHeaderView.self), bundle: bundle)
guard let summaryHeaderView = nib.instantiate(withOwner: self, options: nil).first as? UIView else {
return UIView()
}
return summaryHeaderView
}
}
app code:
#IBOutlet weak var myView: UIView!
var summary = SummaryHeaderView()
override func viewDidLoad() {
super.viewDidLoad()
summary = SummaryHeaderView(frame: self.myView.bounds)
myView.addSubview(summary)
}
Result:

One serious problem with your code is this:
override func viewDidLoad() {
super.viewDidLoad()
summary = SummaryHeaderView(frame: self.myView.bounds)
}
You are assuming here that self.myView.bounds is known at the time viewDidLoad runs. That assumption is wrong. viewDidLoad is too soon for that. Therefore your summary header view shows at the wrong size.
A simple solution would be: instead of using absolute frames everywhere, use auto layout constraints everywhere. That way, all your views and subviews will adjust themselves automatically no matter how big the screen turns out to be.
Another way to do this is to wait until the size of the view is known. For example, move your code into viewDidLayoutSubviews. But then you must take extra steps so that you don't add the subview again later, because viewDidLayoutSubviews can be called many times during the app's lifetime.

Related

Subview Has Size Miscalculated on Storyboard

I have the below issue:
This happens on certain screen sizes only. This view is in a nib linked to a separate file listed here:
import Foundation
import UIKit
class FooterActionView : UIView {
#IBOutlet weak var actionButton: UIButton!
let nibName = "FooterActionView"
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
func commonInit() {
guard let view = loadViewFromNib() else { return }
view.frame = self.bounds
self.addSubview(view)
}
func loadViewFromNib() -> UIView? {
let nib = UINib(nibName: nibName, bundle: nil)
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
}
I have confirmed that the constraints on the storyboard are completely correct (and wouldn't result in this miscalculation of the view width).
This view is subclassed from another view controller as follows:
Where can I look to see what's causing this? (Again I'm fairly certain it's not the constraints). I tried googling this but couldn't find anything that seemed to explain it. Any help is greatly appreciated.
You are saying
view.frame = self.bounds
at a time when we do not yet know what self.bounds is. Then the constraints kick in and the bounds of self.bounds change. But the frame of view.frame doesn’t.
A simple solution would be to give your view the ability to resize itself when self.bounds changes:
view.frame = self.bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
Or you could wait until layoutSubviews to set view.frame.

Loading UIView from Bundle only fills screen partially

Hello there.
Using Swift 4, I am attempting to load a Custom UIView with XIB onto a UIViewController.
However, it only seems to fill the screen partially, and I'm not sure why.
I did the following:
The view controller is defined in a UIStoryboard
UIViewController that adds the UIView in the viewDidLoad
The UIView swift file and the XIB are connected via the File Owner property
The XIB file is added into the copy bundle resources
The hot pink background color is set using the Xcode visual editor, its not done in code.
I simulate using the iphone xr, but I get the same issue if I simulate on iPhone 6s
The view controller code is empty, but I've included the relevant part:
// QuestionViewController
class QuestionViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let subview = QuestionView()
self.view.addSubview(subview)
}
}
The UIView is also pretty basic:
class QuestionView: UIView, XibView {
override init(frame: CGRect) {
super.init(frame: frame)
setupXib()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupXib()
}
func setupXib() {
guard let v = loadFromXib() else {
return
}
addSubview(v)
}
}
I use a protocol that I found on stackoverflow to load the xib file from bundle. Originally I had a lot of issues even loading the bundle, but fortuently I was able to rectify this issue. Anyway, my XIB protocol file is here:
// XIB protocol file
protocol XibView {
func setupXib()
func constrainView(_ view: UIView)
func loadFromXib() -> UIView?
}
extension XibView where Self: UIView {
func setupXib() {
if let xibView = loadFromXib() {
addSubview(xibView)
constrainView(xibView)
}
}
func constrainView(_ view: UIView) {
view.translatesAutoresizingMaskIntoConstraints = false
addConstraints(
NSLayoutConstraint.constraints(
withVisualFormat: "V:|[view]|",
options: [.alignAllCenterX, .alignAllCenterY],
metrics: nil,
views: ["view": view]
)
)
addConstraints(
NSLayoutConstraint.constraints(
withVisualFormat: "H:|[view]|",
options: [.alignAllCenterX, .alignAllCenterY],
metrics: nil,
views: ["view": view]
)
)
}
func loadFromXib() -> UIView? {
let xibView = UINib(nibName: String(describing: Self.self), bundle: Bundle(for: type(of: self))).instantiate(withOwner: self, options: nil).first as? UIView
return xibView
}
}
--
Question:
Why does the UIView not fill the entire screen or only fill the screen partially and how can I resolve this?
With thanks
Edit:
The storyboard looks for the UIViewController only has a single view with no content.
I think you should take a UIview(0,0,0,0 four constraints) in Your Viewcontroller and then assign it a custom class which is a subclass of UIView and then load the Xib file and it will surely occupy the whole screen
Try this man::----
class QuestionViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let subview = QuestionView()
subview.frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.width)
self.view.addSubview(subview)
}
}

Swift load nested xib

I am using the xib approach to view controllers and I'm trying to load another xib inside a xib. Is this possible as every documented method I have tried has failed.
I've tried the following: https://medium.com/#brianclouser/swift-3-creating-a-custom-view-from-a-xib-ecdfe5b3a960
And also using the following:
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setup()
view.prepareForInterfaceBuilder()
}
func setup() {
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.addSubview(view)
}
func loadViewFromNib() -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: identifier, bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}
The problem I'm having is the infinite loop scenario, no doubt called from required init. In both approaches files owner has been set/unset as the custom class is unset/set. I've also cleaned the project as well as removed derived data. Just wondered if this was possible?
Thanks

Could not cast value of type UIView to [CustomView] using Xib

I've seen a number of similar questions, but many aren't up-to-date, and none have fixed my issue.
I have a custom Xib and class, CardView. When I try to instantiate it in code, I get Could not cast value of type 'UIView' to 'CardView'.
Class is as follows:
class CardView: NibView {
#IBOutlet var contentView: UIView!
#IBOutlet weak var wordLabel: UILabel!
#IBOutlet weak var flipButton: UIButton!
#IBOutlet weak var playButton: UIButton!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
Bundle.main.loadNibNamed("CardView", owner: self, options: nil)
addSubview(contentView)
contentView.frame = self.bounds
contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
}
override func awakeFromNib() {
super.awakeFromNib()
}
}
and NibView is a custom superclass and looks like:
class NibView: UIView {
var view: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
// Setup view from .xib file
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// Setup view from .xib file
xibSetup()
}
}
private extension NibView {
func xibSetup() {
backgroundColor = UIColor.white
view = loadNib()
// use bounds not frame or it'll be offset
view.frame = bounds
// Adding custom subview on top of our view
addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[childView]|",
options: [],
metrics: nil,
views: ["childView": view]))
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[childView]|",
options: [],
metrics: nil,
views: ["childView": view]))
}
}
extension UIView {
/** Loads instance from nib with the same name. */
func loadNib() -> UIView {
let bundle = Bundle(for: type(of: self))
let nibName = type(of: self).description().components(separatedBy: ".").last!
let nib = UINib(nibName: nibName, bundle: bundle)
return nib.instantiate(withOwner: self, options: nil).first as! UIView
}
}
Here is a screenshot of my file inspector showing that I set the File's Owner custom class to CardView, which seems to be a stumbling block for many
Finally, I try to instantiate the CardView as follows: CardView().loadNib() as! CardView
In case someone still having this problem, I have the same problem and I think we follow same tutorial.
You have called loadNib() in your xibSetup(). I think you don't have to call it again.
So instead of using
let myCardView = CardView().loadNib() as! CardView
I just use
let myCardView = CardView()
In your UIViewController, when you create an instance of your CardView, instead of casting it like CardView().loadNib() as! CardView, you call the function object_setClass(_, _) to avoid that error (from Xcode 9).
So it should be like:
let myCardView = CardView().loadNib
object_setClass(myCardView, CardView.self)
view.addSubview(myCardView)
Old question, but in case anyone's here:
My problem was that the view subclass was fileprivate. IB doesn't like that apparently.
For me loading xib programmatically in this way doesn’t work. I had to remove File owner’s class name and set that to the class name of first view. And then I had to set outlets to the components which I’m going to use.
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
override init(frame: CGRect) {
super.init(frame: frame)
}
Added to custom view class after outlets. For you it's CardView. Then I loaded that view class like
let viewCard = Bundle.main.loadNibNamed("CardView", owner: self, options: nil)?.first as! CardView
view.addSubview(viewCard)

Bad access when loading .xib file

Im having bad access on this line super.init(coder: aDecoder). I've tried many solutions i found online but it does not solves it. Im using Swift2.0
import UIKit
class JobsView: UIView {
// Our custom view from the XIB file
var view: UIView!
func xibSetup() {
view = loadViewFromNib()
// use bounds not frame or it'll be offset
view.frame = bounds
// Make the view stretch with containing view
view.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]
// Adding custom subview on top of our view (over any custom drawing > see note below)
addSubview(view)
}
func loadViewFromNib() -> UIView {
let bundle = NSBundle.mainBundle()
let nib = UINib(nibName: "JobsView", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
override init(frame: CGRect) {
// 1. setup any properties here
// 2. call super.init(frame:)
super.init(frame: frame)
// 3. Setup view from .xib file
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
// 1. setup any properties here
// 2. call super.init(coder:)
super.init(coder: aDecoder)
// 3. Setup view from .xib file
xibSetup()
}
}
Im calling this Nib from this method
func kolodaViewForCardAtIndex(koloda: KolodaView, index: UInt) -> UIView {
//return UIImageView(image: UIImage(named: "Card_like_\(index + 1)"))
return (NSBundle.mainBundle().loadNibNamed("JobsView",owner: self, options: nil)[0] as? UIView)!
}