I have two Swift files, one is called FileView.swift and the other is a FileViewController.swift. Using MVC Architecture, I am trying to add a subview from FileViewController.swift to FileView.swift's webViewContainer: UIView! which is an IBOutlet from a XIB File.
However, I am getting a nil result when it is called from FileViewController.swift.
class FileView: UIView {
@IBOutlet weak var webViewContainerHeight: NSLayoutConstraint!
@IBOutlet weak var webViewContainerWidth: NSLayoutConstraint!
@IBOutlet weak var closeBttnWidth: NSLayoutConstraint!
@IBOutlet weak var closeBttnHeight: NSLayoutConstraint!
@IBOutlet weak var webViewContainer: UIView!
@IBOutlet weak var closeButton: UIButton!
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setupNIB()
}
private func setup() {
webViewContainer = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 350))
}
class FileViewController: UIViewController, WKNavigationDelegate {
var webView = WKWebView()
var campaignUrl = ""
var finalCampaignUrl = ""
lazy var customView: FileView = {
let customView = FileView()
return customView
}()
override func loadView() {
self.view = self.customView
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
webViewModal()
}
func webViewModal () {
if UIDevice.current.userInterfaceIdiom == .pad {
let deviceWidth = UIScreen.main.bounds.width/3
let deviceHeight = UIScreen.main.bounds.height/3
customView.webViewContainerWidth.constant = 290 + deviceWidth
customView.webViewContainerHeight.constant = 475 + deviceHeight
customView.closeBttnWidth.constant = 55
customView.closeBttnHeight.constant = 55
customView.closeButton.layoutIfNeeded()
customView.webViewContainer.layoutIfNeeded()
}
webView = Global.setWKWebViewInitWithConfig(frame: .zero)
customView.webViewContainer.addSubview(webView)
customView.webViewContainer.showSpinner(CGSize(width: 30 , height: 30), tintColor: UIColor.lightGray)
webView.translatesAutoresizingMaskIntoConstraints = false
let webViewHeightConstraint = NSLayoutConstraint(
item: webView,
attribute: .height,
relatedBy: .equal,
toItem: customView.webViewContainer,
attribute: .height,
multiplier: 1,
constant: 0
)
let webViewWidthConstraint = NSLayoutConstraint(
item: webView,
attribute: .width,
relatedBy: .equal,
toItem: customView.webViewContainer,
attribute: .width,
multiplier: 1,
constant: 0
)
let webViewLeftMarginConstraint = NSLayoutConstraint(
item: webView,
attribute: .leftMargin,
relatedBy: .equal,
toItem: customView.webViewContainer,
attribute: .leftMargin,
multiplier: 1,
constant: 0
)
let webViewRightMarginConstraint = NSLayoutConstraint(
item: webView,
attribute: .rightMargin,
relatedBy: .equal,
toItem: customView.webViewContainer,
attribute: .rightMargin,
multiplier: 1,
constant: 0
)
let webViewBottomMarginContraint = NSLayoutConstraint(
item: webView,
attribute: .bottomMargin,
relatedBy: .equal,
toItem: customView.webViewContainer,
attribute: .bottomMargin,
multiplier: 1,
constant: 0
)
NSLayoutConstraint.activate([webViewHeightConstraint, webViewWidthConstraint, webViewLeftMarginConstraint, webViewRightMarginConstraint, webViewBottomMarginContraint])
webView.navigationDelegate = self
Global.initialLoadWithParam(ofWebView: webView, withURL: NSURL(string: campaignUrl)!)
customView.closeButton.layer.cornerRadius = 0.9 * customView.closeButton.bounds.size.width
customView.closeButton.backgroundColor = #colorLiteral(red: 0.003921568627, green: 0.1647058824, blue: 0.2666666667, alpha: 1)
customView.closeButton.tintColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
customView.webViewContainer.layer.cornerRadius = 15
customView.webViewContainer.layer.masksToBounds = true
}
I expected that this should add
webView = Global.setWKWebViewInitWithConfig(frame: .zero)
to
customView.webViewContainer.addSubview(webView)
but it returns nil instead on customView.webViewContainer's line:
Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
Should'nt webViewContainer already be instantiated when customView was loaded on override func loadView()?
The problem is that your webViewContainer
is a weak
reference:
class FileView: UIView {
// ...
@IBOutlet weak var webViewContainer: UIView!
// ...
}
This works fine if
But in your case, in setup
you create an instance of the view, store it in the weak reference, and then ARC (the reference counting mechanism) will find out that there are no strong references any more to the UIView
and therfore clears the memory, resulting in a nil
reference.
So you need to somehow keep a strong reference; in the simplest case, just skip the weak
reference specifier in the outlet.
But keep in mind that you might have to do additional stuff to prevent strong reference cycles. In your case, if you do not work with xib
files, you could just remove all the @IBOutlet
stuff and make the NSCoder
initializer just call fatalError
, to prevent any creation from a xib
file.