Add WkWebView constraints - ios

I added a wkwebView into a nib. I can not set the size from the wkWebView to the contentView (BlueView). The constraints from the BlueView are correct. Here my code:
thanks for helping
#IBOutlet weak var view: UIView!
#IBOutlet weak var contentView: UIView!
#IBOutlet weak var stopButton: UIButton!
#IBOutlet weak var skipButton: UIButton!
#IBOutlet weak var infoLabel: UILabel!
var webView = WKWebView(frame: .zero)
override func awakeFromNib() {
webView.frame.size = CGSize(width: contentView.bounds.width, height: contentView.bounds.height)
contentView.addSubview(webView)
let myURL = URL(string: "https://www.apple.com")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
}
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
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 = Bundle(for: type(of: self))
let nib = UINib(nibName: "ReCaptchaV2", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil).first as! UIView
return view
}
enter image description here
enter image description here

Set these constraints
override func awakeFromNib() {
contentView.addSubview(webView)
webView.translatesAutoresizingMaskIntoConstraints = false
webView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 0).isActive = true
webView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 0).isActive = true
webView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0).isActive = true
webView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0).isActive = true
let myURL = URL(string: "https://www.apple.com")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
}

Set the constraints from the parent view, not from safe area. And keep only 4 constraints. So :
trailing constraint from parent view = 0
bottom constraint from button = 0
top constraint from parent view = 0
left constraint from parent view = 0
Check these screenshots :
1. Constraints :
2. Parent view selected in constraint :
3. Output :

Related

How to dynamically resize text view inside a stack view

I'm trying to display a dynamically sized UITextView inside a stack view, but the text view is not adjusting to the size of the content.
First I have the arranged subview:
class InfoView: UIView {
private var title: String!
private var detail: String!
private var titleLabel: UILabel!
private var detailTextView: UITextView!
init(infoModel: InfoModel) {
self.title = infoModel.title
self.detail = infoModel.detail
super.init(frame: .zero)
configure()
setConstraint()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func configure() {
titleLabel = UILabel()
titleLabel.text = title
titleLabel.font = .rounded(ofSize: titleLabel.font.pointSize, weight: .bold)
titleLabel.textColor = .lightGray
titleLabel.sizeToFit()
titleLabel.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(titleLabel)
detailTextView = UITextView()
detailTextView.sizeToFit()
detailTextView.text = detail
detailTextView.font = UIFont.systemFont(ofSize: 19)
detailTextView.isEditable = false
detailTextView.textColor = .lightGray
detailTextView.isUserInteractionEnabled = false
detailTextView.isScrollEnabled = false
detailTextView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(detailTextView)
}
private func setConstraint() {
NSLayoutConstraint.activate([
titleLabel.topAnchor.constraint(equalTo: self.topAnchor),
titleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 5),
titleLabel.heightAnchor.constraint(equalToConstant: 40),
detailTextView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor),
detailTextView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
detailTextView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
detailTextView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
])
}
}
Then I implement the stack view in a view controller:
class MyViewController: UIViewController {
var infoModelArr: [InfoModel]!
var stackView: UIStackView!
var scrollView: UIScrollView!
init(infoModelArr: [InfoModel]) {
self.infoModelArr = infoModelArr
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
var infoViewArr = [InfoView]()
for infoModel in infoModelArr {
let infoView = InfoView(infoModel: infoModel)
infoViewArr.append(infoView)
}
stackView = UIStackView(arrangedSubviews: infoViewArr)
stackView.axis = .vertical
stackView.spacing = 10
stackView.distribution = .fillProportionally
stackView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor),
stackView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor),
])
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.contentSize = stackView.bounds.size
}
}
Finally, I call the view controller as following:
let myVC = MyViewController(infoModelArr: [InfoModel(title: "title", detail: "detail"), InfoModel(title: "title", detail: "detail")])
self.present(myVC, animated: true, completion: nil)
Notably, if I were to instantiate the stack view with a single arranged subview, the height of the stack view seems to be dynamically adjusted, but as soon as 2 or more subviews are introduced, the height doesn't reflect the content.
When I attempted to set the intrinsic size of the InfoView,
override func layoutSubviews() {
super.layoutSubviews()
height = titleLabel.bounds.height + detailTextView.bounds.height
}
var height: CGFloat! = 200 {
didSet {
self.invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
let originalSize = super.intrinsicContentSize
return CGSize(width: originalSize.width, height: height)
}
detailTextView.bounds.height returns 0.
The fillProportionally distribution tries to scale the heights of the arranged subviews according to their intrinsic content size, as a proportion of of the stack view's height. e.g. if the stack view has a height of 120, and arranged subview A has an intrinsic height of 10, and arranged subview B has an intrinsic height of 20, then A and B will have a height of 40 and 80 respectively in the stack view.
Your stack view doesn't have a defined height, so fillProportionally doesn't make much sense here.
Instead, a distribution of fill should do the job:
stackView.distribution = .fill
(as an experiment, you can try adding a height constraint to the stack view, and you'll see how fillProportionally works)

Select and deselect reusable views

I create a custom view in xib file. I add 3 views(which inherited from custom) in viewController. Initially they have white color, but when i click on first view it should be changed other color and if i click on second view, the first view should be back in white color.
I need help to change first view color back to white when second view is selected.
My code for customView here
class SubscriptionView: UIView {
#IBOutlet weak var title: UILabel!
#IBOutlet weak var subTitle: UILabel!
#IBOutlet weak var checkMark: UIImageView!
var isSelect: Bool = false
let nibName = "SubscriptionView"
var contentView: UIView?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
override func awakeFromNib() {
super.awakeFromNib()
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapAction)))
}
func commonInit() {
guard let view = loadViewFromNib() else {
return
}
view.frame = self.bounds
view.layer.masksToBounds = true
view.layer.cornerRadius = 14
view.layerBorderColor = AppColor.amaranth
view.layerBorderWidth = 0.5
self.addSubview(view)
contentView = view
}
func loadViewFromNib() -> UIView? {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: nibName, bundle: bundle)
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
public func selectedView(_ isSelect: Bool) {
self.isSelect = isSelect
title.textColor = isSelect ? UIColor.white : AppColor.amaranth
subTitle.textColor = isSelect ? UIColor.white : AppColor.amaranth
checkMark.alpha = isSelect ? 1.0 : 0.0
contentView!.backgroundColor = isSelect ? AppColor.amaranth : UIColor.white
}
#objc private func tapAction() {
///????? selectedView
}
}
Here is a very simple example using the Delegate / Protocol pattern.
Define a Protocol:
// Protocol / Delegate pattern
protocol SubscriptionViewDelegate {
func gotTap(from sender: SubscriptionView)
}
Have your view controller conform to that protocol. In your custom SubscriptionView class, when the user taps you will tell the delegate that a tap was received, and the controller will loop through the SubscriptionView objects setting the "selected" state to true or false, based on the tapped view.
class SubscriptionsViewController: UIViewController, SubscriptionViewDelegate {
let theStackView: UIStackView = {
let v = UIStackView()
v.translatesAutoresizingMaskIntoConstraints = false
v.axis = .vertical
v.alignment = .fill
v.distribution = .fill
v.spacing = 20
return v
}()
// array to track the "subscription" views
var arrayOfSubscriptionViews: [SubscriptionView] = [SubscriptionView]()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .yellow
// add a stack view to hold the "subscription" views
view.addSubview(theStackView)
NSLayoutConstraint.activate([
theStackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20.0),
theStackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20.0),
theStackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20.0),
])
// instantiate 3 "subscription" views
for _ in 1...3 {
// instantiate view
let v = SubscriptionView()
// set self as its delegate
v.delegate = self
// add it to our stack view
theStackView.addArrangedSubview(v)
// append it to our tracking array
arrayOfSubscriptionViews.append(v)
}
}
func gotTap(from sender: SubscriptionView) {
// just for dev / debugging
print("got tap from", sender)
// loop through the subscription views,
// setting the sender selected to TRUE
// the others to FALSE
arrayOfSubscriptionViews.forEach {
$0.selectedView($0 == sender)
}
}
}
// Protocol / Delegate pattern
protocol SubscriptionViewDelegate {
func gotTap(from sender: SubscriptionView)
}
class SubscriptionView: UIView {
#IBOutlet weak var title: UILabel!
#IBOutlet weak var subTitle: UILabel!
#IBOutlet weak var checkMark: UIImageView!
var isSelect: Bool = false
var delegate: SubscriptionViewDelegate?
let nibName = "SubscriptionView"
var contentView: UIView?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
override func awakeFromNib() {
super.awakeFromNib()
}
func commonInit() {
guard let view = loadViewFromNib() else {
return
}
view.frame = self.bounds
view.layer.masksToBounds = true
view.layer.cornerRadius = 14
view.layer.borderColor = UIColor.red.cgColor // AppColor.amaranth
view.layer.borderWidth = 0.5
self.addSubview(view)
contentView = view
selectedView(false)
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapAction)))
}
func loadViewFromNib() -> UIView? {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: nibName, bundle: bundle)
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
public func selectedView(_ isSelect: Bool) {
self.isSelect = isSelect
title.textColor = isSelect ? UIColor.white : .red // AppColor.amaranth
subTitle.textColor = isSelect ? UIColor.white : .red // AppColor.amaranth
checkMark.alpha = isSelect ? 1.0 : 0.0
// contentView!.backgroundColor = isSelect ? AppColor.amaranth : UIColor.white
contentView!.backgroundColor = isSelect ? UIColor.red : UIColor.white
}
#objc private func tapAction() {
// for dev / debugging
print("sending tap from", self)
// tell the delegate self got tapped
delegate?.gotTap(from: self)
}
}
I only made minor changes to your SubscriptionView class, so you should be able to use it as-is with your existing .xib

Load view from XIB as a subview of a scrollview

I'm still a SO and Swift newbie, so please, be patient and feel free to skip this question :-)
In the body of a XIB's awakeFromNib, I want to load some views as subviews of a UIScrollView (basically, the XIB contains a scrollview, a label and a button).
The scrollview perfectly works if in a loop I load views I create on the fly, eg.
let customView = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 150))
customView.frame = CGRect(x: i*300 , y: 0, width: 300, height: 150)
customView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(customView)
But I have a different goal.
In another XIB I have an image view and a stackview with some labels. This XIB is connected in the storyboard to a class SingleEvent that extends UIView.
I want to do the following:
use the XIB as a sort of "blueprint" and load the same view multiple times in my scrollview;
pass to any instance some data;
Is this possible?
I tried to load the content of the XIB this way:
let customView = Bundle.main.loadNibNamed("SingleEvent", owner: self, options: nil)?.first as? SingleEvent
and this way:
let customView = SingleEvent()
The first one makes the app crash, while the second causes no issue, but I can't see any effect (it doesn't load anything).
The content of my latest SingleEvent is the following:
import UIKit
class SingleEvent: UIView {
#IBOutlet weak var label:UILabel!
#IBOutlet weak var imageView:UIImageView!
override init(frame: CGRect) {
super.init(frame: frame)
loadViewFromNib()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
loadViewFromNib()
}
func loadViewFromNib() -> UIView {
let myView = Bundle.main.loadNibNamed("SingleEvent", owner: self, options: nil)?.first as! UIView
return myView
}
}
Thanks in advance, any help is appreciated :-)
There are a number of approaches to loading custom views (classes) from xibs. You may find this method a bit easier.
First, create your xib like this:
Note that the Class of File's Owner is the default (NSObject).
Instead, assign your custom class to the "root" view in your xib:
Now, our entire custom view class looks like this:
class SingleEvent: UIView {
#IBOutlet var topLabel: UILabel!
#IBOutlet var middleLabel: UILabel!
#IBOutlet var bottomLabel: UILabel!
#IBOutlet var imageView: UIImageView!
}
And, instead of putting loadNibNamed(...) inside our custom class, we create a UIView extension:
extension UIView {
class func fromNib<T: UIView>() -> T {
return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
}
}
To load and use our custom class, we can do this:
class FromXIBViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// create an instance of SingleEvent from its xib/nib
let v = UIView.fromNib() as SingleEvent
// we're going to use auto-layout & constraints
v.translatesAutoresizingMaskIntoConstraints = false
// set the text of the labels
v.topLabel?.text = "Top Label"
v.middleLabel?.text = "Middle Label"
v.bottomLabel?.text = "Bottom Label"
// set the image
v.imageView.image = UIImage(named: "myImage")
// add the SingleEvent view
view.addSubview(v)
// constrain it 200 x 200, centered X & Y
NSLayoutConstraint.activate([
v.widthAnchor.constraint(equalToConstant: 200.0),
v.heightAnchor.constraint(equalToConstant: 200.0),
v.centerXAnchor.constraint(equalTo: view.centerXAnchor),
v.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
}
}
With a result of:
And... here is an example of loading 10 instances of SingleEvent view and adding them to a vertical scroll view:
class FromXIBViewController: UIViewController {
var theScrollView: UIScrollView = {
let v = UIScrollView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .cyan
return v
}()
var theStackView: UIStackView = {
let v = UIStackView()
v.translatesAutoresizingMaskIntoConstraints = false
v.axis = .vertical
v.alignment = .fill
v.distribution = .fill
v.spacing = 20.0
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
// add the scroll view to the view
view.addSubview(theScrollView)
// constrain it 40-pts on each side
NSLayoutConstraint.activate([
theScrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40.0),
theScrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -40.0),
theScrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 40.0),
theScrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -40.0),
])
// add a stack view to the scroll view
theScrollView.addSubview(theStackView)
// constrain it 20-pts on each side
NSLayoutConstraint.activate([
theStackView.topAnchor.constraint(equalTo: theScrollView.topAnchor, constant: 20.0),
theStackView.bottomAnchor.constraint(equalTo: theScrollView.bottomAnchor, constant: -20.0),
theStackView.leadingAnchor.constraint(equalTo: theScrollView.leadingAnchor, constant: 20.0),
theStackView.trailingAnchor.constraint(equalTo: theScrollView.trailingAnchor, constant: -20.0),
// stackView width = scrollView width -40 (20-pts padding on left & right
theStackView.widthAnchor.constraint(equalTo: theScrollView.widthAnchor, constant: -40.0),
])
for i in 0..<10 {
// create an instance of SingleEvent from its xib/nib
let v = UIView.fromNib() as SingleEvent
// we're going to use auto-layout & constraints
v.translatesAutoresizingMaskIntoConstraints = false
// set the text of the labels
v.topLabel?.text = "Top Label: \(i)"
v.middleLabel?.text = "Middle Label: \(i)"
v.bottomLabel?.text = "Bottom Label: \(i)"
// set the image (assuming we have images named myImage0 thru myImage9
v.imageView.image = UIImage(named: "myImage\(i)")
theStackView.addArrangedSubview(v)
}
}
}
Result:
Ok, I see. The problem probably in fact that loadViewFromNib function return UIView from xib, but you doesn't use it any way.
Let's try this way:
1) Make your loadViewFromNib function static
// Return our SingleEvent instance here
static func loadViewFromNib() -> SingleEvent {
let myView = Bundle.main.loadNibNamed("SingleEvent", owner: self, options: nil)?.first as! SingleEvent
return myView
}
2) Remove all inits in SingleEvent class
3) Init it in needed place like this:
let customView = SingleView.loadViewFromNib()
To pass data inside view you can create new function in SingleView class:
func configureView(with dataModel:DataModel) {
//Set data to IBOutlets here
}
And use it from outside like this:
let customView = SingleView.loadViewFromNib()
let dataModel = DataModel()
customView.configureView(with: dataModel)

How to add xib subviews with dynamic height to UIView

It should be easy to do and it's very easy in android(with LinearLayout) but I can't figure out how to do it in ios.
I have UIView into which I want to add some dynamic height subviews. Subview is loaded from xib file and contains 2 UILabels with dynamic height.
class Subview: UIView {
#IBOutlet var contentView: UIView!
#IBOutlet weak var title: UILabel!
#IBOutlet weak var desc: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.commonInit()
}
fileprivate func commonInit() {
Bundle.main.loadNibNamed("Subview", owner: self, options: nil)
addSubview(contentView)
self.contentView.frame = self.bounds
self.contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
}
and subview is added like this
var containerView: UIView
func setupView() {
let subview1 = Subview()
subview1.title = "Some title"
subview1.desc = "Some very long description"
self.containerView.addSubview(subview1)
}
Issue is that in this case, subview is not displayed.
If I add
subview1.frame = CGSize(width: self.bounds.width, height: 100)
subview is displayed but height 100 is not correct
If I try to calculate subview size
let measuredSize = subview1.sizeThatFits(CGSize(width: self.bounds.width, height: CGFloat(MAXFLOAT)))
measured width and size is 0.
Any idea what I am doing wrong here? Thanks!
Edit: This is my subview xib file with constraints
You can easily accomplish this by
1- hooking the constraints properly in the xib file from top to bottom
2- theView.translatesAutoresizingMaskIntoConstraints = false
3- set leading , trailing , top constraints ( No height ) , for eample this is a simple xib with one label
When adding it to self.view by
let vv = (Bundle.main.loadNibNamed("repairView", owner: self, options: nil))?[0] as! repairView;
vv.translatesAutoresizingMaskIntoConstraints = false
vv.aedlb.text = "dsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsvvvvdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsvvvvdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsvvvvdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsvvvvdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjdsdsjhdsjhdsjhsdhjdsjhesisbeudjbdsjbdsjds"
view.addSubview(vv)
NSLayoutConstraint.activate([
vv.topAnchor.constraint(equalTo: view.topAnchor, constant: 20),
vv.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0),
vv.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0),
])
The result

Button on UIView does not respond unless I specify the correct CGRect

I am trying to implement a simple UIView that will show at the bottom of the screen when internet connection is lost and will have a dismiss button. My UIView class looks something like this.
import UIKit
class ConnectionLostAlertUIView: UIView {
#IBOutlet var view: UIView!
var viewConstraints: [NSLayoutConstraint] = []
override init(frame: CGRect) {
super.init(frame: frame)
Bundle.main.loadNibNamed("ConnectionLostAlertView", owner: self, options: nil)
self.addSubview(self.view)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
Bundle.main.loadNibNamed("ConnectionLostAlertView", owner: self, options: nil)
self.addSubview(self.view)
}
override func didMoveToSuperview() {
view.translatesAutoresizingMaskIntoConstraints = false
let bottomConstraint = view.bottomAnchor.constraint(equalTo: self.superview!.bottomAnchor)
let leftConstraint = view.leadingAnchor.constraint(equalTo: self.superview!.leadingAnchor)
let rightConstraint = view.trailingAnchor.constraint(equalTo: self.superview!.trailingAnchor)
viewConstraints = [bottomConstraint, leftConstraint, rightConstraint]
NSLayoutConstraint.activate(viewConstraints)
}
#IBAction func dismissButton(_ sender: Any) {
Logger.log(message: "Dismiss button was pressed", event: .d)
self.isHidden = true
}
And I add this UIView to my UIViewController by doing the following
var ConnectionLostAlert: ConnectionLostAlertUIView!
ConnectionLostAlert = ConnectionLostAlertUIView(frame: CGRect.zero)
self.view.addSubview(ConnectionLostAlert)
Well, I can use actual values for CGRect instead of zero but the problem is that after the constraints are added in my UIView, the button in my UIView stops responding. It works just fine if I actually set some CGRect coordinates and not use constraints in the UIView. Is there a better way to do what I am trying to achieve that will still allow me to handle the button event inside the UIView?
The problem is you are adding constraints from the inner view i.e view to the superview skipping the main view (ConnectionLostAlertUIView) that is actually being added to the super view.
What you should do instead is to add constraints between view and self first. Then when self(ConnectionLostAlertUIView) is added to the superview, add the constraints with super view then.
Do something like this:
#IBOutlet var view: UIView!
var viewConstraints: [NSLayoutConstraint] = []
override init(frame: CGRect) {
super.init(frame: frame)
Bundle.main.loadNibNamed("ConnectionLostAlertView", owner: self, options: nil)
self.addSubview(self.view)
view.translatesAutoresizingMaskIntoConstraints = false
let bottomConstraint = view.bottomAnchor.constraint(equalTo: self.bottomAnchor)
let leftConstraint = view.leadingAnchor.constraint(equalTo: self.leadingAnchor)
let rightConstraint = view.trailingAnchor.constraint(equalTo: self.trailingAnchor)
viewConstraints = [bottomConstraint, leftConstraint, rightConstraint]
NSLayoutConstraint.activate(viewConstraints)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
Bundle.main.loadNibNamed("ConnectionLostAlertView", owner: self, options: nil)
self.addSubview(self.view)
}
override func didMoveToSuperview() {
self.translatesAutoresizingMaskIntoConstraints = false
let bottomConstraint = self.bottomAnchor.constraint(equalTo: self.superview!.bottomAnchor)
let leftConstraint = self.leadingAnchor.constraint(equalTo: self.superview!.leadingAnchor)
let rightConstraint = self.trailingAnchor.constraint(equalTo: self.superview!.trailingAnchor)
viewConstraints = [bottomConstraint, leftConstraint, rightConstraint]
NSLayoutConstraint.activate(viewConstraints)
}
#IBAction func dismissButton(_ sender: Any) {
Logger.log(message: "Dismiss button was pressed", event: .d)
self.isHidden = true
}

Resources