Skip to content
  • Home
  • Code
  • iOS & Swift
  • Combine
  • RxSwift
  • SwiftUI
  • Flutter & Dart
  • Tutorials
  • Art
  • Blog
Fx Studio
  • Home
  • Code
  • iOS & Swift
  • Combine
  • RxSwift
  • SwiftUI
  • Flutter & Dart
  • Tutorials
  • Art
  • Blog
Written by chuotfx on October 17, 2019

Basic iOS tutorial : Drawing

iOS & Swift

Contents

  • Chuẩn bị
  • 1. Khái niệm
    • 1.1. Drawing là gì?
    • 1.2. UIBezierPath
  • 2. Drawing
    • 2.1. Create Path
    • 2.2. Drawing trong ViewController
    • 2.3. Drawing với custom view
      • 2.3.1 Draw path in a layer
      • 2.3.2. Draw path in drawRect
  • 3. Một số hình vẽ cơ bản
    • Rectangular
    • Triangles
    • Ovals and Circles
    • Rectangles with Rounded Corners
    • Creating Arcs
  • Tạm kết

Chào bạn, khi người ta nhắc tới vẽ thì đó đúng làm ác mộng của giới lập trình viên. Trong một thời đại mà cái gì cũng có sẵn và nhanh. Thì việc tạo giao diện bằng kéo thả luôn luôn là ưu tiên số 1. Tuy nhiên, sẽ có những UI rất phức tạp và chỉ còn cách duy nhất là vẽ mà thôi. Và

Vẽ cũng là 1 trong các phương pháp custom view.

Bài viết này sẽ vén bức màn bí mật về vẽ trong iOS với Swift. Nếu bạn chưa tìm hiểu về Custom View thì có thể tham khảo bài trước:

  • Basic iOS tutorial : Custom View

Chuẩn bị

  • MacOS 10.14.4
  • Xcode 11.0
  • Swift 5.1

1. Khái niệm

1.1. Drawing là gì?

Configure your app’s drawing environment using colors, renderers, draw paths, strings, and shadows.

Theo Apple Documents

  • Có rất nhiều thư viện liên quan tới đồ hoạ
  • Có rất nhiều cách vẽ khác nhau
  • Có rất nhiều phương pháp vẽ khác nhau

Lập trình viên iOS sẽ rơi vào ma trận của Vẽ và 1 điều rất quan trọng cần chú ý đó là hiệu năng của ứng dụng.

  • Trong bài này thì mình sẽ giới thiệu và hướng dẫn về `UIBezierPath` để custom view bằng phương pháp vẽ.

1.2. UIBezierPath

  • Hiểu đơn giản thì nó là path
  • Dùng để vẽ 1 hình (shape)
    • Các hình cơ bản
    • Các hình phức tạp
    • Đường thẳng
    • Đường cong
    • …
  • Vẽ lên
    • CAShapeLayer
    • Trực tiếp lên View
  • Vì là path nên sẽ chứa các toạ độ điểm theo vector, dẫn tới tăng cường hiệu năng của ứng dụng khi render
  • Vẽ
    • stroke : vẽ các đường viền theo path
    • fill : tô màu cho path
  • Cần xét các thuộc tính color và width cho từng việc stroke hay fill

2. Drawing

  • Việc vẽ sẽ tuân thủ theo các bước sau:
    • Bước 1: Tạo Path
    • Bước 2: Tạo Layer
    • Bước 3: add Path vào Layer
    • Bước 4: add Layer vào View

2.1. Create Path

  • Bắt đầu với path cho việc vẽ 1 đường thẳng đơn giản, có start point và end point
let path = UIBezierPath()
path.move(to: start)
path.addLine(to: end)
path.close()
  • Giải thích:
    • Tạo 1 path
    • Di chuyển về toạ độ điểm bắt đầu
    • Add 1 line từ điểm hiện tại (đang là start) tới điểm mới (đang là end)
    • Close path

2.2. Drawing trong ViewController

  • Viết 1 function để vẽ cho nó pro hơn xí
func drawLine(start: CGPoint, end: CGPoint) {
        // PATH
        let path = UIBezierPath()
        path.move(to: start)
        path.addLine(to: end)
        path.close()
        
        //LAYER
        let shapeLayer = CAShapeLayer()
        shapeLayer.strokeColor = UIColor.blue.cgColor
        shapeLayer.lineWidth = 1.0
        shapeLayer.path = path.cgPath
        
        //ADD LAYER
        self.view.layer.addSublayer(shapeLayer)
    }
  • Trong đó:
    • Layer
      • Tạo 1 layer mới
      • Xét thuộc tính màu và độ dày line
      • Xét path cho layer
    • Add Layer vào view của ViewController
  • Tiếp tục thực thi trong file ViewController.swift
    • Gọi hàm trong viewDidLoad
    • Truyền 2 đối số 2 toạ độ điểm vào
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        drawLine(start: CGPoint(x: 50, y: 100), end: CGPoint(x: 350, y: 600))
    }
    
    func drawLine(start: CGPoint, end: CGPoint) {
        // PATH
        let path = UIBezierPath()
        path.move(to: start)
        path.addLine(to: end)
        path.close()
        
        //LAYER
        let shapeLayer = CAShapeLayer()
        shapeLayer.strokeColor = UIColor.blue.cgColor
        shapeLayer.lineWidth = 1.0
        shapeLayer.path = path.cgPath
        
        //ADD LAYER
        self.view.layer.addSublayer(shapeLayer)
    }

}
  • Kết quả

2.3. Drawing với custom view

  • Tạo 1 sub-class UIView mới để custom view. Tạm thời đặt tên là FxLineView
import UIKit

class FxLineView: UIView {
    
    //init
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        fatalError("init(coder:) has not been implemented")
    }

}
  • Chúng ta sử dụng 2 phương pháp vẽ

2.3.1 Draw path in a layer

  • Sử dụng lại function vẽ ở trên cho custom view
    • Thay đổi màu sắc thành màu đỏ
import UIKit

class FxLineView: UIView {
    
    //init
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        fatalError("init(coder:) has not been implemented")
    }
    
    //draw
    func drawLine(start: CGPoint, end: CGPoint) {
        // PATH
        let path = UIBezierPath()
        path.move(to: start)
        path.addLine(to: end)
        path.close()
        
        //LAYER
        let shapeLayer = CAShapeLayer()
        shapeLayer.strokeColor = UIColor.red.cgColor
        shapeLayer.lineWidth = 1.0
        shapeLayer.path = path.cgPath
        
        //ADD LAYER
        self.layer.addSublayer(shapeLayer)
    }

}
  • Sử dụng trong ViewController
let lineView = FxLineView(frame: view.bounds)
lineView.drawLine(start: CGPoint(x: 50, y: 100), end: CGPoint(x: 300, y: 600))
        
view.addSubview(lineView)
  • Giải thích
    • Tạo đối tượng custom view
    • Add custom view vào view
    • Trong ví dụ, mình sử dụng frame của custom view bằng chính view của ViewController
    • Với bài toán thực tế thì bạn cần phải tính toán cẩn thận hơn.
  • Kết quả

2.3.2. Draw path in drawRect

  • Sử dụng chính function của UIView khi custom là drawRect để vẽ
  • Tạo 1 sub-class UIView mới, tạm lấy tên là FxRectangularView
    • Tạm thời setup như dưới
    • createPath tạo ra 1 path
    • drawRect
      • new path
      • xét thuộc tính và màu
      • fill (vì bài này ko cần vẽ đường thẳng mà sử dụng fill)
import UIKit

class FxRectangularView: UIView {

    override func draw(_ rect: CGRect) {
        // create path
        let path = createPath()
        
        // fill
        let fillColor = UIColor.orange
        fillColor.setFill()
        path.fill()
    }
    
    func createPath() -> UIBezierPath {
        return UIBezierPath()
    }

}
  • Tiến hành tạo path, trong ví dụ thì chúng ta tạo 1 hình chữ nhật đơn giản bằng chính bounds
import UIKit

class FxRectangularView: UIView {

    override func draw(_ rect: CGRect) {
        // create path
        let path = createPath()
        
        // fill
        let fillColor = UIColor.orange
        fillColor.setFill()
        path.fill()
    }
    
    func createPath() -> UIBezierPath {
        let path = UIBezierPath(rect: self.bounds)
        return path
    }

}
  • Sử dụng trong ViewController
let rectView = FxRectangularView(frame: CGRect(x: 30, y: 300, width: 350, height: 150))
view.addSubview(rectView)
  • Kết quả

  • Chú ý
    • stroke dùng để vẽ các đường thẳng, đường cong. Đi đối với nó set color phải phù hợp theo ( `strokeColor`)
    • fill dùng để tô màu 1 hình hay 1 path mà các điểm nối liền mạch với nhau. Đi đôi với nó là fillColor
    • Tuý bị bạn sử dụng, tốt nhất là có cả 2 trong custom view của bạn. Ví dụ:
override func draw(_ rect: CGRect) {
    
    // create path
 
    // Specify the fill color and apply it to the path.
    UIColor.orange.setFill()
    path.fill()
 
    // Specify a border (stroke) color.
    UIColor.purple.setStroke()
    path.stroke()
}

 

Tới đây thì bạn đã nắm được cơ bản các cách vẽ và các phương pháp vẽ khác nhau. Cách sử dụng UIBezierPath . Và sử dụng chúng vào mục đích custom view. Các phần sau thì bạn tự tìm hiểu thêm về các hình vẽ cơ bản của UIBezierPath.

3. Một số hình vẽ cơ bản

Rectangular

func createRectangle() {
    // Initialize the path.
    path = UIBezierPath()
 
    // Specify the point that the path should start get drawn.
    path.move(to: CGPoint(x: 0.0, y: 0.0))
 
    // Create a line between the starting point and the bottom-left side of the view.
    path.addLine(to: CGPoint(x: 0.0, y: self.frame.size.height))
 
    // Create the bottom line (bottom-left to bottom-right).
    path.addLine(to: CGPoint(x: self.frame.size.width, y: self.frame.size.height))
 
    // Create the vertical line from the bottom-right to the top-right side.
    path.addLine(to: CGPoint(x: self.frame.size.width, y: 0.0))
 
    // Close the path. This will create the last line automatically.
    path.close()
}

Triangles

func createTriangle() {
    path = UIBezierPath()
    path.move(to: CGPoint(x: self.frame.width/2, y: 0.0))
    path.addLine(to: CGPoint(x: 0.0, y: self.frame.size.height))
    path.addLine(to: CGPoint(x: self.frame.size.width, y: self.frame.size.height))
    path.close()
}

Ovals and Circles

override func draw(_ rect: CGRect) {
    // self.createRectangle()    
    // self.createTriangle()
 
    // Create an oval shape path.
    self.path = UIBezierPath(ovalIn: self.bounds)
 
    ...
}

override func draw(_ rect: CGRect) {
    self.path = UIBezierPath(ovalIn: CGRect(x: self.frame.size.width/2 - self.frame.size.height/2,
                                            y: 0.0,
                                            width: self.frame.size.height,
                                            height: self.frame.size.height))
 
    ...
}

Rectangles with Rounded Corners

override func draw(_ rect: CGRect) {
    ...
 
    path = UIBezierPath(roundedRect: self.bounds, cornerRadius: 15.0)
 
    ...
}

override func draw(_ rect: CGRect) {
    ...
 
    path = UIBezierPath(roundedRect: self.bounds,
                        byRoundingCorners: [.topLeft, .bottomRight],
                        cornerRadii: CGSize(width: 15.0, height: 0.0))
 
    ...
}

Creating Arcs

override func draw(_ rect: CGRect) {
    ...
 
    path = UIBezierPath(arcCenter: CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2),
                        radius: self.frame.size.height/2,
                        startAngle: CGFloat(180.0).toRadians(),
                        endAngle: CGFloat(0.0).toRadians(),
                        clockwise: true)
 
    ...
}

Bảng phân độ như sau

Tạm kết

  • Biết được cách custom view bằng vẽ
  • Sử dụng UIBezierPath và 2 phương pháp vẽ
    • add layer
    • drawRect
  • Một số hình cơ bản
FacebookTweetPinYummlyLinkedInPrintEmailShares9

Related Posts:

  • RxCocoa Basic – Extend UIKit
    RxCocoa Basic – Extend UIKit
  • SwiftUI - Phần 6 : Basic UI Controls
    SwiftUI - Phần 6 : Basic UI Controls
  • RxCocoa Basic – Forward Delegate
    RxCocoa Basic – Forward Delegate
  • RxCocoa Basic – Traits
    RxCocoa Basic – Traits
Tags: basic ios tutorial, iOS
Written by chuotfx

Hãy ngồi xuống, uống miếng bánh và ăn miếng trà. Chúng ta cùng nhau đàm đạo về đời, về code nhóe!

3 comments

  • dotrinh has written: November 28, 2019 at 9:20 am Reply

    Mấy bài viết của bạn khá là hay, cám ơn bạn.
    Giao lưu nhé.

    • chuotfx has written: November 28, 2019 at 9:24 am Reply

      Thanks bạn! Okay, có gì thì mail cho mình. 😀

  • abc has written: July 8, 2020 at 9:29 am Reply

    teamlead co khac 😀 😀

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Donate – Buy me a coffee!

Fan page

Fx Studio

Tags

Actor Advanced Swift api AppDistribution Asynchronous autolayout basic ios tutorial blog callback ci/cd closure collectionview combine concurrency CoreData Core Location crashlytics darkmode dart dart basic dart tour Declarative decoding delegate deploy fabric fastlane firebase flavor flutter GCD iOS mapview MVVM optional protocol rxswift Swift Swift 5.5 SwiftUI SwiftUI Notes tableview testing TravisCI unittest

Recent Posts

  • Raw String trong 10 phút
  • Dispatch Semaphore trong 10 phút
  • Tổng kết năm 2022
  • KeyPath trong 10 phút – Swift
  • Make color App Flutter
  • Ứng dụng Flutter đầu tiên
  • Cài đặt Flutter SDK & Hello world
  • Coding Conventions – người hùng hay kẻ tội đồ?
  • Giới thiệu về Flutter
  • Tìm hiểu về ngôn ngữ lập trình Dart

You may also like:

  • Basic Grid Layout - SwiftUI Notes #52
    Basic Grid Layout - SwiftUI Notes #52
  • Basic TextField - SwiftUI Notes #27
    Basic TextField - SwiftUI Notes #27
  • RxCocoa Basic - Working with multi UI Control
    RxCocoa Basic - Working with multi UI Control
  • SwiftUI - Phần 6 : Basic UI Controls
    SwiftUI - Phần 6 : Basic UI Controls
  • RxCocoa Basic – Display Data from API
    RxCocoa Basic – Display Data from API

Archives

  • February 2023 (1)
  • January 2023 (2)
  • November 2022 (2)
  • October 2022 (1)
  • September 2022 (5)
  • August 2022 (6)
  • July 2022 (7)
  • June 2022 (8)
  • May 2022 (5)
  • April 2022 (1)
  • March 2022 (3)
  • February 2022 (5)
  • January 2022 (4)
  • December 2021 (6)
  • November 2021 (8)
  • October 2021 (8)
  • September 2021 (8)
  • August 2021 (8)
  • July 2021 (9)
  • June 2021 (8)
  • May 2021 (7)
  • April 2021 (11)
  • March 2021 (12)
  • February 2021 (3)
  • January 2021 (3)
  • December 2020 (3)
  • November 2020 (9)
  • October 2020 (7)
  • September 2020 (17)
  • August 2020 (1)
  • July 2020 (3)
  • June 2020 (1)
  • May 2020 (2)
  • April 2020 (3)
  • March 2020 (20)
  • February 2020 (5)
  • January 2020 (2)
  • December 2019 (12)
  • November 2019 (12)
  • October 2019 (19)
  • September 2019 (17)
  • August 2019 (10)

About me

Education, Mini Game, Digital Art & Life of coders
Contacts:
contacts@fxstudio.dev

Fx Studio

  • Home
  • About me
  • Contact us
  • Mail
  • Privacy Policy
  • Donate
  • Sitemap

Categories

  • Art (1)
  • Blog (22)
  • Code (4)
  • Combine (22)
  • Flutter & Dart (24)
  • iOS & Swift (86)
  • RxSwift (37)
  • SwiftUI (76)
  • Tutorials (70)

Newsletter

Stay up to date with our latest news and posts.
Loading

    Copyright © 2023 Fx Studio - All rights reserved.

    Share this ArticleLike this article? Email it to a friend!

    Email sent!