[Swift] I tried to implement Instagram profile-like UI with UICollectionView only with code without storyboard

Introduction

This time, I will implement an Instagram profile-like UI with UICollectionView with just code. Is the content

I hope this article helps someone

It's just that Swift beginners imitated the UI, so I think there are some points that can not be reached, If you notice any mistakes such as how to write the code, please give me some advice! !!

Three months before I started writing, Piempaon doesn't stop

Completed form

last_qiita.gif

[Instagrammer who really longs for entrepreneurs]

Target audience

・ For beginners of iOS application development ・ Those who want to know how to use UICollectionView ・ Those who want to develop without using StoryBoard ・ Those who like Instagram UI

Development environment

・ Version 11.3 (11C29) ・ Swift 5

Completed version Github

I will put the source code below

https://github.com/Isseymiyamoto/FamousAppUI/tree/master/Instagram_profile/Instagram_profile

file organization

This time, we will add new files in the View and Controller folders because we will not perform communication such as data acquisition. ʻUtils> ʻExtensions.swift includes functions to simplify Layout-related processing, I will not describe the details in this article, so it would be helpful if you copy and paste from Github.

スクリーンショット 2020-09-17 15.46.20.png

Now let's move on to implementation

Implementation procedure

For 1 and 2, skip if you don't need TabBar

  1. Create a controller file to use
  2. Use UITabBarController to determine the Controller to display as TabBar
  3. In SceneDelegate.swift, determine the Controller to be displayed at startup
  4. Create ProfileHeaderCell in View folder
  5. Create FilterView in View folder
  6. Create a PostCell for displaying posted photos in the View folder
  7. Combined and completed! !!

1. Create a Controller file to use

Here, let's create a Controller file to work with TabBar implemented in 2. Since there are 5 tab icons to display on Instagram, create 5 files directly under the Controller folder

ProfileController.swift


import UIKit

class ProfileController: UICollectionViewController{

  override func viewDidLoad() {
    super.viewDidLoad()

  }

}

For the other 4 files, the following is fine

FeedController.swift



import UIKit

class FeedController: UIViewController{

  override func viewDidLoad() {
    super.viewDidLoad()

    //Enter the character string you want to display in the navigationBar
    navigation.item = "Post"
  }

}

2. Use UITabBarController to determine the Controller to display as TabBar

In order to link the file created in 1 with TabBar, create MainTabController.swift directly under the controller folder.

MainTabController.swift



import UIKit

class MainTabController: UITabBarController{
    
    // MARK: - Lifecycle
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        configureUI()
        configureViewControllers()
    }
        
    // MARK: - Helpers
    
    func configureUI(){
        view.backgroundColor = .white
        tabBar.tintColor = .black
    }
    
    func configureViewControllers(){
        
        let feed = FeedController()
        let nav1 = templateNavigationController(image: UIImage(systemName: "house"), rootViewController: feed)
        
        let search = SearchController()
        let nav2 = templateNavigationController(image: UIImage(systemName: "magnifyingglass"), rootViewController: search)
        
        let upload = UploadPostController()
        let nav3 = templateNavigationController(image: UIImage(systemName: "plus.app"), rootViewController: upload)
        
        let notification = NotificationController()
        let nav4 = templateNavigationController(image: UIImage(systemName: "heart"), rootViewController: notification)
        //Displayed as if 2 notifications were received
        nav4.tabBarItem.selectedImage = UIImage(systemName: "heart.fill")
        nav4.tabBarItem.badgeValue = "2"
        
        let profile = ProfileController(collectionViewLayout: UICollectionViewFlowLayout())
        let nav5 = templateNavigationController(image: UIImage(systemName: "person"), rootViewController: profile)
        
        //Decide which controller to place in the tab bar
        viewControllers = [nav1, nav2, nav3, nav4, nav5]
        
        //Initial display of profileController
        selectedIndex = 4
    }
    
    //Function to set any rootViewController, tabIcon image,Used within configureViewControllers
    func templateNavigationController(image: UIImage?, rootViewController: UIViewController) -> UINavigationController{
        let nav = UINavigationController(rootViewController: rootViewController)
        nav.tabBarItem.image = image
        nav.navigationBar.tintColor = .white
        return nav
    }
}

3. In SceneDelegate.swift, determine the Controller to be displayed at startup

Now, let's edit SceneDelegate.swift and setMainTabController to be displayed at startup.

SceneDelegate.swift



class SceneDelegate: UIResponder, UIWindowSceneDelegate {

  //abridgement
  
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let scene = scene as? UIWindowScene else { return }
        window = UIWindow(windowScene: scene)
        window?.rootViewController = MainTabController()
        window?.makeKeyAndVisible()
    }

  //abridgement
}

It is perfect if the following is displayed when you start the Simulator after setting here スクリーンショット 2020-09-28 20.04.35.png

4. Create ProfileHeaderCell in View folder

Next, let's create a View to apply to the Profile Controller. I will make it in the following format, but first let's make a ProfileHeaderCell of the profile outline part here

スクリーンショット 2020-09-28 20.17.06.png

Create a file of ProfileHeader.swift directly under View

ProfileHeader.swift


import UIKit

//It doesn't have to be because it's a haribote
protocol ProfileHeaderDelegate: class {
    func handleEditProfile(_ header: ProfileHeader)
}

class ProfileHeader: UICollectionViewCell{
    
    // MARK: - Properties
    
    //It doesn't have to be because it's a haribote
    weak var delegate: ProfileHeaderDelegate?
    
    private let profileImageView: UIImageView = {
        let iv = UIImageView()
        iv.contentMode = .scaleAspectFit
        iv.clipsToBounds = true
        iv.image = UIImage(named: "Please put a photo appropriately")
        iv.layer.borderColor = UIColor.black.cgColor
        return iv
    }()
    
    private lazy var postCountButton = makeStatsButton(withNumber: "12")
    private lazy var followingCountButton = makeStatsButton(withNumber: "320")
    private lazy var followerCountButton = makeStatsButton(withNumber: "1000")
    
    private lazy var postCountLabel = makeStatsTitle(withTitle: "Post")
    private lazy var followingCountLabel = makeStatsTitle(withTitle: "following")
    private lazy var followerCountLabel = makeStatsTitle(withTitle: "Follower")
    
    private let fullnameLabel: UILabel = {
        let label = UILabel()
        label.text = "Onamae"
        label.font = UIFont.boldSystemFont(ofSize: 14)
        return label
    }()
    
    private let bioLabel: UILabel = {
        let label = UILabel()
        label.text = "This is an attempt to imitate the UI of Instagram profile. That's right. It just imitates."
        label.font = UIFont.systemFont(ofSize: 14)
        label.numberOfLines = 3
        return label
    }()
    
    private let editProfileButton: UIButton = {
        let button = UIButton(type: .system)
        button.setTitle("Edit profile", for: .normal)
        button.setTitleColor(.black, for: .normal)
        button.addTarget(self, action: #selector(handleEditProfileButtonTapped), for: .touchUpInside)
        button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 14)
        button.layer.borderColor = UIColor.lightGray.cgColor
        button.layer.borderWidth = 1
        button.layer.cornerRadius = 4
        button.backgroundColor = .white
        return button
    }()
    
    private let storiesPlusButton: UIButton = {
        let button = UIButton(type: .system)
        button.setImage(UIImage(systemName: "plus"), for: .normal)
        button.tintColor = .black
        button.backgroundColor = .clear
        button.layer.borderColor = UIColor.lightGray.cgColor
        button.layer.borderWidth = 0.75
        return button
    }()
    
    private let storiesPlusLabel: UILabel = {
        let label = UILabel()
        label.text = "New"
        label.textAlignment = .center
        label.font = UIFont.systemFont(ofSize: 10)
        return label
    }()
    
    // MARK: - Lifecycle
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        backgroundColor = .systemGroupedBackground
        
        let postCountStack = makeStatsStackView(button: postCountButton, label: postCountLabel)
        let followingCountStack = makeStatsStackView(button: followingCountButton, label: followingCountLabel)
        let followerCountStack = makeStatsStackView(button: followerCountButton, label: followerCountLabel)
        
        let infoStack = UIStackView(arrangedSubviews: [postCountStack, followingCountStack, followerCountStack])
        infoStack.axis = .horizontal
        infoStack.alignment = .center
        infoStack.distribution = .fillEqually
        
        addSubview(profileImageView)
        profileImageView.anchor(top: safeAreaLayoutGuide.topAnchor, left: leftAnchor, paddingTop: 16, paddingLeft: 16)
        profileImageView.setDimensions(width: 96, height: 96)
        profileImageView.layer.cornerRadius = 96 / 2
        
        addSubview(infoStack)
        infoStack.centerY(inView: profileImageView)
        infoStack.anchor(left: profileImageView.rightAnchor, right: rightAnchor, paddingLeft: 16, paddingRight: 32)
        
        addSubview(fullnameLabel)
        fullnameLabel.anchor(top: profileImageView.bottomAnchor, left: leftAnchor, right: rightAnchor, paddingTop: 16, paddingLeft: 16, paddingRight: 16)
        
        addSubview(bioLabel)
        bioLabel.anchor(top: fullnameLabel.bottomAnchor, left: leftAnchor, right: rightAnchor, paddingTop: 4, paddingLeft: 16, paddingRight: 16)
        
        addSubview(editProfileButton)
        editProfileButton.anchor(top: bioLabel.bottomAnchor, left: leftAnchor, right: rightAnchor, paddingTop: 16, paddingLeft: 16, paddingRight: 16 )
        
        addSubview(storiesPlusButton)
        storiesPlusButton.anchor(top: editProfileButton.bottomAnchor, left: leftAnchor, paddingTop: 16, paddingLeft: 16)
        storiesPlusButton.setDimensions(width: 64, height: 64)
        storiesPlusButton.layer.cornerRadius = 64 / 2
        
        addSubview(storiesPlusLabel)
        storiesPlusLabel.centerX(inView: storiesPlusButton)
        storiesPlusLabel.anchor(top: storiesPlusButton.bottomAnchor, paddingTop: 4)
    }
    
    
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: - Selectors
    
    //It doesn't have to be because it's a haribote
    @objc func handleEditProfileButtonTapped(){
        delegate?.handleEditProfile(self)
    }
    
    
    // MARK: - Helpers
    
    //For creating StackView that aligns the number of buttons and details vertically
    fileprivate func makeStatsStackView(button: UIButton, label: UILabel) -> UIStackView{
        let stack = UIStackView(arrangedSubviews: [button, label])
        stack.axis = .vertical
        stack.alignment = .center
        stack.setDimensions(width: 160, height: 40)
        return stack
    }
    
    //For creating display buttons such as the number of posts and followers
    private func makeStatsButton(withNumber number: String) -> UIButton{
        let button = UIButton(type: .system)
        button.setTitle(number, for: .normal)
        button.setTitleColor(.black, for: .normal)
        button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
        return button
    }
    //For creating labels to display details such as the number of posts and followers
    private func makeStatsTitle(withTitle title: String) -> UILabel{
        let label = UILabel()
        label.text = title
        label.textAlignment = .center
        label.font = UIFont.systemFont(ofSize: 14)
        return label
    }
}

Let's apply this to Profile Controller (skip it once in 5 and 6)

ProfileController.swift


import UIKit

private let profileHeaderCell = "ProfileHeaderCell"

class ProfileController: UICollectionViewController{
    
    // MARK: - Lifecycle
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        configureUI()
    }
    
    // MARK: - Selectors
    
    //Ornament
    @objc func handleRightButtonTapped(){
        print("DEBUG: you pressed the button..")
    }
    
    @objc func handleRefresh(){
        //Do nothing because there is no data
        collectionView.refreshControl?.beginRefreshing()
        collectionView.refreshControl?.endRefreshing()
    }
    
    // MARK: - Helpers
    
    //Overall UI settings
    func configureUI(){
        view.backgroundColor = .systemGroupedBackground
        
        configureNavigationBar()
        configureCollectionView()
        
        //Swipe down to reload wind settings
        let refreshControl = UIRefreshControl()
        refreshControl.addTarget(self, action: #selector(handleRefresh), for: .valueChanged)
        collectionView.refreshControl = refreshControl
    }
    
    //Settings related to navigationBar
    func configureNavigationBar(){
        navigationController?.navigationBar.tintColor = .black
        navigationController?.navigationBar.barTintColor = .systemGroupedBackground
        navigationController?.navigationBar.isTranslucent = false
        navigationController?.navigationBar.shadowImage = UIImage()
        
        navigationItem.title = "user_id"
        navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "line.horizontal.3"), style: .plain, target: self, action: #selector(handleRightButtonTapped))
    }
    
    func configureCollectionView(){
        collectionView.backgroundColor = .systemGroupedBackground
        
        //Profile Header registration
        collectionView.register(ProfileHeader.self, forCellWithReuseIdentifier: profileHeaderCell)
        
        //Place collectionView so that it does not cover tabBar
        guard let tabHeight = tabBarController?.tabBar.frame.height else { return }
        collectionView.contentInset.bottom = tabHeight
    }
}

// MARK: - UICollectionViewDataSource / UICollectionViewDelegate

extension ProfileController{
    
    //Set the number of sections to 1 for the time being → Change to 2 after setting PostCell
    override func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
    //Number of cells to be displayed in the section → Since only one Profile Header is required, 1
    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 1
    }
    
    //Setting of cell to display
    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: profileHeaderCell, for: indexPath) as! ProfileHeader
        return cell
    }
}

// MARK: - UICollectionViewDelegateFlowLayout

extension ProfileController: UICollectionViewDelegateFlowLayout{
    
    //Cell size setting → Please change the height appropriately
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        
        return CGSize(width: view.frame.width, height: 340)
    }
}

If you start up the Simulator so far, the Profile Header part will be displayed! Maybe! Let's finish from here to the end quickly

5. Create FilterView in View folder

FilterView is a filter part for displaying your own post list or a list tagged by your friends. Display as header with indexPath.section = 1 on ProfileController

Now, for FilterView, create it by installing UICollectionView inside UICollectionReusableView. Yes, that is, the FilterViewCell of UICollectionViewCell is also created in a separate file. let's do our best

ProfileFilterView.swift


import UIKit

private let profileHeaderCellIdentifier = "profileHeaderCell"

//Haribote
protocol ProfileFilterViewDelegate: class {
    func filterView(_ view: ProfileFilterView, didSelect index: Int)
}

class ProfileFilterView: UICollectionReusableView {
    
    // MARK: - Properties
    
    //Haribote
    weak var delegate: ProfileFilterViewDelegate?
    
    //collectionView to be put on view
    lazy var collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
        cv.backgroundColor = .systemGroupedBackground
        cv.delegate = self
        cv.dataSource = self
        return cv
    }()
    
    //Animate this guy to create a nicely selected feeling
    private let underlineView: UIView = {
        let view = UIView()
        view.backgroundColor = .black
        return view
    }()
    
    //Borderline with profileHeaderCell
    private let abovelineView: UIView = {
        let view = UIView()
        view.backgroundColor = .lightGray
        return view
    }()
    
    // MARK: - Lifecycle
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        collectionView.register(ProfileFilterCell.self, forCellWithReuseIdentifier: identifier)
        
        //IsSelected at initialization=Determine which cell to be true
        let selectedIndexPath = IndexPath(row: 0, section: 0)
        collectionView.selectItem(at: selectedIndexPath, animated: true, scrollPosition: .left)
        
        addSubview(collectionView)
        //Expand the collection view to fill the parent view
        collectionView.addConstraintsToFillView(self)
    }
    
    override func layoutSubviews() {
        addSubview(abovelineView)
        abovelineView.anchor(left: leftAnchor, bottom: topAnchor, width: frame.width, height: 0.5)
        
        addSubview(underlineView)
        underlineView.anchor(left: leftAnchor, bottom: bottomAnchor, width: frame.width / 2, height: 1)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

// MARK: - UICollectionViewDataSource

extension ProfileFilterView: UICollectionViewDataSource{
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        //Since there are two choices of tag or post, return 2 is ok
        return ProfileFilterOptions.allCases.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! ProfileFilterCell
        
        //Update option on cell side
        let option = ProfileFilterOptions(rawValue: indexPath.row)
        cell.option = option
        
        return cell
    }
}

// MARK: - UICollectionViewDelegate

extension ProfileFilterView: UICollectionViewDelegate{
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let cell = collectionView.cellForItem(at: indexPath)
        //0 to the x coordinate of the cell touchUpInside the underlineView.Move in 3 seconds
        let xPosition = cell?.frame.origin.x ?? 0
        
        UIView.animate(withDuration: 0.3) {
            self.underlineView.frame.origin.x = xPosition
        }
        
        //Haribote → Originally process and write so that the display image can be changed with ProfileController
        delegate?.filterView(self, didSelect: indexPath.row)
    }
}

// MARK: - UICollectionViewDelegateFlowLayout

extension ProfileFilterView: UICollectionViewDelegateFlowLayout{
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let count = CGFloat(ProfileFilterOptions.allCases.count)
        return CGSize(width: frame.width / count, height: frame.height)
    }
    
    //Installed so that there are no gaps between items
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }
}

ProfileFilterViewCell.swift


import UIKit

//Posts or tagging Post list Determine which one
enum ProfileFilterOptions: Int, CaseIterable{
    case post
    case tag
    
    var systemImage: UIImage? {
        switch self {
        case .post: return UIImage(systemName: "rectangle.split.3x3")
        case .tag: return UIImage(systemName: "person.crop.rectangle")
        }
    }
}

class ProfileFilterViewCell: UICollectionViewCell{
    
    // MARK: - Properties
    
    //Post or tag post list Set to change the image of imageView when either or Nen is updated
    var option: ProfileFilterOptions! {
        didSet{ imageView.image = option.systemImage }
    }
    
    private var imageView: UIImageView = {
        let iv = UIImageView()
        return iv
    }()
    
    //Change tintColor depending on whether it is selected or not
    override var isSelected: Bool {
        didSet{
            imageView.tintColor = isSelected ? .black : .lightGray
        }
    }
    
    // MARK: - Lifecycle
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        addSubview(imageView)
        imageView.tintColor = .lightGray
        imageView.setDimensions(width: 24, height: 24)
        imageView.center(inView: self)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Now, I'm out of breath to write an article, but let's move on to step 6.

6. Create a PostCell for displaying posted photos in the View folder

Finally, let's quickly create a cell that just displays a photo! !!

PostCell.swift


import UIKit

class PostCell: UICollectionViewCell{
    
    // MARK: - Properties
    
    let postImageView: UIImageView = {
        let iv = UIImageView()
        iv.contentMode = .scaleAspectFill
        iv.clipsToBounds = true
        return iv
    }()
    
    
    // MARK: - Lifecycle
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        self.layer.borderColor = UIColor.white.cgColor
        self.layer.borderWidth = 0.5
        
        addSubview(postImageView)
        postImageView.addConstraintsToFillView(self)
        postImageView.center(inView: self)
        
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

I entered life easy mode at once, so let's match everything with ProfileController at the end! !!

7. Combined and completed! !!

ProfileController.swift


import UIKit

private let filterViewIdentifier = "filterView"
private let profileHeaderCellIdentifier = "profileHeaderCell"
private let postCellIdentifier = "postCell"

class ProfileController: UICollectionViewController{
    
    // MARK: - Properties
    
    //Create a Haribote UIIMage array that you want to adapt to the post cell location
    private var imageArray: [UIImage?] =
        [UIImage(named: "jeff"), UIImage(named: "zack"), UIImage(named: "elon"), UIImage(named: "steve"),
         UIImage(named: "jeff"), UIImage(named: "zack"), UIImage(named: "elon"), UIImage(named: "steve"),
         UIImage(named: "jeff"), UIImage(named: "zack"), UIImage(named: "elon"), UIImage(named: "steve"),
         UIImage(named: "jeff"), UIImage(named: "zack"), UIImage(named: "elon"), UIImage(named: "steve")]
    
    
    // MARK: - Lifecycle
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        configureUI()
    }
    
    
    // MARK: - Selectors
    
    @objc func handleRightButtonTapped(){
        print("DEBUG: you pressed the button..")
    }
    
    @objc func handleRefresh(){
        //Do nothing because there is no data
        collectionView.refreshControl?.beginRefreshing()
        collectionView.refreshControl?.endRefreshing()
    }
    
    // MARK: - Helpers
    
    func configureUI(){
        view.backgroundColor = .systemGroupedBackground
        
        configureNavigationBar()
        configureCollectionView()
        
        let refreshControl = UIRefreshControl()
        refreshControl.addTarget(self, action: #selector(handleRefresh), for: .valueChanged)
        collectionView.refreshControl = refreshControl
    }
    
    
    func configureNavigationBar(){
        navigationController?.navigationBar.tintColor = .black
        navigationController?.navigationBar.barTintColor = .systemGroupedBackground
        navigationController?.navigationBar.isTranslucent = false
        navigationController?.navigationBar.shadowImage = UIImage()
        
        navigationItem.title = "user_id"
        navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "line.horizontal.3"), style: .plain, target: self, action: #selector(handleRightButtonTapped))
        
    }
    
    func configureCollectionView(){
        collectionView.backgroundColor = .systemGroupedBackground
        
        collectionView.register(ProfileHeader.self, forCellWithReuseIdentifier: profileHeaderCellIdentifier)
        collectionView.register(PostCell.self, forCellWithReuseIdentifier: postCellIdentifier)
        
        collectionView.register(ProfileFilterView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: filterViewIdentifier)
        
        guard let tabHeight = tabBarController?.tabBar.frame.height else { return }
        collectionView.contentInset.bottom = tabHeight
        
        //Assimilate FilterView with navigationBar when scrolling
        guard let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else { return }
        flowLayout.sectionHeadersPinToVisibleBounds = true
    }
}

// MARK: - UICollectionViewDataSource

extension ProfileController{
    
    override func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 2
    }
    
    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        switch section {
        case 0:
            return 1
        default:
            //Place as many cells as you want to display
            return imageArray.count
        }
    }
    
    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        switch indexPath.section {
        case 0:
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: profileHeaderCellIdentifier, for: indexPath) as! ProfileHeader
            return cell
        default:
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: postCellIdentifier, for: indexPath) as! PostCell
            //Assign to image of cell
            cell.postImageView.image = imageArray[indexPath.row]
            return cell
        }
    }
    
    override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        //ProfileFilterView registration as header
        let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: filterViewIdentifier, for: indexPath) as! ProfileFilterView
        return header
    }
}

// MARK: - UICollectionViewDelegate

extension ProfileController{
    override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        if(indexPath.section == 1){
          print("DEBUG: this item is \(indexPath.row)")
        }
    }
}
// MARK: - UICollectionViewDelegateFlowLayout

extension ProfileController: UICollectionViewDelegateFlowLayout{
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        switch section {
        case 0:
            return CGSize(width: 0, height: 0)
        default:
            let height: CGFloat = 50
            return CGSize(width: view.frame.width, height: height)
        }
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        switch indexPath.section {
        case 0:
            let height: CGFloat = 340
            return CGSize(width: view.frame.width, height: height)
        default:
            //3 columns display, square size
            let size = view.frame.width / 3
            return CGSize(width: size, height: size)
        }
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }
}

Finally

How about that? Have you completed the Instagram profile-like Haribote UI?

Towards the end, I'm getting tired and less talkative. I would like to add that if you let me know in the comments, such as where the explanation is insufficient.

Or rather, I'm sorry if there is a complaint that it doesn't work even if I copy it. I'll fix it right away.

Well then!

Recommended Posts

[Swift] I tried to implement Instagram profile-like UI with UICollectionView only with code without storyboard
How to implement UICollectionView in Swift with code only
I tried using Realm with Swift UI
I tried to implement ModanShogi with Kinx
[iOS] I tried to make a processing application like Instagram with Swift
I tried to implement file upload with Spring MVC
[Java 11] I tried to execute Java without compiling with javac
I tried to implement Stalin sort with Java Collector
[Swift] I tried to implement exception handling for vending machines
[Swift] I tried to implement the function of the vending machine
I tried to implement the image preview function with Rails / jQuery
I tried to implement flexible OR mapping with MyBatis Dynamic SQL
I tried to interact with Java
[Ruby] I tried to diet the if statement code with the ternary operator
I tried migrating Processing to VS Code
I tried to get started with WebAssembly
[Swift] Easy to implement modal with PanModal
I tried to implement the Iterator pattern
Swift beginners tried to implement microwave logic!
I tried to implement a function equivalent to Felica Lite with HCE-F of Android
I tried to verify AdoptOpenJDK 11 (11.0.2) with Docker image
I tried to make Basic authentication with Java
I tried to implement polymorphic related in Nogizaka.
I tried to manage struts configuration with Coggle
I tried to manage login information with JMX
Swift beginners tried to implement vending machine logic!
I tried to implement a server using Netty
I tried to break a block with java (1)
I tried what I wanted to try with Stream softly.
I tried to read and output CSV with Outsystems
I tried to implement Firebase push notification in Java
I started MySQL 5.7 with docker-compose and tried to connect
I tried to get started with Spring Data JPA
I tried to draw animation with Blazor + canvas API
[Java] I tried to implement Yahoo API product search
I tried to implement the Euclidean algorithm in Java
roman numerals (I tried to simplify it with hash)
When I tried to scroll automatically with JScrollBar, the event handler was drawn only once.