Creating Custom Views on iOS
Overview
In this tutorial I will create a custom UI with swift for iOS. I decided to create a custom view on ios, after I saw Prakhar Neel Sharma’s design at dribbble.
When you finish this tutorial, your application will look like as shown in the image. You can also download the source from my github repository
Development
Let’s start with a Single View Application
as follows.
After your project is created we need to add our background and profile images into the Assets.xcassets
folder. You can select the folder and just drag & drop the files you want to use as profile image and background image. I have named those image files as profile
and background
Now we need to create a new Cocoa Touch Class
for our custom view as follows.
You can create a new file with Cmd+N
shortcut.
In the second popup, make sure that UIView
is selected at Subclass section. I’ll call this file CustomView.swift
Now, it’s time to write some code.
First thing I want to do is to create a function called createCustomPath()
for drawing a UIBezierPath
. Open CustomView.swift
file and add these lines.
//CustomView.swift
import UIKit
class CustomView: UIView {
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
//override func draw(_ rect: CGRect) {
// Drawing code
//}
func createCustomPath(padding: CGFloat, startY: CGFloat, endY: CGFloat) -> UIBezierPath {
let path = UIBezierPath()
path.move(to: CGPoint(x: bounds.minX + padding, y: bounds.minY + startY))
path.addLine(to: CGPoint(x: bounds.minX + padding, y: bounds.maxY - padding))
path.addLine(to: CGPoint(x: bounds.maxX - padding, y: bounds.maxY - padding))
path.addLine(to: CGPoint(x: bounds.maxX - padding, y: bounds.minY + endY))
path.close()
return path
}
}
This function will create our custom shape.
To draw the shapes, we need to uncomment override func draw(_ rect: CGRect)
and call createCustomPath()
function.
We need to set colors before we create and fill()
our shapes, so our draw()
function will be as follows. There are 5 different shapes in out custom UI so, I will call createCustomPath()
function five times. First shape has the opacity value of 0.1 . Next three shapes have the opacity value of 0.2 . And the last one’s opacity is set to 1.
override func draw(_ rect: CGRect) {
//set fill color with 0.1 opacity
UIColor(red:255/255, green: 255/255, blue: 255/255, alpha: 0.1).set()
createCustomPath(padding: 15, startY: 220, endY: 190).fill()
//change fill color opacity to 0.2
UIColor(red:255/255, green: 255/255, blue: 255/255, alpha: 0.2).set()
createCustomPath(padding: 15, startY: 240, endY: 260).fill()
createCustomPath(padding: 15, startY: 265, endY: 190).fill()
createCustomPath(padding: 15, startY: 270, endY: 290).fill()
UIColor(red:240/255, green: 240/255, blue: 240/255, alpha: 1).set()
createCustomPath(padding: 15, startY: 340, endY: 190).fill()
}
Now it’s time to change ViewController.swift file. In the ViewController we will create
- An instance of CustomView
- A UIImageView for our profile image
- A UISegmentedControl view for Add and Message buttons
- Two UITextView s for Name and Job Title fields
Then we will add those items to our MainView with addSubiew() function.
We will also create setup functions for each field explained above, to place them in the view, and setup the height, width anchors.
// ViewController.swift
let customView : CustomView = {
let view = CustomView()
view.translatesAutoresizingMaskIntoConstraints = false;
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOffset = CGSize(width: 0, height: 0)
view.layer.shadowOpacity = 0.4
view.layer.shadowRadius = 25
return view
}()
let profileImage: UIImageView = {
let profileImageView = UIImageView()
profileImageView.image = UIImage(named: "profile")
profileImageView.translatesAutoresizingMaskIntoConstraints = false
profileImageView.layer.cornerRadius = 35
profileImageView.layer.borderColor = UIColor.white.cgColor
profileImageView.layer.borderWidth = 1
profileImageView.layer.masksToBounds = true
return profileImageView
}()
let segmentedController: UISegmentedControl = {
let sc = UISegmentedControl(items:["Add", "Message"])
sc.translatesAutoresizingMaskIntoConstraints = false
sc.selectedSegmentIndex = 0
sc.tintColor = UIColor(red:40/255, green: 40/255, blue: 40/255, alpha: 1)
sc.layer.cornerRadius = 0.0
sc.layer.borderColor = UIColor(red:240/255, green: 240/255, blue: 240/255, alpha: 1).cgColor
sc.layer.borderWidth = 0.1;
sc.layer.masksToBounds = true
return sc
}()
let nameTextView: UITextView = {
let nameTextView = UITextView()
nameTextView.text = "Ferdi Sönmezay"
nameTextView.isEditable = false
nameTextView.font = UIFont.systemFont(ofSize: 20, weight: UIFontWeightThin)
nameTextView.backgroundColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.0)
nameTextView.textColor = UIColor.black
nameTextView.translatesAutoresizingMaskIntoConstraints = false
return nameTextView
}()
let summaryTextView: UITextView = {
let summaryTextView = UITextView()
summaryTextView.text = "Senior Software Developer, Ankara"
summaryTextView.isEditable = false
summaryTextView.font = UIFont.systemFont(ofSize: 12, weight: UIFontWeightRegular)
summaryTextView.backgroundColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.0)
summaryTextView.textColor = UIColor.darkGray
summaryTextView.translatesAutoresizingMaskIntoConstraints = false
return summaryTextView
}()
After we defined our fields, now we need to add those items in our view using view.addSubview()
function. Change viewDidLoad()
function as follows.
override func viewDidLoad() {
super.viewDidLoad()
//background-image
UIGraphicsBeginImageContext(self.view.frame.size)
UIImage(named: "background")?.draw(in: self.view.bounds)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
self.view.backgroundColor = UIColor(patternImage: image)
view.addSubview(customView)
view.addSubview(profileImage)
view.addSubview(nameTextView)
view.addSubview(summaryTextView)
view.addSubview(segmentedController)
}
The next step is to specify anchors for each field defined above. I will create a function for each field.
func setupCustomView() {
customView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true;
customView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
customView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
customView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
customView.backgroundColor = UIColor(red: 100/255, green: 53/255, blue: 121/255, alpha: 0.0)
}
func setupProfileImage() {
profileImage.leftAnchor.constraint(equalTo: view.leftAnchor, constant:50).isActive = true
profileImage.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant:-30).isActive = true
profileImage.widthAnchor.constraint(equalToConstant: 70).isActive = true
profileImage.heightAnchor.constraint(equalToConstant: 70).isActive = true
}
func setupNameTextView() {
nameTextView.topAnchor.constraint(equalTo: profileImage.bottomAnchor, constant:10).isActive = true
nameTextView.leftAnchor.constraint(equalTo: profileImage.leftAnchor, constant:-10).isActive = true
nameTextView.widthAnchor.constraint(equalTo: customView.widthAnchor, constant: -40).isActive = true
nameTextView.heightAnchor.constraint(equalToConstant: 32).isActive = true
}
func setupSummaryTextView() {
summaryTextView.topAnchor.constraint(equalTo: nameTextView.bottomAnchor).isActive = true
summaryTextView.leftAnchor.constraint(equalTo: nameTextView.leftAnchor).isActive = true
summaryTextView.widthAnchor.constraint(equalTo: nameTextView.widthAnchor).isActive = true
summaryTextView.heightAnchor.constraint(equalToConstant: 24).isActive = true
}
func setupSegmentedController() {
segmentedController.topAnchor.constraint(equalTo: summaryTextView.bottomAnchor, constant:10).isActive = true
segmentedController.leftAnchor.constraint(equalTo: view.leftAnchor, constant:15).isActive = true
segmentedController.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -30).isActive = true
segmentedController.heightAnchor.constraint(equalToConstant: 60).isActive = true
}
Finally we need to call those functions in viewDidLoad
, and we’re done.
override func viewDidLoad() {
super.viewDidLoad()
...
...
setupCustomView()
setupProfileImage()
setupNameTextView()
setupSummaryTextView()
setupSegmentedController()
}