Container View bounds issue in Swift - ios

I'm writing a function which can get the height and width of the container view:
// container view's UIViewController
class SelectionView: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
func getBounds -> (CGFloat,CGFloat){
let x = self.view.bounds.width / 5
let y = self.view.bounds.height / 15
return x,y
}
}
I write a button to call this getBounds() and it works well, but when I put it in the viewDidLoad() function
override func viewDidLoad() {
super.viewDidLoad()
getBounds()
}
getBounds() returns me different height and width and it is clearly not the bounds of this container view.
I'm pretty sure I've linked this class to the container view!

View layout is not setup in viewDidLoad. Therefore any resizing is not done yet and your size is wrong(probably the same as declared in Storyboard/Xib).
Move getBounds in viewWillLayoutSubviews or viewWillAppear and it will work correctly. Please mind that those method won't be called one time only ;)

The view hasn't been laid out in viewDidLoad, you will likely need to catch it in a later method or in viewDidLayoutSubviews.

Related

Container view width decreases after adding safe area constraints

I have a ViewController and a container view testcontrollerViewController inside of it. I noticed that after I'm adding constraints to that container view in the storyboard (all the edges match the safe area) its width decreases a little bit, but if I remove constraints width changes back to normal. (Switches from 724px to 712px and back)
Is it a bug or am I doing something wrong?
I print container view width in its viewDidAppear function.
class testcontrollerViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
view.backgroundColor = .red
print("container vc load \(testview.frame.width)")
}
override func viewDidAppear(_ animated: Bool) {
print("container vc appear \(testview.frame.width)")
print("container view safe area insets \(view.safeAreaInsets)")
}
#IBOutlet var testview: UIView!
In my opinion, I think because there is a gap between the superview and safearea. When you constraints the container view it relates to the safearea only - I think that is the reason the width changed above.

Swift - Interactive dismissing form sheet doesn't work iOS 13

I'm presenting my UIViewController with modalPresentationStyle = .formSheet. Everything works as expected except interactive dismissing this controller (I drag form sheet from the top down). Controller is dismissed but it's presentingViewController doesn't move back. It remains stucked with black edges around.
If I dismiss presented controller using dismiss(animated:completion:), presentingViewController moves back as expected so it seems it doesn't work just when user dismiss it by dragging.
If you need any other details let me know. Thank you. šŸ™
I'm using iOS 13.3 and Xcode 11.2.1.
Update:
I found a problem. šŸŽ‰ This view which I presented contained UITableView. I was using my method to set its tableFooterView:
extension UITableView {
func setFooterView(_ view: UIView) {
view.frame = bounds
view.setNeedsLayout()
view.layoutIfNeeded()
let height = view.systemLayoutSizeFitting(
CGSize(width: view.frame.width, height: UIView.layoutFittingCompressedSize.height)
).height
var frame = view.frame
frame.size.height = height
view.frame = frame
tableFooterView = view
sectionFooterHeight = 0
}
}
...and I called this method in my view controller in viewDidLayoutSubviews() since I thought that this is where I should set this footer view (Simply because we were using it like this in various projects šŸ¤·šŸ»ā€ā™‚ļø):
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.setFooterView(footerView)
}
But I wasn't right. Simply calling it in viewDidLoad() resolved it.
override func viewDidLoad() {
super.viewDidLoad()
tableView.setFooterView(footerView)
}
No the question is: why?
If anyone could provide correct explanation why this happens I would be grateful and I would reward him/her with bounty. šŸŽ

Get the frame of a UIStackView subViews

I have created a UIStackView in IB which has the distribution set to Fill Equally. I am looking to get the frame for each subView but the following code always returns (0, 0, 0, 0).
class ViewController: UIViewController {
#IBOutlet weak var stackView: UIStackView!
override func viewDidLoad() {
super.viewDidLoad()
let pView = UIView()
let sView = UIView()
pView.backgroundColor = UIColor.red
sView.backgroundColor = UIColor.orange
stackView.addArrangedSubview(pView)
stackView.addArrangedSubview(sView)
}
override func viewDidLayoutSubviews() {
print(stackView.arrangedSubviews[0].frame)
print(stackView.arrangedSubviews[1].frame)
}
}
I would think that a stack view set to fill equally would automatically set the calculate it.
Any help would be appreciated.
After reading over your code I think this is just a misunderstanding of viewDidLayoutSubviews(). Basically it is called when all the views that are descendants of the main view have been laid out but this does not include the subviews(descendants) of these views. See discussion notes from Apple.
"When the bounds change for a view controller's view, the view adjusts the positions of its subviews and then the system calls this method. However, this method being called does not indicate that the individual layouts of the view's subviews have been adjusted. Each subview is responsible for adjusting its own layout."
Now there are many ways to get the frame of the subviews with this being said.
First you could add one line of code in viewdidload and get it there.
override func viewDidLoad() {
super.viewDidLoad()
let pView = UIView()
let sView = UIView()
pView.backgroundColor = UIColor.red
sView.backgroundColor = UIColor.orange
stackView.addArrangedSubview(pView)
stackView.addArrangedSubview(sView)
stackView.layoutIfNeeded()
print(stackView.arrangedSubviews[0].frame)
print(stackView.arrangedSubviews[1].frame)
}
OR you can wait until viewDidAppear and check there.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print(stackView.arrangedSubviews[0].frame)
print(stackView.arrangedSubviews[1].frame)
}

Swift / cornerRadius and masksToBounds lets UIImageView disappear

I have a UITableViewController with a UIImageView. The content is loaded with the following simple code.
#IBOutlet weak var avatarImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
updateUI()
}
func updateUI() {
backendless.userService.currentUser.retrieveProperties()
avatarImageView.layer.cornerRadius = avatarImageView.frame.size.width/2
avatarImageView.layer.masksToBounds = true
let imgURL = NSURL(string: (backendless.userService.currentUser.getProperty("Avatar") as? String)!)
avatarImageView.sd_setImageWithURL(imgURL)
tableView.reloadData()
}
The ImageView does not appear.
But if I delete the following code, the UIImageView will be set and displayed correctly.
avatarImageView.layer.cornerRadius = avatarImageView.frame.size.width/2
avatarImageView.layer.masksToBounds = true
What am I missing? Help is very appreciated.:
PS:
Xcode 8.0
Swift 2.3
Contrains for ImageView are set to 120*120
sd_setImageWithURL = pod 'SDWebImage'
Try this:
override func viewDidLoad() {
super.viewDidLoad()
self.view.layoutIfNeeded() // <- This will update your initial view frames
updateUI()
}
What is happening is that in Xcode 8.0, by the time you hit viewDidLoad, the frame of all subviews of a controller is still x: 0.0, y: 0.0, w: 1000.0, h: 1000.0, so in effect you're making the corner radius of your avatar image view 500.0pt. When you call layout if needed, the correct frames will be calculated, and give you the value you're expecting.
One obvious problem is that you are calling updateUI too soon. In viewDidLoad, there is no UI. The view has loaded but that is all; it is not in the interface and no layout has yet taken place. Your code depends upon avatarImageView.frame, but we do not yet know what that is. You have constraints on the image view, but they have not yet taken effect! Thus you should postpone this call until after the interface is present, e.g. viewDidAppear or at least viewDidLayoutSubviews. But of course then you probably only want to call it once (typed in browser, not tested, but it will be something like this):
var didConfigureUI = false
func viewDidLayoutSubviews() {
if !didConfigureUI {
didConfigureUI = true
updateUI()
}
}
I think your problem with your controller. You have added the image view to a UITableViewController. That means constraint set to image view will not work if you use the frame in view did load. You need to change frame of the UIIMageView before use it or you need to use UIViewController. use the below code before using the frame of image view.
avatarImageView.frame.size.width = 120.0
avatarImageView.frame.size.height = 120.0
I think this will solve the problem. But you should use UIViewController if there is no special purpose of UITableViewController.
ā€œxy.view.layoutIfNeeded()ā€
needs to be called everytime iā€™m calling a property before the view has finished loading
for example imageView.view.layoutIfNeeded() to call for the height and widths values

UIView from CGRectMake does not have the right height

I am adding a UIView to a container view programmatically, (the container view however is created in storyboard). Here is the code:
class ViewController: UIViewController{
#IBOutlet weak var dwView: UIView!
private var dwSelector = dwSelectorView()
override func viewDidLoad(){
super.viewDidLoad()
addDWSelector()
}
func addDWSelector(){
dwSelector.setTranslatesAutoresizingMaskIntoConstraints(false)
dwSelector.frame = CGRectMake(self.dwView.bounds.origin.x, self.dwView.bounds.origin.y, self.dwView.bounds.width / 2.0, self.dwView.frame.height)
println("dw height: \(self.dwView.frame.height)")
//prints 568, way too large of a value
self.dwView.addSubview(dwSelector)
}
}
The heigh of dwView is 123 in storyboard but the print state printed 568 and so now this is what it looks like:
You should always not rely on -(void)viewDidLoad since view bounds is incorrect at this point or - (void)viewWillAppear if you are using auto layout to set your view's frame. If you layout view in UIViewController, viewDidLayoutSubviews() is a appropriate place, if you layout subviews in UIView, it is layoutSubviews().
Check this article to get more details:Where to progmatically lay out views in iOS 5 (and handling orientation changes)
have you tried to call addDWSelector() in viewWillAppear()?

Resources