seagatewholesale.com

Creating a Functional Dribbble App Design with UIKit

Written on

Chapter 1: Introduction to App Design

In this tutorial series, we will dive into the process of converting a digital design into a working application.

Dribbble App Design Preview

Welcome back, everyone! In our last session, we started to implement the CollectionView CompositionalLayout.

Now, let's take a deeper look into CompositionalLayout! We will initiate our journey by creating a file named Layouts.swift. This file will help us manage multiple layouts for different CollectionView cells and will be structured as a Singleton to ensure only a single instance exists throughout the app.

Inside this Singleton, we will define a function called headerSection() that returns an NSCollectionLayoutSection. This function will allow us to set up the layout for TopCell, utilizing a layout group that consists of a layout item.

import Foundation

import UIKit

class Layouts {

static let shared = Layouts() // Singleton instance

func headerSection() -> NSCollectionLayoutSection {

// Create an item that fills its container

let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0)))

// Create a vertical group with fixed height

let group = NSCollectionLayoutGroup.vertical(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(150)), subitems: [item])

// Create a section using the group

let section = NSCollectionLayoutSection(group: group)

return section

}

}

Next, we will return to HomeController and update the layout configuration in the init() function to use the Layouts singleton.

init() {

let layout = UICollectionViewCompositionalLayout(section: Layouts.shared.headerSection())

super.init(collectionViewLayout: layout)

}

Chapter 2: Developing the Services Section

Having completed the TopCell, it's time to move on to the services section. We aim to achieve a specific layout, which will require some customization. First, we will create a new file named ServiceContainer.swift. This will subclass UIView and represent an individual service, containing a UIImageView for the icon and a UILabel for the title.

class ServiceContainer: UIView {

// Property to hold the icon name and trigger UI updates

var iconName: String? {

didSet {

configData()

}

}

// Set up the initial view state

override init(frame: CGRect) {

super.init(frame: frame)

translatesAutoresizingMaskIntoConstraints = false

configViews()

configConstraints()

}

required init?(coder: NSCoder) {

fatalError("init(coder:) has not been implemented")

}

// Label for the service title

lazy var bodyLabel: UILabel = {

let label = UILabel()

label.numberOfLines = 0

label.lineBreakMode = .byWordWrapping

label.textColor = UIColor(named: "mainColor")

label.font = .preferredFont(forTextStyle: .headline)

label.translatesAutoresizingMaskIntoConstraints = false

return label

}()

// Image view for the icon

lazy var icon: UIImageView = {

let iv = UIImageView()

iv.contentMode = .scaleAspectFit

iv.translatesAutoresizingMaskIntoConstraints = false

return iv

}()

// Configure views

func configViews() {

layer.cornerRadius = 20

addSubview(bodyLabel)

addSubview(icon)

}

// Set up constraints for the views

func configConstraints() {

NSLayoutConstraint.activate([

bodyLabel.leadingAnchor.constraint(equalToSystemSpacingAfter: leadingAnchor, multiplier: 4),

bodyLabel.trailingAnchor.constraint(equalToSystemSpacingAfter: icon.leadingAnchor, multiplier: 4),

bottomAnchor.constraint(equalToSystemSpacingBelow: bodyLabel.bottomAnchor, multiplier: 3),

icon.heightAnchor.constraint(equalToConstant: 28),

icon.widthAnchor.constraint(equalToConstant: 28),

icon.topAnchor.constraint(equalToSystemSpacingBelow: topAnchor, multiplier: 3),

icon.leadingAnchor.constraint(equalToSystemSpacingAfter: bodyLabel.trailingAnchor, multiplier: 1),

trailingAnchor.constraint(equalToSystemSpacingAfter: icon.trailingAnchor, multiplier: 3),

])

}

// Update image view with the corresponding icon

func configData() {

guard let iconName = iconName else { return }

icon.image = UIImage(systemName: iconName)?.withRenderingMode(.alwaysTemplate)

}

}

Now, let’s create ServicesCell.swift, which will subclass UICollectionViewCell to organize the individual services using UIStackViews.

class ServicesCell: UICollectionViewCell {

override init(frame: CGRect) {

super.init(frame: frame)

configViews()

configConstraints()

}

required init?(coder: NSCoder) {

fatalError("init(coder:) has not been implemented")

}

// Define top and bottom stack views

var topServices: UIStackView = {

let stack = UIStackView()

stack.distribution = .fillProportionally

stack.axis = .horizontal

stack.spacing = 12

stack.translatesAutoresizingMaskIntoConstraints = false

return stack

}()

var bottomServices: UIStackView = {

let stack = UIStackView()

stack.distribution = .fillProportionally

stack.axis = .horizontal

stack.spacing = 12

stack.translatesAutoresizingMaskIntoConstraints = false

return stack

}()

// Instances of ServiceContainer for different services

let cleaningService: ServiceContainer = {

let service = ServiceContainer()

service.bodyLabel.text = "CleaningnServicesn& Bundles"

service.backgroundColor = UIColor(named: "greenBG")

service.icon.tintColor = UIColor(named: "greenColor")?.resolvedColor(with: UITraitCollection(userInterfaceStyle: .dark))

service.iconName = "lamp.desk"

return service

}()

let errandsService: ServiceContainer = {

let service = ServiceContainer()

service.bodyLabel.text = "Errandsn& Chores"

service.backgroundColor = UIColor(named: "orangeBG")

service.icon.tintColor = UIColor(named: "orangeColor")?.resolvedColor(with: UITraitCollection(userInterfaceStyle: .dark))

service.iconName = "bag"

return service

}()

let requestsService: ServiceContainer = {

let service = ServiceContainer()

service.bodyLabel.text = "SpecialnRequests"

service.iconName = "bell"

service.backgroundColor = UIColor(named: "yellowBG")

service.icon.tintColor = UIColor(named: "yellowColor")?.resolvedColor(with: UITraitCollection(userInterfaceStyle: .dark))

return service

}()

let partnerService: ServiceContainer = {

let service = ServiceContainer()

service.bodyLabel.text = "PartnernServices"

service.iconName = "person.2"

service.backgroundColor = UIColor(named: "grayBG")

service.icon.tintColor = UIColor(named: "grayColor")?.resolvedColor(with: UITraitCollection(userInterfaceStyle: .dark))

return service

}()

// Set up the components of the cell

func configViews() {

addSubview(topServices)

addSubview(bottomServices)

[cleaningService, errandsService].forEach { topServices.addArrangedSubview($0) }

[requestsService, partnerService].forEach { bottomServices.addArrangedSubview($0) }

}

// Set up Auto Layout constraints

func configConstraints() {

NSLayoutConstraint.activate([

topServices.topAnchor.constraint(equalToSystemSpacingBelow: topAnchor, multiplier: 0),

topServices.leadingAnchor.constraint(equalToSystemSpacingAfter: leadingAnchor, multiplier: 2),

trailingAnchor.constraint(equalToSystemSpacingAfter: topServices.trailingAnchor, multiplier: 2),

bottomServices.topAnchor.constraint(equalToSystemSpacingBelow: topServices.bottomAnchor, multiplier: 2),

bottomServices.leadingAnchor.constraint(equalToSystemSpacingAfter: leadingAnchor, multiplier: 2),

trailingAnchor.constraint(equalToSystemSpacingAfter: bottomServices.trailingAnchor, multiplier: 2),

cleaningService.leadingAnchor.constraint(equalToSystemSpacingAfter: topServices.leadingAnchor, multiplier: 0),

cleaningService.heightAnchor.constraint(equalToConstant: 130),

errandsService.heightAnchor.constraint(equalToConstant: 130),

errandsService.centerYAnchor.constraint(equalTo: topServices.centerYAnchor),

topServices.trailingAnchor.constraint(equalToSystemSpacingAfter: errandsService.trailingAnchor, multiplier: 0),

requestsService.leadingAnchor.constraint(equalToSystemSpacingAfter: bottomServices.leadingAnchor, multiplier: 0),

requestsService.heightAnchor.constraint(equalToConstant: 130),

partnerService.heightAnchor.constraint(equalToConstant: 130),

partnerService.centerYAnchor.constraint(equalTo: requestsService.centerYAnchor),

bottomServices.trailingAnchor.constraint(equalToSystemSpacingAfter: partnerService.trailingAnchor, multiplier: 0)

])

}

}

Next, we will return to Layouts.swift and add the following function to create the services section.

func servicesSection() -> NSCollectionLayoutSection {

let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)))

let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(280)), subitems: [item])

let section = NSCollectionLayoutSection(group: group)

section.contentInsets.top = 8

return section

}

This servicesSection() function will provide a layout for ServicesCell, where items will be arranged horizontally within a group of fixed height. We can now add this cell to HomeController with two minor updates.

init() {

let layout = UICollectionViewCompositionalLayout {

(sectionNumber, _) -> NSCollectionLayoutSection? in

if sectionNumber == 0 {

return Layouts.shared.headerSection()

} else {

return Layouts.shared.servicesSection()

}

}

super.init(collectionViewLayout: layout)

}

required init?(coder: NSCoder) {

fatalError("init(coder:) has not been implemented")

}

The first adjustment involves the init() function where the layout for each section is determined by its section number. The first section is assigned to the header layout, while the subsequent section corresponds to the services layout.

override func numberOfSections(in collectionView: UICollectionView) -> Int {

return 2

}

With this second adjustment, the number of sections in the collection view is now set to two.

Chapter 3: Conclusion and Next Steps

That's a wrap for this episode! In the next segment, we will focus on creating and implementing the recommendations cell, alongside some exciting titles.

Don't forget to check out the completed source code available in the GitHub repository below, and feel free to leave a star if you find it beneficial!

Thank you for following along, and see you in the next tutorial!

Share the page:

Twitter Facebook Reddit LinkIn

-----------------------

Recent Post:

# Reconnecting in the Digital Age: Prioritizing Human Connection

Exploring the impact of technology on intimacy and the importance of nurturing human relationships in the modern world.

Navigating Leadership: Balancing Exploitation and Exploration

Understanding the balance between exploiting resources and exploring new opportunities is crucial for successful leadership in business.

12 Simple Strategies to Enhance Your Attractiveness Effortlessly

Discover straightforward and cost-free strategies to boost your attractiveness and charm effortlessly in various social settings.