How can I make this kind of scroll menu?

I have added Scroll View and Stack View in it, but it does not seems to be the solution. I would like to have an effect like below: One photo and 3 labels under the photo in a container. I have 12 items like this, that is why I need ScrollView. Any ideas how to make it step by step? Here is the solution (airbnb app) I would like to have (I needed to put it on imgur, because there was an error, while adding a photo here):

https://imgur.com/pFY3DX5

Answered by Claude31 in 696109022

So do I need scrollView than if I have more items to place? 

No, collectionView already manages the scrolling. You will have more items than you can display, and CollectionView will automatically take care.

  • To create a CollectionView, just drag the object in InterfaceBuilder inside your VC.
  • Then declare the class as the dataSource and Delegate.
  • In code, declare that class conforms to UICollectionViewDelegate and UICollectionViewDataSource.

You will be asked to provide some func to conform to protocols. Follow steps.

  • Then create a UICollectionViewCell class (with its xib).
  • Define all elements (image, labels) inside.

Code in class would be like:

class FirstCollectionViewCell: UICollectionViewCell {
    
    @IBOutlet weak var imageCell: UIImageView!
    @IBOutlet weak var title: UILabel!
    @IBOutlet weak var subTitle: UILabel!
    @IBOutlet weak var dateOfPublish: UILabel!
    
    // MARK: - View LifeCycle
    
    override func awakeFromNib() {
        
        super.awakeFromNib()
        // Initialization code
    }
    
}
  • In the class that contains the Collection, register the nib:
        let nib = UINib(nibName: "FirstCollectionViewCell", bundle: nil)   // FirstCollectionCell is name of Nib, also name of class
        collectionView.register(nib, forCellWithReuseIdentifier: "FirstCollectionCell")
  • Back to IB, in the storyboard where you have put the CollectionView, set the identifiers for the CollectionViewCell.

  • Set the scroll direction of CollectionViewto vertical.

  • Set the constraints so that Cell is equal width

So now, start and tell if you have problems.

You call it a menu, but that's not a menu in fact. It is simply (at first glance) a collection View.

If you know how to create a collection view, that is very easy to do, with a custom cell that contains an image and a few labels below.

what about this one?  https://developer.apple.com/documentation/swiftui/picking-container-views-for-your-content

No. It is for SwiftUI. And you said you use UIKit.

If I have 12 items to place in this view, I need to create 12 collection views or just one with 12 elements

A single collectionView of course. That's the very purpose of collectionView to provide this scrolling, selection and much more.

This tutorial may help you if needed:

https://www.raywenderlich.com/18895088-uicollectionview-tutorial-getting-started

Just don't forget to set the width of collectionView equal (with just a few more pixels) to your cell width, in order to have only one cell horizontally and get the menu effect.

Accepted Answer

So do I need scrollView than if I have more items to place? 

No, collectionView already manages the scrolling. You will have more items than you can display, and CollectionView will automatically take care.

  • To create a CollectionView, just drag the object in InterfaceBuilder inside your VC.
  • Then declare the class as the dataSource and Delegate.
  • In code, declare that class conforms to UICollectionViewDelegate and UICollectionViewDataSource.

You will be asked to provide some func to conform to protocols. Follow steps.

  • Then create a UICollectionViewCell class (with its xib).
  • Define all elements (image, labels) inside.

Code in class would be like:

class FirstCollectionViewCell: UICollectionViewCell {
    
    @IBOutlet weak var imageCell: UIImageView!
    @IBOutlet weak var title: UILabel!
    @IBOutlet weak var subTitle: UILabel!
    @IBOutlet weak var dateOfPublish: UILabel!
    
    // MARK: - View LifeCycle
    
    override func awakeFromNib() {
        
        super.awakeFromNib()
        // Initialization code
    }
    
}
  • In the class that contains the Collection, register the nib:
        let nib = UINib(nibName: "FirstCollectionViewCell", bundle: nil)   // FirstCollectionCell is name of Nib, also name of class
        collectionView.register(nib, forCellWithReuseIdentifier: "FirstCollectionCell")
  • Back to IB, in the storyboard where you have put the CollectionView, set the identifiers for the CollectionViewCell.

  • Set the scroll direction of CollectionViewto vertical.

  • Set the constraints so that Cell is equal width

So now, start and tell if you have problems.

do you mean constraints for cell in collection view?

I mean is that in the cell xib, you define its size ; you can also constraint its width. For instance 400.

In storyboard, select the collectionView, add new Constraints to set the width for instance to 420.

@Claude31, for now I think Im doing fine with the collectionView. I will put a tick, when I'll finish. How can I input distance from some point in the label? I have locationLabel in this ViewController (In the cell in CollectionView), but I would like to input the distance in kilometers between user current location and from the point in Struct, which is in another ViewController called simply 'ViewController':

This is this struct from another ViewController:

    struct Place {

      let name: String

      let description: String

      let typ: String

      let lattitude: CLLocationDegrees

      let longtitude: CLLocationDegrees

        var coordinate: CLLocationCoordinate2D {

            .init(latitude: lattitude, longitude: longtitude)

        }

    }

    let places = [Place(name: "one", description: "desc_one", typ: "one", lattitude: 81.108187, longtitude: 37.075812),

                   Place(name: "two", description: "two", typ: "two", lattitude: 31.076187, longtitude: 87.000563),

                   Place(name: "Three", description: "three", typ: "three", lattitude: 31.096688, longtitude: 26.991312),

This is a very different question, more for CoreLocation area.

So, you should close this thread and start a new one.

But a quick answer:

Just import CoreLocation and use:

let loc1 = CLLocation(latitude: 81.108187, longitude: 37.075812)
// Or : let loc1 = CLLocation(places[0].lattitude, places[0].longitude)
let loc2 = CLLocation(latitude: 31.076187, longitude: 87.000563)
// Or : let loc2 = CLLocation(places[1].lattitude, places[1].longitude)
let loc3 = CLLocation(latitude: 31.096688, longitude: 26.991312)
// Or : let loc3 = CLLocation(places[2].lattitude, places[2].longitude)

let dist1_2 = loc2.distance(from: loc1) / 1000 // In km
let dist2_3 = loc3.distance(from: loc2) / 1000 // In km
print("dist1_2 =", dist1_2, "km")
print("dist2_3 =", dist2_3, "km")

You will get:

  • dist1_2 = 5953.932994696864 km
  • dist2_3 = 5650.435157084044 km

Note: you'd better rename lattitude into latitude

Ok, so far I got this, and the layout is ride off:

import UIKit

class TrasyOpisyController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return nazwy_modernizm.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ModernizmCollectionViewCell", for: indexPath as IndexPath) as! ModernizmCollectionViewCell
        cell.imageCell.image=self.images_modernizm[indexPath .row]
        cell.title.text=self.names_modernizm[indexPath .row]
        cell.subTitle.text=self.opisy_modernizm[indexPath .row]
        //cell.locationLabel.text=self.czas_rower[indexPath .row]
        return cell
    }
    
    @IBOutlet weak var collectionView: UICollectionView!
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath){
        collectionView.deselectItem(at: indexPath, animated: true) //this does not work
    }
    

    
    let names_modernizm = [("One"),("Two"),("Three")]
    let opisy_modernizm = [("one"),("two"),("three")]
    let images_modernizm = [UIImage(named: "one.jpeg"), UIImage(named: "two.jpeg"), UIImage(named: "three.jpeg")]
    


    
    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.delegate = self
        collectionView.dataSource = self
       // collectionView.collectionViewLayout = UICollectionViewFlowLayout()
       
    }
    
    
    
}

//extension ViewController: UICollectionViewDelegateFlowLayout{
  //  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
   //     return CGSize(width: 10, height: 10)
   // }
//} // I was trying with this, but it gets worst

And ModernizmCollectionViewCell:

import UIKit

class ModernizmCollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var imageCell: UIImageView!
    @IBOutlet weak var iconLabel: UIImageView!
    @IBOutlet weak var title: UILabel!
    @IBOutlet weak var subTitle: UILabel!
    @IBOutlet weak var locationLabel: UILabel!
    
    
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

}

In storyboard it looks fine, but when I run the app everything rides off:

So, what is wrong exactly ?

It looks like :

  • you have not set width horizontal on the CollectionView, through leading and trailing to the safe area.

-> Can you show the constraints you have defined for CollectionView

  • For imageCell

-> have you defined (in IB Attributes Inspector) the view Content Mode as 'Aspect fit` ?

  • Label is too large, because you have not define horizontal constraints.

-> Logically, you should defined a leading to its container (10 for instance) and a trailing of 60 or 80, to make room for iconLabel

-> To make sure that text will fit, select a variable font, with minimum size of 0.6 for instance (in Attributes inspector for Label)

-> Can you show the constraints you have defined for Label

-> Can you show the constraints you have defined for iconLabel

Can you list me what should I do with the constraints step by step?

I thought I did it in my previous post…

To be efficient, send a screen shot of the constraints you have defined, inside the cell (for each object inside) and for the collectionView (the collection itself and its cell). I will inspect them.

Also, should the cell be at full width or it can stay as it was in previeous storyboard screenshot that I've sent? 

It seems that the screenshot is in the preview, it is not the storyboard screenshot of the cell.

Ok that was the one I was looking at. But it seems to be defined directly in the collection, not in the cell nib. Anyway, this thread is now too long. You should close it on my answer advising to use a collectionview and start a new thread for the cell layout.

How can I make this kind of scroll menu?
 
 
Q