Collection View with self sizing cells not scrollable horizontally anymore - ios

I set up a collection view and got all working with fixed height and width for the cells.
Now I would like to have my Cells self sized, so I found that I can add
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.estimatedItemSize = CGSize(width: 200, height: 50)
menuCollectionView.collectionViewLayout = layout
}
With above code my cell resizes by the width (as wanted), but my collection view is not scrollable horizontally anymore, but instead lays out the cells vertically.
Is estimatedItemSize only thought to work for vertical scrolling? What is wrong with my approach?

The default scroll direction of a UICollectionViewFlowLayout is vertical, so you need to set the scrollDirection for the new layout, too:
layout.scrollDirection = UICollectionViewScrollDirection.Horizontal

Related

Changing frame and contentInset of UICollectionView in layoutSubviews() using IGListKit

I need to change the frame and top content inset (contentInset.top) of my collection view (which is a UICollectionView). The top inset and frame changes depend on the bounds of the superview and the content offset of the collection view, thus I put the inset-changing code in layoutSubviews().
override func layoutSubviews() {
super.layoutSubviews()
collectionView.collectionViewLayout.invalidateLayout()
collectionView.contentInset.top = new_inset_top
collectionView.frame = new_frame
}
However, the collection view does not account for the new insets and the log shows the following:
The behavior of the UICollectionViewFlowLayout is not defined because:
the item height must be less than the height of the UICollectionView
minus the section insets top and bottom values, minus the content
insets top and bottom values.
How can I fix this to let the collection view displays correctly?
I am using IGListKit
You do not need to do all these inside of layoutSubviews.
in viewDidLoad implement it like following
func viewDidLoad() {
super.viewDidLoad()
// let say you want 40px top inset
collectionView.contentInset = UIEdgeInsetsMake(40, 0, 0, 0)
}
Another thing is that, i don't get, why are you setting the frame in layoutSubviews. There is no need to set frame to change the contentInset.

Change scrollDirection on a subclass of UICollectionViewFlowLayout

I have a collectionView that scrolls vertically. Inside the cells of that collectionView is another collectionView that I have a custom layout for. The layout is currently a subclass of UICollectionViewFlowLayout and I want the scroll direction to be horizontal. I have tried a variety of ways to set the scrollDirection to horizontal but none of them are working. How do I set scroll direction on a subclass of FlowLayout?
You can easily change scroll direction by this code:
if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .horizontal
}

UICollectionView horizontal columns with vertical scroll

I'm trying to get a UICollectionView to display horizontal columns when on iPad.
This is my current layout when on iPhone (Collection view scrolls vertically):
Each UICollectionViewCell has a dynamic height depending on the content but the width is set to 100% of the screen width.
The cell height is set like this:
if let flowLayout = collectionView?.collectionViewLayout
as? UICollectionViewFlowLayout {
flowLayout.estimatedItemSize = CGSize(width: 1, height: 1)
}
It's a standard UICollectionView using the usual dataSource/delegate methods (sizeForItemAtIndexPath, cellForItemAtIndexPath etc.)
When on iPad, I'm adjusting the width depending on the number of columns required:
var numberOfColumns = 1
if screenWidth > 760 {
numberOfColumns = 2
}
let cellWidth = screenWidth/numberOfColumns
Adjusting the width of the columns gives me this:
But i'm trying to get something like this:
Is there a way to achieve this using UICollectionView?
A few notes about the collection view:
Layout is set to Flow
Scroll direction is vertical
Headers are a fixed height
Cell height is dynamic based on content
I need the collection view to scroll vertically if the content extends outside of the screen

UITableViewController layout not working

I am trying to position UITableView to the left side of the app, whole height but taking just 1 / 3 of the available width with the code like this:
class ViewController: UIViewController {
var tableController: UITableViewController?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableController = UITableViewController();
addChildViewController(tableController!)
self.view.addSubview(tableController!.view)
tableController!.didMove(toParentViewController: self)
}
override func viewDidLayoutSubviews() {
tableController!.view.backgroundColor = UIColor.red
tableController!.view.frame = CGRect(
x: 0,
y: 0,
width: view.bounds.width / 3,
height: view.bounds.height);
//tableController!.view.frame = view.bounds
}
}
And it looks like this:
And I don't get why the lines are not correctly aligned horizontally and it looks like being cut on the right.
If I give the view controller full width / height by uncommenting the last line, it looks better:
Question 1
Are you properly constraining the TableView in the parent view? You can set proportional constraints. Please see this post on the topic: AutoLayout to keep view sizes proportional
Side Note
For iOS 11, constraining views to layout guides will trigger warnings in your IDE. Consider embedding UI components inside another view that takes up the entire width of the parent view using Auto-resizing Masks. See the topic here: Xcode Auto Layout Resizing Issue

UIScrollView not scrolling in Swift

My UIScrollView won't scroll down. I don't know why. I already followed Apple documentation regarding to this issue.
#IBOutlet weak var scroller: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidLayoutSubviews() {
scroller.scrollEnabled = true
// Do any additional setup after loading the view
scroller.contentSize = CGSizeMake(400, 2300)
}
You need to set the frame of your UIScrollView so that it is less than the contentSize. Otherwise, it won't scroll.
Also, I would recommend that you add the following to your viewDidLoad method:
scroller.contentSize = CGSize(width: 400, height: 2300)
If you are using AutoLayout
Set content size in viewDidAppear which works for me.
override func viewDidAppear(_ animated: Bool) {
scrollView.contentSize = CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height+300)
}
Alot of the time the code is correct if you have followed a tutorial but what many beginners do not know is that the scrollView is NOT going to scroll normally through the simulator. It is suppose to scroll only when you press down on the mousepad and simultaneously scroll. Many Experienced XCode/Swift/Obj-C users are so use to doing this and so they do not know how it could possibly be overlooked by beginners. Ciao :-)
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(scrollView)
// Do any additional setup after the view
}
override func viewWillLayoutSubviews(){
super.viewWillLayoutSubviews()
scrollView.contentSize = CGSize(width: 375, height: 800)
}
This code will work perfectly fine as long as you do what I said up above
Do not give fix height to scroll view and always give top of first subview to scrollview and bottom of last subview to scrollview. By this way scroll view will automatically grow as per the size of contained subviews. No need to give contentSize to the scrollview.It will work for small as well as large size iPhone.
Swift 3.0 version
scroller.contentSize = CGSize(width: scroller.contentSize.width, height: 2000)
If you are using autolayout, then the contentSize property stops working and it will try to infer the content size from the constraints. If that is the case, then your problem could be that you are not defining the necessary constraints to the content view so that the scrollview can infer the content size.
You should define the constraints of your content view to the top and bottom edges of the scrollview.
If you are using Storyboard:
Put your Content view inside the UIScrollView
Add top, bottom, left and right constraints with the scroll view
Add equal heights and widths constraints
For a vertical scroll set the Equal Heights Constraint priority to 250. For a horizontal scroll set the Equal Widths Constraint priority to 250
In my case, I used UIStackView inside UIScrollView.
Added some views-elements from code to stackview.
It won't scroll.
Fixed it by setting stackview's userInteractionEnabled to false.
The problem could be that your scrollView doesn't know its contentSize like stated above, but the fix is easier than what the above answers are. Like Carlos said but I will elaborate more. If you want your scrollView to scroll vertically(up & down), make your contentView which is in the hierarchy of the scrollView equal width to the ViewController and give it a height constraint that works for your project i.e. 700. For the opposite(horizontally) make the height equal to the ViewController and the width some big number that works for your project.
FWIW, I found that I needed to use sathish's solution from above, though it was insufficient to effect the intervention in viewDidAppear alone. I had to instead make the adjustment for every new content assignment:
func display(pattern: Pattern) {
let text : NSAttributedString = pattern.body()
patternTextView.attributedText = text
// Set the size of the view. Autolayout seems to get in the way of
// a correct size calculation
patternTextView.contentSize = CGSize(width: 348, height: 620)
}
The manifest constants (yeah, I know, yuk, but it makes it easier to understand here) are from the autolayout spec.
It worked for me. In Size Inspector
Layout = Translates Mask into constraints.
Autoresizing = all click.
For Swift 5.6 and iOS 15:
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
let subView: UIView = UILabel()
subView.text = String(repeating: "MMMMMMM ", count: 100)
NSLayoutConstraint.activate([
subView.topAnchor.constraint(equalTo: scrollView.topAnchor),
subView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
subView.leftAnchor.constraint(equalTo: scrollView.leftAnchor),
subView.rightAnchor.constraint(equalTo: scrollView.rightAnchor),
// Constrain width so the label text wraps and we scroll vertically.
subView.widthAnchor.constraint(lessThanOrEqualTo: scrollView.widthAnchor),
])
Increase the content Height work for me.
I do not know it is a good solution, but you can try to set headerview to empty UITableView.
let scrollView: UIView = UIView(frame: CGRectMake(0, 0, 400, 2300))
tableView.tableHeaderView = scrollView

Resources