How to add xib subviews with dynamic height to UIView - ios

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

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)

Different Height for same UILabel Text

I have two UILabel with (orange and pink one) one is multiline label and other one is just one label. Two of this component (which has same texts) is inside UIStackView and this Stackview is inside UITableView (Automatic dimension). My problem is that whether I set the same text to UILabels their heights look different. I'm searching for this problem for two days but none of solutions work for me.
Ambiguous layout warnings for UILabels in UITableViewCell
Label1 has top, left, right layout and Label2 has top,left,right,bottom.
UIStackView distribution is .fillProportionally
I want the labels has same height If they are same for example;
For View1;
Label1 = "Multiline Label 1 Text which is two line"
Label2 = "One Line Label 2 text"
For View2;
Label1 = "Multiline Label 1 Text which is two line"
Label2 = "One Line Label 2 text"
What I want is: view1 and view2 height should be same because they have same elements with same content In them. Stackview which has this view1 and view2 has fill proportionally option should set Its height according to view1 + view2 height.
Container view which gets a xib file (It is just a xib file which has two label in it with the constraints I mention earlier).
class ViewWithLabel: UIView {
#IBOutlet weak var firstLabel: UILabel!
#IBOutlet weak var secondLabel: UILabel!
#IBOutlet var containerView: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
self.translatesAutoresizingMaskIntoConstraints = false
// first: load the view hierarchy to get proper outlets
let name = String(describing: type(of: self))
let nib = UINib(nibName: name, bundle: .main)
nib.instantiate(withOwner: self, options: nil)
addSubview(containerView)
containerView.frame = self.bounds
containerView.autoresizingMask = [.flexibleWidth,.flexibleHeight]
}
convenience init(firstLabelText: String, secondLabelText: String) {
self.init()
firstLabel.text = firstLabelText
secondLabel.text = secondLabelText
}
override func layoutSubviews() {
super.layoutSubviews()
firstLabel.preferredMaxLayoutWidth = self.frame.width
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Another container view which has a Stackview and gets the above view and add to Stackview.
class ViewWithStack: UIView {
let verticalStackView: UIStackView = {
let s = UIStackView()
s.distribution = .fillEqually
s.spacing = 0
s.axis = .vertical
s.translatesAutoresizingMaskIntoConstraints = false
return s
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.translatesAutoresizingMaskIntoConstraints = false
self.backgroundColor = UIColor.white
self.layer.cornerRadius = 6.0
self.layer.applySketchShadow(color: UIColor(red:0.56, green:0.56, blue:0.56, alpha:1), alpha: 0.2, x: 0, y: 0, blur: 10, spread: 0)
addSubview(verticalStackView)
verticalStackView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
let bottomEqual = verticalStackView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0)
bottomEqual.isActive = true
verticalStackView.leftAnchor.constraint(equalTo: self.leftAnchor,constant: 0).isActive = true
verticalStackView.rightAnchor.constraint(equalTo: self.rightAnchor,constant: 0).isActive = true
verticalStackView.layoutMargins = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20)
verticalStackView.isLayoutMarginsRelativeArrangement = true
}
convenience init(orientation: NSLayoutConstraint.Axis,labelsArray: [UIView]) {
self.init()
verticalStackView.axis = orientation
for label in labelsArray {
verticalStackView.addArrangedSubview(label)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
EDIT: If I write firstLabel.sizeToFit() in layoutSubviews function. First label looks fixed but content view still has same height and there is a big gap between label 1 and label 2.
EDIT 2: When I add setContentHuggingPriority(.required, for: .vertical) for labels. First label looks okay but second label looks bigger too.

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)

XIB view not showing with correct layout [iOS Swift]

I'm trying to make a custom alert view and facing some issues with the fact that the displayed view is cutting the bottom half of the view (Images below)
How it's being displayed:
Desired Output:
So basically, I have a XIB called CustomAlertView supported by a class of same name with init as follows:
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("CustomAlertView", owner: self, options: nil)
contentView.frame = self.bounds
contentView.translatesAutoresizingMaskIntoConstraints = true
addSubview(contentView)
//contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
I have another class that is responsible for creating an alert, CustomAlert, using the customAlertView. This CustomAlert class is creating the backgroundView and dialogView( which I'm trying to add my customAlertView to it) with the following code:
func initialize(title:String, description:String){
dialogView.clipsToBounds = true
backgroundView.frame = frame
backgroundView.backgroundColor = UIColor.black
backgroundView.alpha = 0.6
backgroundView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTappedOnBackgroundView)))
addSubview(backgroundView)
dialogView.frame.origin = CGPoint(x: 0, y: 0)
dialogView.frame.size = CGSize(width: frame.width-32, height: frame.height/3)
dialogView.backgroundColor = UIColor.white
dialogView.layer.cornerRadius = 6
let alertView = CustomAlertView.init(frame: self.bounds)
alertView.titleLabel.text = title
alertView.descriptionLabel.text = description
alertView.cancelButton.backgroundColor = UIColor.brown
dialogView.addSubview(alertView)
addSubview(dialogView)
}
I believe that I'm making a confusion with the frames and bounds but couldn't find a solution.
I'd like the desired output to be placed perfectly inside the dialogView.
EDIT
Code for my .show function in CustomAlert
func show(animated:Bool){
self.backgroundView.alpha = 0
self.dialogView.center = CGPoint(x: self.center.x, y: self.frame.height + self.dialogView.frame.height/2)
UIApplication.shared.delegate?.window??.rootViewController?.view.addSubview(self)
if animated {
UIView.animate(withDuration: 0.33, animations: {
self.backgroundView.alpha = 0.66
})
UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 10, options: UIViewAnimationOptions(rawValue: 0), animations: {
self.dialogView.center = self.center
}, completion: { (completed) in
})
}else{
self.backgroundView.alpha = 0.66
self.dialogView.center = self.center
}
}
Github link git-alert-view
For anyone facing the same difficulties as me, I was able to accomplish the wanted result.
I used AutoLayouts as suggested by #HAK. But instead of writing my own NSLayoutConstraint I used roberthein library called TinyConstraints.
Basically, I used as follow:
Instead of
NSLayoutConstraint.activate([
alertView.topAnchor.constraint(equalTo: superview.topAnchor, constant: 0),
alertView.leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: 0),
alertView.bottomAnchor.constraint(equalTo: superview.bottomAnchor, constant: 0),
alertView.trailingAnchor.constraint(equalTo: superview.trailingAnchor, constant:
0)])
with TinyConstraints:
alertView.edges(to: superview)
That´s it
Change your CustomAlertView like this:
class CustomAlertView: UIView {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var descriptionLabel: UILabel!
#IBOutlet weak var confirmButton: UIButton!
#IBOutlet weak var cancelButton: UIButton!
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
static func customAlert() -> CustomAlertView {
return Bundle.main.loadNibNamed("CustomAlertView", owner: self, options: nil)!.first as! CustomAlertView
}
}
Your CustomAlert's initialize method like this:
func initialize(title:String, description:String){
dialogView.clipsToBounds = true
backgroundView.frame = frame
backgroundView.backgroundColor = UIColor.black
backgroundView.alpha = 0.6
backgroundView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTappedOnBackgroundView)))
addSubview(backgroundView)
dialogView.frame.origin = CGPoint(x: 0, y: 0)
dialogView.frame.size = CGSize(width: frame.width-32, height: frame.height/3)
dialogView.backgroundColor = UIColor.white
dialogView.layer.cornerRadius = 6
let alertView = CustomAlertView.customAlert()
alertView.titleLabel.text = title
alertView.descriptionLabel.text = description
alertView.cancelButton.backgroundColor = UIColor.brown
dialogView.addSubview(alertView)
addSubview(dialogView)
}
In the CustomAlertView xib:
1. Select fileowner and remove the class (default to NSObject).
2. Select fileowner and then remove all the outlets.
3. Select your content view and give it class = CustomAlertView.
4. Select your CustomAlertView and make all outlet connections.
Final Xib:
And you have a working alert:
PS: Adjust the UI accordingly.
In your xib class add this :
override func awakeFromNib() {
super.awakeFromNib()
xibSetup()}
func xibSetup() {
guard let view = loadViewFromNib() else { return }
view.frame = bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(view)
contentView = view
}
Reference from this : https://medium.com/zenchef-tech-and-product/how-to-visualize-reusable-xibs-in-storyboards-using-ibdesignable-c0488c7f525d#.3c0javomy**

Add WkWebView constraints

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 :

Resources