Set scrollview size and position by code - swift

I've added a scrollView in Main.storyboard and made it the same size as the view and set the backgroundcolor to blue.
But I'm having troubles with autolayout so I'm trying to deactivate the constraints by code (succeeded) and set the frame position and size with the following code inside viewDidLoad()
NSLayoutConstraint.deactivateConstraints(self.view.constraints())
self.scrollView.frame = CGRectMake(0.0, 100.0, 200.0, 200.0)
self.scrollView.backgroundColor = (UIColor.redColor())
When I run the code the colour changes to red (so I've got that going for me, which is nice) but the scrollView doesn't change position or size.
What is the right syntax to do this?

This will work if you set the frame in viewDidLayoutSubviews():
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.scrollView.frame = CGRect(x: 0.0, y: 100.0, width: 200.0, height: 200.0)
}
Trying to do this in viewDidLoad() will not work because scrollView is not yet in the view hierarchy at this point.
Note that you are only deactivating the constraints for the view and not its subviews in your code snippet. This may not be what you intended.
Also note that I'm not endorsing this as a way to fix constraint problems. ;-)

Related

How to update the size of stackView after adding subviews (programatically)?

let innerView = UIView(frame: .init(x: 0, y: 0, width: 50, height: 50))
let outerSV = UIStackView(arrangedSubviews: [innerView])
outerSV.frame.width // still 0! should be 50
outerSV.frame.height // still 0! should be 50
So the question is: how to update the size of stackView after adding subviews?
You should call sizeToFit() and layoutIfNeeded() on the subviews of the stackView. Constrain the UIStackView as you normally would, and constrain the subviews as you normally would.
Also, you need to have it set to Fill Proportionally and it will resize to fit the new content.
And Make sure you don't have a bottom constraint of a height constraint on the stackview. You should only need left, right and top constraint.

Resizing NSScrollView

How can I resize NSScrollView using Swift?
class MainViewController: NSViewController {
#IBOutlet weak var scrollView: NSScrollView!
override func viewDidLoad {
scrollView.setFrameOrigin(NSPoint(x: 340, y: 537))
scrollView.setFrameSize(CGSize(width: scrollView.contentSize.width, height: 102.0))
}
}
The above code works only if there are no constraints set. But, without constraints, the ScrollView doesn't stay in place when the window is resized.
If you are trying to set the document size (scrollable area) of a NSScrollView, then all we need to do is set the document view's size.
📌 Note: This assumes there are no constraints on the document view.
scrollView.documentView?.setFrameSize(NSSize(width: 576, height: 56))
If you are trying to scroll to a specific region, see scrollToVisible(_:).
So this worked for me since my ScrollView was positioned on the top left:
scrollView.setFrameOrigin(NSPoint(x: self.view.frame.minX + 340, y: self.view.frame.maxY - 172))
scrollView.setFrameSize(CGSize(width: scrollView.contentSize.width, height: 102.0))
And the below image shows the settings used in Size Inspector to enable auto resize and also to stay to the top-left of the window.

View is not autoresizing to meet constraints

I have a hard time wrapping my head around this problem. I have included a simple example where I set constraints to be wrapping the device. However, the view frame is not resizing based on different devices.
Iphone XR -> See how frame is different than holderView
https://imgur.com/ZyoCNGQ
Iphone 8 -> Frame is same size as holderView
https://imgur.com/S6xnzcZ
Ideally the holder height and width should change with different devices, however, that is not happening.
Your layout seems to look fine.
If they look fine in the simulator, they are fine.
I suspect that you are printing your frames before the layout gets properly set.
Try moving the debugging print statements to viewDidLayoutSubviews or viewDidAppear, from viewDidLoad.
Also, if you want to wrap the entire device, you need to set constraints around the superView, not safeAreaLayoutGuide.
Although I've done it programmatically, the following is basically the layout you have:
class ViewController: UIViewController {
var holderView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
holderView = UIView()
holderView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(holderView)
NSLayoutConstraint.activate([
holderView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
holderView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
holderView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
holderView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
])
holderView.backgroundColor = .orange
print(holderView.frame)
print(view.frame)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print(holderView.frame)
print(view.frame)
}
}
In iPhone XR,
(0.0, 0.0, 0.0, 0.0)
(0.0, 0.0, 414.0, 896.0)
(0.0, 44.0, 414.0, 818.0)
(0.0, 0.0, 414.0, 896.0)
is printed. You can see that holderView's frame is (0,0,0,0). But it gets eventually set. Since you used a storyboard, I think the initial frame size is set to the canvas frame size.

Swift text field border isn't the right width

I have a bottom border that I generated after following the answer here.
This works absolutely great except the border isn't the right width. It's set with constraints to match the width of the button below it but as you can see is coming up short.
What am I missing?
Code :
extension UITextField
{
func setBottomBorder(withColor color: UIColor)
{
self.borderStyle = UITextBorderStyle.none
self.backgroundColor = UIColor.clear
let width: CGFloat = 3.0
let borderLine = UIView(frame: CGRect(x: 0, y: self.frame.height - width, width: self.frame.width, height: width))
borderLine.backgroundColor = color
self.addSubview(borderLine)
}
}
then in the VC :
override func viewDidLoad() {
authorNameOutlet.setBottomBorder(withColor: UIColor.lightGray)
}
Then Xcode shows...
but the simulator shows...
I've tried this both setting the width of the text field to be 0.7 x the superview width (same as the button below it) and also setting the width of the text field to be equal width of the button but neither works.
This is because of AutoLayout.
You can add autoresizingMask to your line.
borderLine.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
You are working with with a static frame for the border line view. After viewDidLoad your view controller's view gets resized.
Option 1: (Fast and dirty)
Move your code from viewDidLoad() to viewWillAppear(_ animated: Bool). viewWillAppear gets called after the first layout of your view controller's view
Option 2:
Add constraint for your border line view. So that your border line view will resize automatically.
Importent hint:
Do not forget super calls in overrides or you will get strange bugs!
E.g:
override func viewDidLoad() {
super.viewDidLoad()
// your code
}

Header View behind NavigationBar [duplicate]

I have a UICollectionView that is the entire view but it lives inside "view" (it is not UICollectionViewController). Adding a cell to this collection view shows it in the following order in storyboard:
This is how it looks in the emulator:
I don't understand how to get rid of that view. All the insets are at zero in Storyboard Size Inspector for collection view. Just to be sure, I also have the following code:
override func viewWillLayoutSubviews() {
let layout = self.collectionViewProducts.collectionViewLayout as! UICollectionViewFlowLayout
let containerWidth = UIScreen.main.bounds.size.width - 40.0
let itemWidth = (containerWidth / 3.0)
let itemHeight = (itemWidth / 0.75) + 30
layout.sectionInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
layout.itemSize = CGSize(width: itemWidth, height: itemHeight)
}
How can I get rid of that top padding?
You can fix top padding issue by considering one of the following method.
Method 1: Natural way to fix your problem by setting up your collectionView dimensions properly from StoryBoard.
Method 2: **Updated**
You can validate collection frame in viewDidLayoutSubviews or viewWillLayoutSubviews
override func viewDidLayoutSubviews() {
collectionView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
}
Method 3: You can Adjust Scroll View Insets from your StoryBoard Attributes Inspector.
Method 4: You can fix this issue programatically by adjusting CollectionView contentInset.
collectionView.contentInset = UIEdgeInsets(top: **Any Value**, left: 0, bottom: 0, right: 0)
Output with top padding 5:
Output with top padding 44:
Output with top padding 64:
I think because this viewController is embedded in a navigationController. Let select this viewController in the storyboard and uncheck Adjust Scroll View Insets:
There is one more way to resolve this issue and that is selecting collectionView -> scrollView Content Insets -> "Automatic" to "Never".
By default scrollView Content Insets value is Automatic. Please check the below image.
For more details check: UIScrollView.ContentInsetAdjustmentBehavior
https://developer.apple.com/documentation/uikit/uiscrollview/2902261-contentinsetadjustmentbehavior
Make your Navigation Controller > NavigationBar translucent by unchecking the Translucent check box in IB > Attribute Inspector, and it will work.
I also had the same problem, and i fixed it with a way totally ridiculous solution.
My collectionView contained several sections which had no title & no item cells.
The top, bottom inset values of the section insets were 10 respectively.
so each empty section charged height of 20 pixels.
I had 4 empty sections, and therefore, 80 top margins in the collection view.
Hope you check this as well if none of the above solutions works.