Pavan Vasan
Pavan Vasan

Reputation: 407

Having some UITableViews and some UICollectionViews in a single screen

I am currently working on a particular problem where I have to add a UITableView and a couple of UICollectionViews along with a couple of labels in a single screen.

Here is the mockup:-

The screen I need to build

Right now, This is how my view looks (I am just working on the UI for now):-

The current screen I have built

The UICollectionViews below 'Live now' and 'Related Stories' are horizontally scrollable and in the middle of those UICollectionViews is a UITableView

Rather than having to compress these subviews inside the UIViewController class that I have built, I wish to remake it in such a way that the whole view is scrollable while keeping the same scrolling experience of the subviews currently set in the view.

I considered using another UITableview that encompasses all the views(the collection views and the table view), but then, the scrolling of that particular table view would cause a bad scrolling experience with the table view that I have to add.

The above could be said for using a UIScrollView (UICollectionViews that would be added would have no problem since they are being scrolled horizontally).

Would it be best to use a UICollectionView?

Any Suggestion that could help is welcome. Do let me know if there is anything from my side

Here is my source code:

import UIKit
import SnapKit
import EasyPeasy

class ArticleViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UITableViewDelegate, UITableViewDataSource {

var liveLabel = UILabel()
var engageLabel = UILabel()
var storyLabel = UILabel()

var livelayout = UICollectionViewFlowLayout.init()
var storylayout = UICollectionViewFlowLayout.init()
var liveCollectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout.init())
var storyCollectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout.init())

var liveRows = [
    "https://images.sportsflashes.com/australia-win-first-t20i-after-nail-biting-finish-at-vizag1551028137.jpg",
    "https://images.sportsflashes.com/australia-win-first-t20i-after-nail-biting-finish-at-vizag1551028137.jpg",
    "https://images.sportsflashes.com/australia-win-first-t20i-after-nail-biting-finish-at-vizag1551028137.jpg",
    "https://images.sportsflashes.com/australia-win-first-t20i-after-nail-biting-finish-at-vizag1551028137.jpg",
    "https://images.sportsflashes.com/australia-win-first-t20i-after-nail-biting-finish-at-vizag1551028137.jpg",
    "https://images.sportsflashes.com/australia-win-first-t20i-after-nail-biting-finish-at-vizag1551028137.jpg",
    "https://images.sportsflashes.com/australia-win-first-t20i-after-nail-biting-finish-at-vizag1551028137.jpg"
]

var articleRows = [
    "https://c.ndtvimg.com/2019-03/6dkbrsrg_varun-dhawan_625x300_14_March_19.jpg",
    "https://c.ndtvimg.com/2019-03/6dkbrsrg_varun-dhawan_625x300_14_March_19.jpg",
    "https://c.ndtvimg.com/2019-03/6dkbrsrg_varun-dhawan_625x300_14_March_19.jpg",
    "https://c.ndtvimg.com/2019-03/6dkbrsrg_varun-dhawan_625x300_14_March_19.jpg",
    "https://c.ndtvimg.com/2019-03/6dkbrsrg_varun-dhawan_625x300_14_March_19.jpg",
    "https://c.ndtvimg.com/2019-03/6dkbrsrg_varun-dhawan_625x300_14_March_19.jpg",
    "https://c.ndtvimg.com/2019-03/6dkbrsrg_varun-dhawan_625x300_14_March_19.jpg",
    "https://c.ndtvimg.com/2019-03/6dkbrsrg_varun-dhawan_625x300_14_March_19.jpg",
    "https://c.ndtvimg.com/2019-03/6dkbrsrg_varun-dhawan_625x300_14_March_19.jpg"
]

var storyRows = [
    "https://www.anime-planet.com/images/characters/takuma-mamizuka-83947.jpg",
    "https://www.anime-planet.com/images/characters/takuma-mamizuka-83947.jpg",
    "https://www.anime-planet.com/images/characters/takuma-mamizuka-83947.jpg",
    "https://www.anime-planet.com/images/characters/takuma-mamizuka-83947.jpg",
    "https://www.anime-planet.com/images/characters/takuma-mamizuka-83947.jpg",
    "https://www.anime-planet.com/images/characters/takuma-mamizuka-83947.jpg",
    "https://www.anime-planet.com/images/characters/takuma-mamizuka-83947.jpg",
    "https://www.anime-planet.com/images/characters/takuma-mamizuka-83947.jpg",
    "https://www.anime-planet.com/images/characters/takuma-mamizuka-83947.jpg"
]

let table = UITableView()

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.view.backgroundColor = UIColor.white

    if self.navigationController == nil {
        return
    }

    self.navigationController?.isNavigationBarHidden = false
    self.navigationItem.leftBarButtonItem = UIBarButtonItem(image:(UIImage(named: "back_arrow")?.withRenderingMode(.alwaysOriginal)), style:.plain, target:self, action:#selector(backPress))

    var dateBarButtonItem = UIBarButtonItem(title: "Mar 2019", style: .plain, target: self, action: nil)
    dateBarButtonItem.tintColor = UIColor.black
    self.navigationItem.rightBarButtonItem = dateBarButtonItem

    // Create a navView to add to the navigation bar
    let navView = UIView()

    // Create the label
    let nameLabel = UILabel()
    nameLabel.text = "Pavan Vasan"
    nameLabel.sizeToFit()
    nameLabel.center = navView.center
    nameLabel.textAlignment = NSTextAlignment.center

    // Create the image view
    let image = UIImageView()
    image.image = UIImage(named: "twitter")
    // To maintain the image's aspect ratio:
    let imageAspect = image.image!.size.width/image.image!.size.height
    // Setting the image frame so that it's immediately before the text:
    image.frame = CGRect(x: nameLabel.frame.origin.x-nameLabel.frame.size.height*imageAspect, y: nameLabel.frame.origin.y, width: nameLabel.frame.size.height*imageAspect, height: nameLabel.frame.size.height)
    image.contentMode = UIView.ContentMode.scaleAspectFit
    // Add both the label and image view to the navView
    navView.addSubview(nameLabel)
    navView.addSubview(image)

    // Set the navigation bar's navigation item's titleView to the navView
    self.navigationItem.titleView = navView

    // Set the navView's frame to fit within the titleView
    navView.sizeToFit()
}

override func viewDidLoad() {
    super.viewDidLoad()
    setupLiveCollectionView()
    setupTable()
    setupStoryCollectionView()
}

func setupLiveCollectionView() {
    self.view.addSubview(self.liveLabel)
    self.liveLabel.text = "Live now"
    self.liveLabel.font = UIFont.boldSystemFont(ofSize: 17.5)
    self.liveLabel.textColor = UIColor.black
    self.liveLabel.textAlignment = .center
    self.liveLabel.easy.layout(
        Left(10).to(self.view),
        Top(75).to(self.view)
    )

    livelayout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
    livelayout.itemSize = CGSize(width: 75, height: 75)
    livelayout.scrollDirection = .horizontal

    liveCollectionView = UICollectionView(frame: CGRect(x: 0, y: 100, width: self.view.frame.width, height: 95), collectionViewLayout: livelayout)
    liveCollectionView.dataSource = self
    liveCollectionView.delegate = self
    liveCollectionView.register(LiveViewCell.self, forCellWithReuseIdentifier: "MyCell")
    liveCollectionView.backgroundColor = UIColor.white
    liveCollectionView.showsHorizontalScrollIndicator = false
    self.view.addSubview(liveCollectionView)
}

func setupStoryCollectionView() {
    self.view.addSubview(self.storyLabel)
    self.storyLabel.text = "Related Stories"
    self.storyLabel.font = UIFont.boldSystemFont(ofSize: 17.5)
    self.storyLabel.textColor = UIColor.black
    self.storyLabel.textAlignment = .center
    self.storyLabel.easy.layout(
        Left(10).to(self.view),
        Top(15).to(self.table)
    )

    storylayout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
    storylayout.itemSize = CGSize(width: 120, height: 120)
    storylayout.scrollDirection = .horizontal

    storyCollectionView = UICollectionView(frame: CGRect(x: 0, y: 250 + self.view.bounds.height*0.40, width: self.view.frame.width, height: 130), collectionViewLayout: storylayout)
    storyCollectionView.dataSource = self
    storyCollectionView.delegate = self
    storyCollectionView.register(StoryViewCell.self, forCellWithReuseIdentifier: "StoryCell")
    storyCollectionView.backgroundColor = UIColor.white
    storyCollectionView.showsHorizontalScrollIndicator = false
    self.view.addSubview(storyCollectionView)
}

func setupTable() {

    table.delegate = self
    table.dataSource = self
    table.register(ArticleTableViewCell.self, forCellReuseIdentifier: "ArticleCell")
    table.separatorStyle = .none

    self.view.addSubview(table)
    self.table.easy.layout(
        Top(15).to(self.liveCollectionView),
        Left(0).to(self.view),
        Width(self.view.bounds.width),
        Height(self.view.bounds.height*0.4)
    )
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    if collectionView == self.liveCollectionView {
        return self.liveRows.count
    } else if collectionView == self.storyCollectionView {
        return self.storyRows.count
    }
    return 0
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    if collectionView == self.liveCollectionView {
        let myCell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath as IndexPath) as! LiveViewCell
        myCell.configure(self.liveRows[indexPath.row])
        return myCell
    } else if collectionView == self.storyCollectionView {
        let myCell = collectionView.dequeueReusableCell(withReuseIdentifier: "StoryCell", for: indexPath as IndexPath) as! StoryViewCell
        myCell.configure(self.storyRows[indexPath.row])
        myCell.layer.borderColor = UIColor.black.cgColor
        myCell.layer.cornerRadius = 15
        myCell.layer.borderWidth = 0.5
        myCell.layer.masksToBounds = true
        return myCell
    }
    return UICollectionViewCell()
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return self.articleRows.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "ArticleCell", for: indexPath) as! ArticleTableViewCell
    cell.configure(self.articleRows[indexPath.row])
    return cell
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 325
}

@objc func backPress(sender:UIButton!) {
    self.navigationController?.popViewController(animated: true)
}
}

Upvotes: 0

Views: 142

Answers (4)

Chirag Kalsariya
Chirag Kalsariya

Reputation: 256

You can do it without any major changes in your code just below updates.

  1. First, need to set constraint outlet of Tableview Height. e.g consTblHeight
  2. Add Tableview size change observer.
func setupTable() {
    table.addObserver(self, forKeyPath: "contentSize", options: NSKeyValueObservingOptions.new, context: nil)
}
  1. Observer Method
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
            self.consTblHeight.constant = self.table.contentSize.height
            self.view.layoutIfNeeded()
        }

Upvotes: 0

iCoder
iCoder

Reputation: 67

Having nested scroll views could be against the HIG guidelines, which advocates not to place a scroll view inside of another scroll view.

In fact, similar to our use case, Apple has given the example of their Stocks app, where stock quotes scroll vertically above company-specific information that scrolls horizontally.

For more details, please refer to the HIG documentation at https://developer.apple.com/design/human-interface-guidelines/ios/views/scroll-views/

Upvotes: 0

RPK
RPK

Reputation: 11

You can use a single tableview for full screen and create custom cell by adding UICollectionView inside that.

Upvotes: 1

Mr Spring
Mr Spring

Reputation: 545

It would be preferable in complex screens to use only one main list view type(UITableView or UICollectionView).

In it u can add UIStackView or more UITableViews or UICollectionView.

In your case i would have used a UICollectionView and change the size of the cell according to your needs

Upvotes: 1

Related Questions