how to wrap content a custom view in Swift - swift

I have created a xib file which contains some subviews. I set the appreciate width and height for the subviews.
there is class which inherited UIView. This class name is CustomUIView. Then I set This class as the file owner of that xib file.
Now I want to add this xib file to a view controller in storyboard. so I add a UIView and set its class to CustomUIView.
But I have to set width and height for this view. I set leading and top constraint. but I want the height and width of it be set based on the height and width of the subviews.
How should I do it

You don't have to set width and height anchors for the view , if you already set it inside the xib for every element
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let v = (Bundle.main.loadNibNamed("hhhh", owner: self, options: nil))?[0] as! hhhh;
view.addSubview(v)
v.translatesAutoresizingMaskIntoConstraints = false
v.topAnchor.constraint(equalTo:self.view.topAnchor,constant:30).isActive = true
v.leadingAnchor.constraint(equalTo:self.view.leadingAnchor,constant:30).isActive = true
}
Result

Related

How to programmatically set the constraints of the subViews of a UIPageViewController?

I have contained the subViews of a UIPageViewController within a UIView so that my screen has a partial scrollView container. However, the subViewControllers extend beyond both, the UIView that is supposed to contain the (horizontal/swiping page style) scrollView and the screen of the device.
I have already tried to use autolayout constraints but the subViews still go beyond the device screen.
Here is the UIView that contains the subViews of the UIPVC:
let pagingContainer: UIView = {
let view = UIView()
view.backgroundColor = .white
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
and here is the set up within viewDidLoad():
let pageController = PageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
addChild(pageController)
pageController.didMove(toParent: self)
pagingContainer.addSubview(pageController.view)
In case I haven't articulated properly:
What I wish for to happen is that the bottom half of my screen is a horizontal-page-style swiping scrollView that contains x number of subViewControllers (under UIPVC), and the size of subViewControllers are limited to the size of the UIView(pagingContainer).
I think I might understand what you're asking.
It should be pretty simple, set your left/right/top/bottom constraints for the pageController.view to be equal to the pagingContainer
In my example, I'm using SnapKit, so I set the edges equal to superview (which is the paingContainer).
let pageController = PageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
self.addChild(pageController)
pageController.didMove(toParent: self)
pagingContainer.addSubview(pageController.view)
// I set up constraints with SnapKit (since I mostly use that pod)
pageController.view.snp.makeConstraints({ (make) in
make.edges.equalToSuperview()
})
// But if I remember correctly, you can also set it like so:
pageController.view.translatesAutoresizingMaskIntoConstraints = false
pageController.view.widthAnchor.constraint(equalTo: self.pagingContainer.widthAnchor).isActive = true
pageController.view.heightAnchor.constraint(equalTo: self.pagingContainer.heightAnchor).isActive = true
pageController.view.centerXAnchor.constraint(equalTo: self.pagingContainer.centerXAnchor).isActive = true
Here is a quick gif of what it looks like. Main view controller only has red background and a pagingContainer on the bottom half and inset of 30 on each side (to demonstrate the size of pageController being within the pagingContainer and not overflowing)

UIView created from XIB file is not showing

I have created a xib file in my project which looks like this
I'm initializing it with this code
class MainItem: UIView {
class func instanceFromNib() -> UIView {
let view = UINib(nibName: "MainItem", bundle: nil).instantiate(withOwner: nil, options: nil).first as! UIView
return view
}
}
stackView.addArrangedSubview(MainItem.instanceFromNib())
and I'm adding it to a UIStackView which already has some views like UILabel or UIScrollView. but the added view is not showing when I'm running the app and here is the result
what is the problem?
One possible reason is that you did not specify the height of the view, so the height of it becomes to 0 during the rendering process. There are two ways to do it:
Set the alignment of you stackView to .fillEqually. You can do this because the label has non 0 intrinsic heigh.
stackView.distribution = .fillEqually
Tell iOS how to calculate the height of your view. For example override intrinsicContentSize of your view

Setting constraints on UITableView inside UIView

I have a UIViewController that has some base controls and in the center has a UIView (as container of swappable ui controls). I swap out the UI controls in this center UIView (container) depending on what the user is trying to do. All of the UI controls that go in to the UIView container are defined programmatically and I use programatic constraints to place them inside the UIView container.
This works fine for all of the sets of UI controls I have done so far. Now I am adding a set of controls to the UIView container that includes a UITableView
I cant figure out how to get the TableView to show up inside the UIView programatically. I can define say a button and label and run the app and see the container with the button and the label. If I add the UITableView as below then the container just does not show up at all.
// tableView
tableView.leftAnchor.constraint(equalTo: inputsContainerView.leftAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: inputsContainerView.bottomAnchor).isActive = true
tableView.widthAnchor.constraint(equalTo: inputsContainerView.widthAnchor).isActive = true
tableView.heightAnchor.constraint(equalTo: inputsContainerView.heightAnchor, multiplier: 1/2).isActive = true
prior to the above code I have already added all of the needed controls to the container subview ...
// Add controls to the view
inputsContainerView.addSubview(listTextView)
inputsContainerView.addSubview(listImageButton)
inputsContainerView.addSubview(listImageClear)
inputsContainerView.addSubview(tableView)
If I leave off the tableview then the container shows up with the other three fields. If I add the tableview then the container and all the other three controls are gone.
How do I add the tableView to the UIView and have it show up?
Here is how I defined the UITable view
let tableView: UITableView = {
let tv = UITableView()
return tv
}()
as a compare, when I define others controls like this they show up fine after adding to the subview and setting the constraints programatically
e.g.
// DEFINE
let listTextView: UITextView = {
let textView = UITextView()
textView.translatesAutoresizingMaskIntoConstraints = false
textView.text = ""
textView.textColor = defaultTextColor
textView.font = subtitleFont
textView.layer.borderColor = UIColor.black.cgColor
textView.layer.borderWidth = 1
textView.textAlignment = NSTextAlignment.left
return textView
}()
then later
// Place - with constraints
// listTextView
listTextView.leftAnchor.constraint(equalTo: inputsContainerView.leftAnchor, constant: padFromLeft).isActive = true
listTextView.topAnchor.constraint(equalTo: inputsContainerView.topAnchor, constant: 10).isActive = true
listTextView.widthAnchor.constraint(equalTo: inputsContainerView.widthAnchor, multiplier: 9/16).isActive = true
listTextView.heightAnchor.constraint(equalTo: inputsContainerView.widthAnchor, multiplier: 5/16).isActive = true
Just added my comment as detailed answer, so others can see the solution faster and benefit from it.
So taken from the apple documentation, to set your own constraints programmatically, you need to set translatesAutoresizingMaskIntoConstraints to false:
Note that the autoresizing mask constraints fully specify the view’s size and position; therefore, you cannot add additional constraints to modify this size or position without introducing conflicts. If you want to use Auto Layout to dynamically calculate the size and position of your view, you must set this property to false, and then provide a non ambiguous, nonconflicting set of constraints for the view.
So in your case you miss to set it for your table view, when you define it. Just add this line to it:
tv.translatesAutoresizingMaskIntoConstraints = false

Set UITableView header view height depends on its childviews

I have a headerview in my UITableView but I can't find a solution to make it dynamically height. In my headerview I have a UITextView which height depends on its content.
What I have tried so far.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
sizeHeaderToFit()
}
func sizeHeaderToFit() {
let headerView = mTabView.tableHeaderView!
mTxtViewDescription.sizeToFit()
mConDescriptionHeight.constant = mTxtViewDescription.frame.height
print(mContainerView.frame.maxY)
headerView.frame.size.height += mTxtViewDescription.frame.height
}
The green area is my headerview
Changing the height of the frame of a UITableView header that's already been set won't recalculate the height, meaning you can't just change the height of a tableHeaderView as its layout is managed by the tableView. You need to set the tableHeaderView again, setting the property triggers the new calculation.
There are a number of questions going into detail about this, several linked from this one.

UIView cropped off even though constraints are set

I've created a custom view in a xib file, in which I've set all simulated Metrics to "Inferred"
In my view I float one stack view x to the left by pinning the left and the top side and stack view to the right by pinning the right and top. Somehow like this (the = sign symbolizes the screen borders)
==========
=x y=
= =
= =
= =
= =
==========
I don't set any constraints for the width and the height, since everything should be inferred.
The corresponding class to my view is quite simple:
class MyView: UIView {
var view:UIView!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
view = UIView.loadFromNibNamed("MyView")
addSubview(view)
}
}
extension UIView {
class func loadFromNibNamed(nibNamed: String, bundle : NSBundle? = nil) -> UIView? {
return UINib(
nibName: nibNamed,
bundle: bundle
).instantiateWithOwner(nil, options: nil)[0] as? UIView
}
}
I use this view in my storyboard by pinning it to the left, right, top and bottom.
.
The thing is that the view is loaded from the nib, but that the width is somehow not dynamic. I would expect to the view to resize itself depending on the size of the parent. However, approximately 1/3 of the right Stack View is cropped off and not visible on screen even though I've set the constraints.
What do I have to do to fix this?
You need to set the translatesAutoresizingMaskIntoConstraints property to false for any view you load from a XIB if you want it to have a dynamic frame size. Otherwise the system will automatically create layout constraints like a fixed width and a fixed height that will keep your view from resizing.
Additionally, you need to add some constraints in code after adding your view to the view hierarchy that pin its top, bottom, left and right edge to the corresponding edges of its superview. After all, you're adding the XIB's contents as a subview to your custom view MyView i.e. the two views are not the same and you need to tell the system how it should position the subview (the constraints you added in your storyboard only relate to your MyView instance, not to its subview). These additions to your code should do the trick:
class MyView: UIView {
var view:UIView!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
view = UIView.loadFromNibNamed("MyView")
view.translatesAutoresizingMaskIntoConstraints = false
addSubview(view)
// Pin view to all four edges of its superview
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[view]|", options: [], metrics: nil, views: ["view": view]))
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[view]|", options: [], metrics: nil, views: ["view": view]))
}
}
Side notes:
Read a more detailed explanation on the translatesAutoresizingMaskIntoConstraints property in the official documentation.
All settings in the "simulated metrics" section in Interface Builder only apply to Interface Builder itself. They won't have any effect on your app when you run it on Simulator or a real device.
We need to identify were the issue is, so let's start by confirming that the stack view's are getting their frame's set. In your view controller where the stack views are we can override the following methods and check the frames of the stack views.
viewWillLayoyutSubviews()
viewDidLayoutSubviews()
Confirm that the frame's are getting set we can now look at the constraints that are being set in the storyboard. It is not clean fro the information you have provided how you want the stack views layout together, is one on top of the other view? Are they supposed to be side-by-side?
You will need to check that the constraints are valid for the layout you you want.
3.Confirm the Size Classes for your view(s) are configured correctly. Apple has a document called 'Size Classes Design Help' here. I believe this is your problem. Your custom class is set to have a size class of Any/Any and the super view of the custom view has a different set of size classes, for example Compact/Regular. So you custom view is not adjusting it's size based on size classes since it is set to be the same for any combination of size classes. Try configuring a different size class combination for your custom class to to see if there are any differences - Compact/Regular for example.