1. Trang chủ
  2. » Giáo án - Bài giảng

Giáo trình lập trình di động trên iOS Dành cho bậc Cao đẳng

137 22 1

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Giáo Trình Lập Trình Di Động Trên iOS
Tác giả Tiêu Kim Cương
Trường học Trường Cao đẳng Công nghệ Thủ Đức
Chuyên ngành Công nghệ thông tin
Thể loại Giáo Trình
Năm xuất bản 2020
Thành phố Thành phố Hồ Chí Minh
Định dạng
Số trang 137
Dung lượng 8,91 MB

Cấu trúc

  • 1.1 Tổng quan về phát triển ứng dụng trên iOS (10)
    • 1.1.1 IOS là gì? Kiến trúc bên trong iOS? (10)
    • 1.1.2 Mô hình MVC trong lập trình iOS (11)
    • 1.1.3 Phát triển ứng dụng trên iOS (12)
    • 1.1.4 Viết ứng dụng đầu tiên trên iOS (12)
  • 1.2 Ngôn ngữ lập trình Swift (16)
    • 1.2.1 Cơ bản về ngôn ngữ lập trình Swift (16)
    • 1.2.2 Kiểu dữ liệu, biến, hằng (17)
    • 1.2.3 Biến kiểu Optional (18)
    • 1.2.4 Biến Tuples (20)
    • 1.2.5 Chuỗi thoát trong Swift (21)
    • 1.2.6 Các loại toán tử trong ngôn ngữ Swift (21)
    • 1.2.7 Rẽ nhánh và vòng lặp trong Swift (23)
    • 1.2.8 Xử lý chuỗi và ký tự trong Swift (28)
    • 1.2.9 Mảng trong Swift (29)
    • 1.2.10 Tập hợp trong Swift (31)
    • 1.2.11 Làm việc với kiểu Dictionaries (33)
    • 1.2.12 Lệnh guard (35)
    • 1.2.13 Hàm trong Swift (35)
    • 1.2.14 Định nghĩa và sử dụng Cấu trúc, Lớp đối tượng trong Swift (41)
    • 1.2.15 Ép kiểu (44)
    • 1.2.16 Protocol và cơ chế Delegate trong Swift (46)
  • 1.3 Câu hỏi và bài tập chương 1 (49)
  • CHƯƠNG 2. THIẾT KẾ GIAO DIỆN VÀ XỬ LÝ SỰ KIỆN TRÊN IOS (54)
    • 2.1 Thiết kế giao diện với Storyboard (54)
      • 2.1.1 Các thành phần chính trong iOS Project (54)
      • 2.1.2 Màn hình chờ LaunchScreen.storyboard (57)
      • 2.1.3 Màn hình thiết kế giao diện Main.storyboard (57)
      • 2.1.4 Case Study: Tạo ứng dụng Calculate (61)
    • 2.2 Xử lý sự kiện trên iOS (64)
      • 2.2.1 Kết nối các đối tượng với code (64)
      • 2.2.2 Cách viết hàm trong iOS với ngôn ngữ Swift (66)
      • 2.2.3 Hoàn thiện ứng dụng Calculate giai đoạn 1 (67)
    • 2.3 Tổ chức code theo mô hình MVC (70)
      • 2.3.1 Phân tích ứng dụng theo mô hình MVC (70)
      • 2.3.2 Xây dụng ứng dụng theo mô hình MVC (71)
      • 2.3.3 Mở rộng và hoàn thiện ứng dụng Calculate (72)
    • 2.4 Autolayout trong iOS (76)
      • 2.4.1 Vấn đề giao diện trong ứng dụng Calculate (76)
      • 2.4.2 Cải tiến giao diện cho ứng dụng Calculate (76)
    • 2.5 Case Study: Thiết kế ứng dụng Quản lý món ăn (79)
      • 2.5.1 Luyện tập thiết kế giao diện cơ bản và autolayout trong iOS (79)
      • 2.5.2 Xử lý sự kiện với các Component cơ bản (81)
      • 2.5.3 Làm việc với UITapGestureRecognizer (85)
      • 2.5.4 Xây dựng Control mới cho ứng dụng (87)
      • 2.5.5 Thêm thuộc tính vào Attributes Inspector (92)
      • 2.5.6 Table view (95)
      • 2.5.7 Navigation và truyền tham số giữa các màn hình ứng dụng (100)
    • 2.6 Câu hỏi và bài tập chương 2 (111)
  • CHƯƠNG 3. LÀM VIỆC VỚI CƠ SỞ DỮ LIỆU (113)
    • 3.1 Thiết kế Data model cho ứng dụng (113)
      • 3.1.1 Phân tích ứng dụng Quản lý món ăn (113)
      • 3.1.2 Thiết kế Datamodel cho ứng dụng (113)
    • 3.2 Kiểm thử tính đúng đắn của Data model (114)
      • 3.2.1 Xây dựng các test cases kiểm thử tính đúng đắn cho Datamodel của ứng dụng (114)
      • 3.2.2 Kiểm thử và điều chỉnh (115)
    • 3.3 Một số dạng lưu trữ dữ liệu lâu dài trên ứng dụng iOS (116)
      • 3.3.1 Core Data (116)
      • 3.3.2 SQLite (116)
      • 3.3.3 Lưu trữ trên mạng (116)
    • 3.4 Cơ sở dữ liệu SQLite với các ứng dụng iOS (116)
      • 3.4.1 Cài đặt thư viện SQLite và Framework FMDB (116)
      • 3.4.2 Thiết kế tầng truy xuất dữ liệu DAL (118)
    • 3.5 Xây dựng tầng truy xuất dữ liệu cho ứng dụng iOS (119)
      • 3.5.1 Thiết kế các chức năng cơ bản: Đóng, mở, tạo bảng… (119)
      • 3.5.2 Thiết kế các API cho tầng trên (121)
    • 3.6 Sử dụng tầng DAL cho ứng dụng iOS (123)
    • 3.7 Câu hỏi và bài tập chương 3 (125)
  • CHƯƠNG 4. BẢN ĐỒ TRỰC TUYẾN TRÊN IOS (126)
    • 4.1 Một số dạng bản đồ trực tuyến thông dụng (126)
      • 4.1.1 Map Kit (126)
      • 4.1.2 Google Map (127)
    • 4.2 Sử dụng bản đồ trực tuyến trong các ứng dụng iOS (127)
      • 4.2.1 Tích hợp MapKit vào ứng dụng (127)
      • 4.2.2 Tạo mới bản đồ trong ứng dụng iOS (127)
      • 4.2.3 Đánh dấu trên bản đồ dùng Annotation (129)
      • 4.2.4 Lấy vị trí hiện tại của người dùng trên bản đồ (130)
      • 4.2.5 Tương tác với các Annotation trên bản đồ (131)
      • 4.2.6 Xác định vị trí và di chuyển trên bản đồ (131)
      • 4.2.7 Đánh dấu vị trí bằng sự kiện LongPressGesture (132)
      • 4.2.8 Chỉ đường (132)
    • 4.3 Câu hỏi và bài tập chương 4 (135)
  • TÀI LIỆU THAM KHẢO (137)

Nội dung

Tổng quan về phát triển ứng dụng trên iOS

IOS là gì? Kiến trúc bên trong iOS?

iOS, hay còn gọi là iPhone OS, là hệ điều hành di động được phát triển bởi Apple cho các thiết bị như iPhone, iPad, iPod touch và Apple TV Ban đầu, iOS được thiết kế riêng cho iPhone và ra mắt lần đầu vào năm 2007 với phiên bản iOS 1 Năm 2008, iOS 2 được giới thiệu, mang đến tính năng App Store Đến năm 2009, iOS 3 ra mắt với tính năng iPhone Enhancement, đánh dấu sự phát triển không ngừng của hệ điều hành này qua các thế hệ.

Năm 2010 đánh dấu một bước đột phá của Apple với sự ra mắt của iOS 4, bao gồm các tính năng như MultiTasking, Retina và FaceTime Đến năm 2020, iOS 14 đã được giới thiệu với 8 tính năng mới nổi bật, bao gồm khả năng chọn trình duyệt mặc định khác ngoài Safari, App Library mới với Widgets và ứng dụng được tổ chức trong một thư mục, giao diện cuộc gọi cải tiến dưới dạng biểu tượng, và khả năng thêm Widgets tùy chỉnh trực tiếp trên Màn hình chính Siri cũng đã được thay đổi để xuất hiện nhỏ gọn hơn ở phía dưới màn hình, trong khi các tính năng mới như gõ lên lưng iPhone để chụp ảnh màn hình và điều chỉnh âm lượng, hiệu ứng âm thanh không gian trên AirPod Pro, cũng như khả năng thu hồi tin nhắn đã gửi và Picture in Picture trên YouTube đã được bổ sung.

Trên phương diện lập trình viên thì có thể hiểu iOS như một Sofware Stack bao gồm 4 tầng (Hình 1.1.1) Trong đó:

- Tầng Core OS: Là hạt nhân của hệ điều hành iOS dựa trên nền của Unix

Tầng Core Services, Media Layer và Cocoa Touch là ba thành phần chính hỗ trợ phát triển ứng dụng trên iOS Tầng Core Services cung cấp các thư viện và dịch vụ thiết yếu như quản lý file, cơ sở dữ liệu SQLite và dịch vụ mạng Tầng Media Layer tập trung vào các thư viện cho âm thanh, video và xử lý hình ảnh, PDF Cuối cùng, tầng Cocoa Touch bao gồm các thư viện cần thiết cho giao diện người dùng, như xử lý Multi-Touch, MapKit, các loại View và Controls, cũng như các tính năng như Camera và Image Picker.

Mô hình MVC trong lập trình iOS

MVC là viết tắt của Model, View và Controller trong thiết kế phần mềm (là một

Software Design Pattern) giúp các nhà phát triển phần mềm tổ chức code sáng sủa, dễ hiệu chỉnh, sửa chữa và dễ bảo trì

Mô hình MVC tổ chức ứng dụng thành ba nhóm đối tượng chính: Nhóm Views, Nhóm Models và Nhóm Controllers Nhóm Views chịu trách nhiệm về giao diện và tương tác với người dùng, trong khi Nhóm Models quản lý dữ liệu và các hoạt động chuyên môn Nhóm Controllers đóng vai trò trung gian, điều phối giữa yêu cầu của người dùng và các chức năng nghiệp vụ của ứng dụng Ví dụ, trong ứng dụng Máy tính bỏ túi, lập trình viên thiếu kinh nghiệm có thể chỉ thiết kế giao diện và tạo một lớp duy nhất để xử lý cả yêu cầu và tính toán, dẫn đến việc tổ chức code không hiệu quả.

Việc điều chỉnh và bảo trì ứng dụng có thể gặp nhiều khó khăn, đặc biệt khi thêm tính năng mới Để tổ chức ứng dụng theo mô hình MVC, cần tách lớp đã xây dựng thành hai phần: lớp CalculatorBrain (Models) đảm nhận các hoạt động tính toán, nhận toán hạng và toán tử, sau đó trả về kết quả; và lớp Calculator (Controllers) điều phối giữa yêu cầu người dùng từ Views và các hoạt động tính toán Khi người dùng thực hiện phép toán, lớp Controller sẽ tiếp nhận và phân tích yêu cầu, sau đó gửi đến CalculatorBrain để thực hiện và cập nhật kết quả lên giao diện cho người dùng.

Phát triển ứng dụng trên iOS

Để phát triển ứng dụng trên iOS, người học cần nắm vững kiến thức và kỹ năng cần thiết, đồng thời chuẩn bị môi trường phát triển đáp ứng các yêu cầu tối thiểu.

Để trở thành một chuyên gia trong lĩnh vực công nghệ thông tin, bạn cần thành thạo trong thiết kế và phát triển ứng dụng theo phương pháp hướng đối tượng, cũng như có khả năng thiết kế và xây dựng cơ sở dữ liệu dựa trên mô hình dữ liệu quan hệ.

- Hệ điều hành: Cần sử dụng hệ điều hành Mac OSX (Sử dụng các máy tính của Apple hoặc cài trên máy ảo)

- Công cụ phát triển: Cài đặt bộ công cụ Xcode (phiên bản mới nhất).

Viết ứng dụng đầu tiên trên iOS

Khởi động Xcode rồi thực hiện chuỗi thao tác trên thanh Menu hệ thống File => New

=> Project… hoặc nhấp chọn Create a new Xcode Project (Hình 1.1.4.1) để tạo một Project mới cho ứng dụng iOS

Hình 1.1.4.1 Màn hình khởi động Xcode

Tiếp theo, cần chọn một Template cho ứng dụng muốn xây dựng: Lựa chọn iOS => App và Next để tiếp tục (Hình 1.1.4.2)

Hình 1.1.4.2 Giao diện lựa chọn một Template cho ứng dụng iOS

Hình 1.1.4.3 Cấu hình cho một Project iOS

The configuration screen for the iOS project appears (Figure 1.1.4.3) In the Product Name field, enter the desired application name (for this example, use Empty Window) For the Team section, temporarily select None, and in the Organization Identifier field, input tdc.edu.vn (the domain of TDC).

In the interface selection, choose Storyboard and set the Language option to Swift Currently, uncheck the two options for Use Core Data and Include Tests Once completed, click Next.

Lựa chọn nơi sẽ chứa Project => Create Xcode sẽ tạo một Project mới theo cấu hình đã lựa chọn (Hình 1.1.4.4)

Trên thanh công cụ ở phía trên màn hình, chọn loại máy ảo là iPhone 8 Plus để chạy chương trình Để thử nghiệm ứng dụng, nhấn vào nút “Play” trên thanh công cụ Xcode sẽ tự động biên dịch chương trình, khởi tạo máy ảo iPhone 8 Plus, cài đặt và chạy ứng dụng trên máy ảo Kết quả là một màn hình trắng xuất hiện trên máy ảo iPhone 8 Plus, đó chính là ứng dụng mà chúng ta vừa tạo và chạy.

Hình 1.1.4.4 Màn hình khởi tạo của Project iOS mới

Trong Panel bên trái, nhấp chọn ViewCotroller.swift để nhìn rõ hơn giao diện của bộ công cụ phát triển ứng dụng iOS (Hình 1.1.4.5)

Hình 1.1.4.5 Các vùng cơ bản trong bộ công cụ phát triển ứng dụng trên iOS

Vùng (1) là bảng điều hướng giúp người dùng xem cấu trúc của Project và di chuyển giữa các thành phần Trong khi đó, vùng (2) là nơi soạn thảo mã nguồn cho chương trình.

(3) là bảng thuộc tính của các đối tượng và vùng (4) dùng để xem kết quả và gỡ rối chương trình

Ngôn ngữ lập trình Swift

Cơ bản về ngôn ngữ lập trình Swift

Swift là ngôn ngữ lập trình do Apple phát triển từ năm 2010, nhằm thay thế Objective-C trong phát triển ứng dụng iOS Được thiết kế dựa trên nhiều ngôn ngữ nổi tiếng như Rust, Haskell, Ruby, Python, C# và CLU, Swift mang lại nhiều ưu điểm vượt trội như tốc độ nhanh, đơn giản và hiệu năng xử lý tốt hơn Tính năng Playground cho phép lập trình viên xem kết quả trong thời gian thực, hỗ trợ hiệu quả cho quá trình phát triển ứng dụng Hơn nữa, Swift cũng cho phép tái sử dụng các thư viện viết bằng Objective-C, làm cho nó trở thành lựa chọn hàng đầu cho phát triển ứng dụng iOS hiện tại và tương lai.

Dưới đây là một số cú pháp cơ bản của ngôn ngữ Swift:

STT Cú pháp Mô tả

1 import TenThuVien Sử dụng thư viện có sẵn cho ứng dụng iOS Ví dụ: import UIKit

2 // Comment Ghi chú cho một dòng code, nội dung ghi chú sẽ bị bỏ qua khi biên dịch chương trình

3 /* Comment */ Ghi chú trên nhiều dòng code, nội dung ghi chú sẽ bị bỏ qua khi biên dịch chương trình

4 import UIKit var name = "Nguyen A" var greeting = "Hi" print(greeting); print(name)

Trong Swift, bạn không cần sử dụng dấu ";" để kết thúc lệnh nếu mỗi lệnh nằm trên một dòng riêng biệt Tuy nhiên, khi có nhiều lệnh trên cùng một dòng, bạn cần phân tách chúng bằng dấu ";".

Quy tắc đặt tên trong Swift:

Tên trong Swift phân biệt chữ hoa, chữ thường; tên phải bắt đầu bằng chữ cái a-z hoặc

A-Z, hoặc ký tự gạch dưới

“_”; tiếp theo có thể là các chữ cái chữ số và ký tự “_”

Tên trong Swift không được chứa các ký tự đặc biệt như

@, #, %,… (Đặt tên giống trong ngôn ngữ lập trình C)

Một số tên hợp lệ: aBC10, Abc100, move_name, abc_123…

Swift hỗ trợ việc sử dụng tên biến dựa trên các ký tự Unicode, cho phép người dùng đặt tên biến bằng các ký tự trong bảng chữ cái của nhiều ngôn ngữ khác nhau Chẳng hạn, khai báo biến như sau là hoàn toàn hợp lệ: var 你好 = "你好世界" và khi in ra, kết quả sẽ là 你好.

6 Từ khoá (Keyword) trong ngôn ngữ Swift: Một số từ khoá dùng để khai báo trong Swift:

Trong ngôn ngữ lập trình, các từ khóa được sử dụng với nhiều mục đích khác nhau, chẳng hạn như từ khóa "import" để sử dụng thư viện có sẵn và "var" để khai báo biến.

Không được đặt tên biến, tên hàm… trùng với tên từ khoá

Tuy nhiên, ngôn ngữ Swift vẫn cho phép dùng tên biến trùng với từ khoá với điều kiện đặt trước và sau nó ký tự

In programming, certain keywords are essential for defining the structure and functionality of code Valid keywords include class, deinit, enum, extension, func, import, init, internal, let, operator, private, protocol, public, static, struct, subscript, typealias, and var Additionally, control flow statements utilize keywords such as break, case, continue, default, do, else, fallthrough, for, if, in, return, switch, where, and while Understanding and correctly using these keywords is crucial for effective coding.

In programming, certain keywords play a crucial role in expressions, including "as," "false," "is," "nil," "self," "super," and "true." Additionally, there are keywords used in specific contexts, such as "associatedtype," "dynamic," "didSet," "final," "get," "inout," "optional," "override," and "required." Understanding these keywords is essential for effective coding and enhances the overall functionality of the programming language.

Quy tắc sử dụng dấu cách

Dấu cách (dấu khoảng trắng) được dùng để phân tách các từ trong một câu lệnh Swift

Trong JavaScript, khi khai báo biến với từ khóa var, có thể có một hoặc nhiều khoảng trắng giữa từ khóa và tên biến, ví dụ: var age Tuy nhiên, khác với các ngôn ngữ lập trình khác, số lượng khoảng trắng ở hai bên các toán tử phải bằng nhau.

Kiểu dữ liệu, biến, hằng

Trong Swift có một số kiểu dữ liệu cơ bản sau:

STT Tên kiểu dữ liệu Mô tả

1 Int/UInt Dùng để khai báo các biến kiểu số nguyên có dấu/không dấu Phạm vi biểu diễn: từ -2147483648 tới

2147483647 (Với số nguyên 32 bit có dấu) hoặc từ 0 tới

4294967295 (Với số nguyên không dấu 32 bit)

Biến kiểu số thực trong lập trình được khai báo bằng từ khóa "float", với phạm vi biểu diễn từ 1.2E-38 đến 3.4E+38 Kiểu số thực này sử dụng 4 bytes và có độ chính xác lên tới 6 chữ số sau dấu phẩy thập phân.

Biến kiểu số thực độ chính xác kép (double) được khai báo bằng từ khóa "double" trong lập trình Phạm vi biểu diễn của kiểu dữ liệu này từ 2.3E-308 đến 1.7E+308, với kích thước 8 bytes và độ chính xác lên tới 15 chữ số sau dấu phẩy thập phân.

4 Bool Dùng để khai báo các biến kiểu logic, chỉ nhận các giá trị đúng hoặc sai (true/false)

5 String Dùng để khai báo các biến kiểu chuỗi ký tự (ví dụ như

6 Character Dùng để khai báo các biến kiểu ký tự đơn

7 Optional Dùng để khai báo một dạng biến đặc biệt trong Swift

(sẽ được làm rõ trong phần sau)

8 Tuples Dùng để định nghĩa các biến được tạo thành từ nhiều giá trị đơn lẻ khác nhau trong Swift (sẽ được làm rõ trong phần sau)

Trong ngôn ngữ Swift, người dùng có thể tạo kiểu dữ liệu bí danh bằng cách sử dụng từ khoá typealias, cho phép đặt tên mới cho một kiểu dữ liệu đã có Cú pháp được sử dụng là: typealias TênMới = Kiểu.

Ví dụ, việc sử dụng typealias Nguyen = Int sẽ tạo ra một kiểu số nguyên mới mang tên Nguyen, cho phép chúng ta khai báo kiểu dữ liệu nguyên tương tự như kiểu Int.

10 Các kiểu dữ liệu tự định nghĩa Đa dạng, phụ thuộc vào nhu cầu của người dùng (sẽ được trình bày ở các phần sau)

Bảng 1.2.2 Một số kiểu dữ liệu thông dụng trong Swift

Trong Swift, người dùng có thể tự định nghĩa nhiều loại kiểu dữ liệu khác nhau như Array, Dictionaries, Structures và Classes, nhằm đáp ứng nhu cầu đa dạng về dữ liệu trong các ứng dụng iOS Các kiểu dữ liệu này sẽ được giải thích chi tiết hơn trong các chương tiếp theo.

Khai báo biến, hằng trong Swift: Trong Swift có thể khai báo biến theo một trong hai cách dưới đây: var variableName: =

Ví dụ: var name:String = "Nguyen Van A"

Hoặc khai báo ẩn danh kiểu dữ liệu như dưới đây: var variableName =

Ví dụ: var name = "Nguyen Van A"

Khi khai báo biến trong Swift, giá trị khởi tạo có thể có hoặc không Nếu không chỉ rõ kiểu dữ liệu, bạn cần phải khởi tạo giá trị ban đầu cho biến, từ đó trình biên dịch sẽ xác định kiểu dữ liệu gần nhất với giá trị đó (tính năng type inference) Ví dụ, khi khai báo var a = 50, biến a sẽ được xác định là kiểu Int; còn với var b = 10 + 0.15, biến b sẽ được xác định là kiểu Double.

Trong Swift, nếu các biến không thay đổi giá trị trong suốt quá trình hoạt động của ứng dụng, nên khai báo chúng dưới dạng hằng bằng từ khóa let thay vì từ khóa var.

Biến kiểu Optional

Trong ngôn ngữ Swift, Optional là một kiểu biến đặc biệt, cho phép khai báo biến mà không cần khởi tạo giá trị ngay lập tức Trong các ứng dụng iOS, mọi biến cần được khởi tạo trước khi sử dụng, nhưng đôi khi giá trị khởi tạo không thể xác định được cho đến khi chương trình chạy Biến Optional có thể có giá trị hoặc không tồn tại (nil) Để khai báo biến là kiểu Optional, chúng ta sử dụng cú pháp thêm dấu ? hoặc ! sau kiểu dữ liệu, ví dụ: var bienOptionalInt: Int?, var bienOptionalString: String? = nil, và var bienOptionalFloat: Float!.

Khi khai báo một biến là Optional, bạn cần unwrap biến đó trước khi sử dụng giá trị của nó Để thực hiện việc unwrap, hãy thêm ký tự “!” sau biến Ví dụ, với biến Optional var greeting: String?, bạn có thể gán giá trị "Hello everyone" Để in giá trị ra màn hình Console, hãy kiểm tra xem greeting có khác nil hay không và sử dụng print(greeting!).

} else { print("biến greeting không có giá trị: nil")

Biến greeting được khai báo là kiểu Optional, cho phép gán giá trị cho nó Việc gán giá trị cho biến này có thể không xảy ra, dẫn đến kết quả khác nhau Khi làm việc với biến Optional, cần kiểm tra xem nó có khác rỗng hay không.

Khi làm việc với biến Optional trong Swift, ta cần kiểm tra giá trị của biến có khác nil hay không và xử lý các trường hợp đó một cách thích hợp Cụ thể, nếu biến khác nil, ta sẽ sử dụng lệnh print(greeting!) để hiển thị giá trị của biến greeting ra màn hình Console của Xcode Dấu “!” sau tên biến được dùng để unwrap giá trị của biến Optional trước khi sử dụng.

Khi khai báo biến Optional dưới dạng 3 với dấu “!”, lập trình viên không cần unwrap giá trị của nó vì nó sẽ được unwrap tự động Tuy nhiên, trong thực tế, việc sử dụng dạng này hiếm khi cần thiết, vì nó không cảnh báo lập trình viên về việc unwrap giá trị trước khi sử dụng, có thể dẫn đến sai sót trong quá trình lập trình.

Khi sử dụng biến Optional trong lập trình, việc quên kiểm tra giá trị của biến có thể dẫn đến việc cố gắng truy xuất biến nil, gây ra lỗi và làm chương trình bị sập Để tránh tình trạng này, luôn cần kiểm tra giá trị của biến trước khi sử dụng.

Khi làm việc với biến Optional trong Swift, cần kiểm tra xem biến đó có giá trị nil hay không Một cách hiệu quả để thực hiện điều này là sử dụng Optional Binding Ví dụ, với biến `greeting` có kiểu `String?`, bạn có thể gán giá trị "Hello everyone" và sử dụng cú pháp `if let` để xuất chuỗi ra màn hình Console nếu biến không nil.

} else { print("biến greeting không có giá trị: nil")

Thay vì kiểm tra biến greeting có nil hay không ở dòng thứ 3, ta có thể khai báo một biến hằng trung gian myGreeting để lấy giá trị từ biến Optional, giúp loại bỏ bước unwrap biến greeting.

Biến Tuples

Trong các ứng dụng iOS, khi cần sử dụng lại một nhóm các giá trị từ nhiều biến đơn lẻ, biến kiểu Tuples (biến bộ) sẽ trở thành lựa chọn phù hợp.

In iOS, you can declare tuples in several ways: First, you can use an implicit type declaration with `let error501 = (501, "Not implemented")`, where `error501` is a tuple variable with an inferred type Alternatively, you can explicitly define the tuple's type using `let error501: (Int, String) = (501, "Not implemented")` Another method is to create a named tuple with `let error501 = (code: 501, description: "Not implemented")`, allowing for clearer access to the elements Lastly, you can also specify the type for a named tuple with `let error501: (code: Int, description: String) = (501, "Not implemented")`.

Ví dụ 1 Khai báo theo cách 1: let person = ("Nguyen Van A", "34, Linh Dong, Thu Duc", 0976555555) print("Ho va ten \(person.0)") print("Dia chi \(person.1)") print("So dien thoai \(person.2)")

Biến person là một kiểu tuples với ba trường giá trị: Họ và tên, Địa chỉ và Số điện thoại Khi sử dụng tuples không có tên, ta có thể truy xuất từng trường bằng cách sử dụng chỉ số, với các trường được đánh chỉ số từ 0 đến n-1.

Bạn có thể gán biến `person` cho một tuple chứa tên, địa chỉ và số điện thoại, ví dụ: `let person = ("Nguyen Van A", "34, Linh Dong, Thu Duc", 0976555555)` Sau đó, bạn có thể truy xuất các thông tin này bằng cách sử dụng cú pháp `let (name, adress, phone) = person` Cuối cùng, bạn có thể in ra các thông tin như sau: `print("Ho va ten \(name)")`, `print("Dia chi \(adress)")`, và `print("So dien thoai \(phone)")`.

In Swift, you can define a tuple with named elements for better clarity and organization For example, you can create a tuple called 'person' that includes a name, address, and phone number, like this: `let person: (name: String, address: String, phone: Int) = ("Nguyen Van A", "34, Linh Dong, Thu Duc", 0976555555)` You can then easily access and print each element using the names: `print("Full Name: \(person.name)")`, `print("Address: \(person.address)")`, and `print("Phone Number: \(person.phone)")` This approach enhances code readability and maintainability.

Chuỗi thoát trong Swift

Trong Swift, khi làm việc với chuỗi, việc chèn các ký tự đặc biệt như ký tự xuống dòng, ký tự '\' hay các dấu nháy đơn và kép là rất cần thiết Để thực hiện điều này, bạn cần sử dụng các chuỗi thoát tương ứng để đảm bảo tính chính xác của chuỗi.

STT Chuỗi thoát Mô tả

1 \\ Đưa ký tự đặc biệt ‘\’ vào trong chuỗi

2 \n Đưa ký tự xuống dòng vào trong chuỗi

3 \r Đưa ký tự Về đầu dòng vào trong chuỗi

4 \t Đưa ký tự tab vào trong chuỗi

5 \' Đưa ký tự ‘'’ vào trong chuỗi

6 \" Đưa ký tự ‘"’ vào trong chuỗi

Các loại toán tử trong ngôn ngữ Swift

Trong Swift có nhiều loại toán tử khác nhau: a) Các phép toán số học

STT Phép toán Mô tả

5 % a % b Phép lấy phần dư của phép chia a cho b

6 ++ a++ Phép tăng giá trị a lên 1

7 a—Phép giảm giá trị a đi 1 b) Các phép so sánh

STT Phép toán Mô tả

1 == a == b Kết quả phép so sánh là true nếu giá trị a bằng giá trị b, ngược lại sẽ là false

2 != a != b Kết quả phép so sánh là true nếu giá trị của a khác với giá trị của b và ngược lại sẽ là false

3 > a > b Kết quả phép so sánh là true nếu giá trị của a lớn hơn giá trị của b và ngược lại sẽ là false

4 < a < b Kết quả phép so sánh là true nếu giá trị của a nhỏ hơn giá trị của b và ngược lại sẽ là false

5 >= a >= b Kết quả phép so sánh là true nếu giá trị của a lớn hơn hoặc bằng giá trị của b và ngược lại sẽ là false

Kết quả so sánh 6 2 Phép dịch 2 bit sang phải (kết quả 00 0110 00) e) Các phép gán

STT Phép toán Mô tả

1 = c = a + b Kết quả phép a + b được gán cho biến c

6 %= a %= b Thực hiện giống như a = a % b f) Các phép tập hợp

STT Phép toán Mô tả

Rẽ nhánh và vòng lặp trong Swift

Cú pháp rẽ nhánh loại 1: Câu lệnh if if { conditional code }

Nếu điều kiện đúng (giá trị true), đoạn lệnh trong mã điều kiện sẽ được thực hiện Ngược lại, nếu điều kiện sai, đoạn lệnh đó sẽ bị bỏ qua và không có lệnh nào được thực hiện.

Hình 1.2.7.1 Hoạt động của câu lệnh rẽ nhánh loại 1

Cú pháp rẽ nhánh loại 2: Câu lệnh if…else if {

Nếu biểu thức điều kiện nhận giá trị true (biểu thức điều kiện đúng) thì đoạn lệnh sẽ được thực hiện, ngược lại đoạn lệnh

sẽ được thực hiện

Hình 1.2.7.2 Hoạt động của câu lệnh rẽ nhánh loại 2

Cú pháp rẽ nhánh loại 3: Câu lệnh if … else if … else if … else

Trường hợp có nhiều hơn 2 nhánh rẽ, chúng ta có thể sử dụng câu lệnh rẽ nhánh loại 3 theo cú pháp sử dụng như sau: if {

Nếu biểu thức điều kiện nhận giá trị true, thì đoạn lệnh tương ứng

Trong quá trình thực hiện, sẽ được áp dụng, tương tự cho n-1 trường hợp còn lại Đặc biệt, đoạn lệnh trong chỉ được thực hiện khi tất cả các biểu thức điều kiện từ đến đều không thỏa mãn, tức là đều có giá trị false.

Lưu ý: Các câu lệnh rẽ nhánh lại có thể được lồng trong nhau

Cú pháp lệnh rẽ nhánh nhiều lựa chọn dạng switch

Trường hợp có nhiều hơn 2 nhánh rẽ, thường chúng ta sẽ dùng câu lệnh rẽ nhánh dạng

3 như ở trên hoặc sử dụng câu lệnh rẽ nhánh dạng switch theo cú pháp như sau: switch { case :

Khi sử dụng câu lệnh rẽ nhánh switch, điều kiện được thay thế bằng một giá trị Dựa vào giá trị này, câu lệnh sẽ thực hiện đoạn code tương ứng: nếu giá trị bằng , đoạn code trong sẽ được thực hiện, và tương tự cho các trường hợp khác Nếu không có trường hợp nào phù hợp, đoạn lệnh trong sẽ được thực hiện, mặc dù phần này là tùy chọn Đặc biệt, trong ngôn ngữ Swift, câu lệnh switch không yêu cầu sử dụng câu lệnh break để thoát khỏi nhánh hiện tại, điều này khác với các ngôn ngữ như Java hay C++.

Một số ví dụ về các câu lệnh rẽ nhánh trong Swift

Tạo một Playground và nhập đoạn mã sau: thay đổi giá trị biến "diem" từ 0-10 mỗi lần chạy để quan sát kết quả Hãy thử thay đổi giá trị biến "diem" ra ngoài khoảng 0-10 và xem phản hồi Ví dụ mã: ```swiftimport UIKitvar diem = 6if diem >= 0 && diem < 4 { print("Diem \(diem) la loai Yeu")}```

} else if diem >= 4 && diem < 7 { print("Diem \(diem) la loai Trung binh")

} else if diem >= 7 && diem < 8 { print("Diem \(diem) la loai Kha")

} else if diem >= 8 && diem < 9 { print("Diem \(diem) la loai Gioi")

} else if diem >= 9 && diem {

Trong lập trình, tên hàm được đặt theo quy tắc cụ thể, trong đó CacThamSo đại diện cho các giá trị được truyền vào hoặc ra từ hàm, và KieuGiaTriTraVe xác định kiểu dữ liệu của giá trị trả về từ hàm.

CacLenhTrongThanHam bao gồm tất cả các câu lệnh cần thiết để thực hiện chức năng của hàm, từ việc nhận các tham số đầu vào (input) cho đến việc tính toán và xử lý chúng theo một thuật toán cụ thể Kết quả đầu ra (output) được trả về theo yêu cầu của hàm thông qua lệnh return trong thân hàm.

Ví dụ 1: Viết hàm trả về kết quả là phép nhân của hai giá trị nguyên a và b bất kỳ import UIKit

// Định nghĩa hàm func mul(a:Int, b:Int) -> Int { return a*b

// Lời gọi hàm let result = mul(a: 10, b: 20)

Hàm mul được định nghĩa với hai tham số kiểu Int là a và b, và trả về một giá trị kiểu Int Hàm thực hiện phép nhân giữa a và b (a*b) và sử dụng câu lệnh return để trả lại kết quả Sau khi gọi hàm, biến result sẽ lưu trữ giá trị trả về từ hàm mul, tương ứng với a*b.

Lưu ý: Nếu hàm không có giá trị trả về (hoặc không có tham số) thì có thể bỏ [->

Hàm chào trong lập trình không nhận giá trị đầu vào và không trả về giá trị đầu ra, chỉ thực hiện chức năng hiển thị lời chào "Hello" trên màn hình Ví dụ: `func chao() { print("Hello") }`.

Truyền tham số vào/ra và Sử dụng hàm trong Swift

Trong ngôn ngữ lập trình Swift, khái niệm Tên ngoài và Tên trong được định nghĩa rõ ràng trong hàm Khi gọi hàm, người dùng có thể chỉ định rõ ràng giá trị cho các tham số bằng cách sử dụng cú pháp như mul(a: 10, b: 20), giúp tránh nhầm lẫn thứ tự tham số Tên tham số trong thân hàm, như trong câu lệnh return a*b, được gọi là Tên trong, trong khi tên tham số trong lời gọi hàm được gọi là Tên ngoài Mặc định, Tên trong và Tên ngoài sẽ trùng nhau Tuy nhiên, nếu muốn chỉ định Tên trong mà không cần Tên ngoài, có thể định nghĩa hàm theo cách khác.

// Định nghĩa hàm func mul(_ a:Int, _ b:Int) -> Int { return a*b

// Lời gọi hàm let resultInt = mul(10, 20)

Trường hợp ta không muốn dùng Tên ngoài và Tên trong như mặc định nữa mà tự định nghĩa Tên ngoài khác thì ta làm như sau: import UIKit

// Định nghĩa hàm func mul(giaTriA a:Int, giaTriB b:Int) -> Int { return a*b

// Lời gọi hàm let resultInt = mul(giaTriA: 10, giaTriB: 20)

Trường hợp này giaTriA và giaTriB là các Tên ngoài tương ứng với các tham số a và b (Tên trong) của định nghĩa hàm

Trong Swift, việc truyền tham số cho các hàm có thể chia thành hai loại: truyền theo tham trị và truyền theo tham biến Truyền theo tham trị là khi giá trị từ bên ngoài được đưa vào hàm thông qua các tham số mà không cần lấy giá trị ra Ngược lại, truyền theo tham biến cho phép đưa giá trị từ bên ngoài vào và có thể thay đổi giá trị đó bên trong hàm.

Trong lập trình, hàm có thể nhận tham số đầu vào và trả giá trị đầu ra Mỗi ngôn ngữ lập trình có cú pháp và cách xử lý riêng cho việc này Ví dụ, trong hàm mul, các tham số a và b là tham số đầu vào nhưng không thể lấy giá trị đầu ra trực tiếp Thay vào đó, kết quả của phép nhân a*b được trả về thông qua câu lệnh return Đối với các hàm phức tạp hơn với nhiều giá trị trả về, việc lấy giá trị thường được thực hiện bằng cách truyền theo tham biến.

Để hoán vị giá trị cho hai biến a và b, ta cần viết một hàm nhận hai tham số đầu vào là a và b Hàm này sẽ thực hiện việc hoán đổi giá trị giữa chúng trong thân hàm mà không thể trả về giá trị ra ngoài bằng câu lệnh return Do đó, ta sử dụng truyền tham biến cho hàm hoanVi Cụ thể, cú pháp của hàm sẽ là: func hoanVi(giaTriA a: inout Int, giaTriB b: inout Int) { let temp = a; a = b; b = temp }.

Để khai báo tham số kiểu dữ liệu trong hàm, ta sử dụng từ khóa inout để chỉ định rằng các tham số a và b là tham biến, cho phép truyền giá trị vào và lấy giá trị ra Khi đó, a và b sẽ không còn là tham số cục bộ mà sẽ tham chiếu đến hai biến bên ngoài, giúp giữ lại mọi thay đổi sau khi thoát khỏi hàm Ví dụ, khi gọi hàm với var a = 10 và var b = 20, ta sử dụng hoanVi(giaTriA: &a, giaTriB: &b) để truyền vào hai biến a và b với ký tự tham chiếu ‘&’ Nếu truyền theo tham trị, giá trị sẽ được sao chép vào các biến cục bộ, dẫn đến lỗi do biến hằng không thể thay đổi.

Truyền tham số kiểu generic trong Swift

Trong ví dụ trên, hàm mul chỉ hỗ trợ phép nhân cho các số nguyên (Int) Để mở rộng hàm này cho các kiểu dữ liệu khác như Float hoặc Double, ta cần định nghĩa hàm kiểu generic với T là kiểu Numeric Cả tham số đầu vào và giá trị trả về sẽ được xác định dựa trên kiểu T.

// Định nghĩa hàm func mul(a:T, b:T) -> T { return a*b

// Lời gọi hàm let resultInt = mul(a: 10, b: 20) let resultDouble = mul(a: 20.2, b: 10)

Chúng ta có thể điều chỉnh hàm hoanVi trong ví dụ 2 để hoán vị cho bất kỳ biến nào với mọi kiểu dữ liệu Cú pháp của hàm sẽ như sau: func hoanVi(giaTriA a: inout T, giaTriB b: inout T) { let temp = a; a = b; b = temp }.

} var a = "Hello" var b = "Chao" hoanVi(giaTriA: &a, giaTriB: &b) print("Loi chao a: \(a), b: \(b)")// Loi chao a: Chao, b: Hello

Trường hợp đặc biệt, nếu số lượng tham số đưa vào hàm không biết trước, ta có thể khai báo như sau:

Ví dụ 3: Viết hàm thực hiện tính tổng của n số bất kỳ import UIKit func genericSum(_ numbers: T ) -> T { var sum:T = 0 for num in numbers { sum += num

} let result1 = genericSum(1, 3, 5, 7, 9.1) // 25.1 let result2 = genericSum(20.2, 3, 60, 70.4, 6, 5.5, 9) // 174.1

Lưu ý: Khai báo “…” sau kiểu của tham số (giống với Java) là chìa khoá ví dụ này

Một biến không chỉ giới hạn trong việc chứa giá trị cho các kiểu dữ liệu cụ thể, mà còn có khả năng "trỏ" đến một hàm khác Để thực hiện điều này, biến cần được khai báo với kiểu tương ứng.

Kiểu hàm, hay còn gọi là prototype của hàm, cần phải tương thích với hàm mà nó sẽ "trỏ" tới Cú pháp để định nghĩa kiểu hàm bao gồm các tham số và kiểu trả về, được viết theo dạng: (Kiểu tham số1, Kiểu tham số 2, … Kiểu tham số n) -> Kiểu trả về.

Định nghĩa và sử dụng Cấu trúc, Lớp đối tượng trong Swift

Trong Swift, việc định nghĩa lớp và sử dụng các đối tượng tuân thủ các nguyên tắc cơ bản của lập trình hướng đối tượng, tương tự như nhiều ngôn ngữ lập trình khác Phần này sẽ tập trung vào cú pháp định nghĩa và cách sử dụng các đối tượng trong Swift, mà không nhắc lại kiến thức nền tảng về lập trình hướng đối tượng Ngoài ra, cũng sẽ đề cập đến định nghĩa và sử dụng cấu trúc trong Swift.

Lớp và cấu trúc đều có nhiều điểm tương đồng nhưng cũng tồn tại những khác biệt rõ rệt Cả hai đều có vai trò quan trọng trong việc tổ chức và phân loại thông tin, tuy nhiên, lớp mang những đặc điểm riêng mà cấu trúc không có.

- Đều định nghĩa các thuộc tính và phương thức của chúng

- Đều định nghĩa các hàm khởi tạo

- Đều có thể mở rộng chức năng bằng từ khoá extension

Một số điểm khác biệt chỉ có ở lớp đối tượng đó là:

- Tính kế thừa của lớp đối tượng

- Hàm huỷ để giải phóng tài nguyên

- Một đối tượng có thể có nhiều tham chiếu tại một thời điểm Định nghĩa lớp trong Swift

Dùng cú pháp sau: class : {

Trong Swift, khi định nghĩa một lớp, là tên lớp, là các lớp cha mà lớp này kế thừa, và nếu có nhiều lớp, chúng sẽ được phân tách bằng dấu ‘,’; phần chứa mã chương trình để định nghĩa các thuộc tính và phương thức trong lớp.

Dùng cú pháp sau: struct {

Trong đó: là tên của cấu trúc và sẽ là đoạn chương trình định nghĩa các thuộc tính và phương thức của cấu trúc

Trong ngôn ngữ Swift, mọi thuộc tính trong class hoặc struct cần phải được khởi tạo giá trị nếu không phải là kiểu Optional, tương tự như việc sử dụng Constructor trong Java hoặc C++ Cú pháp để khai báo hàm khởi tạo là: init() {

Trong đó, là các tham số được truyền vào hàm khởi tạo, trong khi là những câu lệnh sử dụng các giá trị này để gán giá trị cho từng thuộc tính của lớp hoặc cấu trúc.

Ví dụ 1: Định nghĩa và sử dụng cấu trúc Resolution và lớp VideoMode như dưới đây import UIKit

// Cấu trúc Resolution lưu độ phân giải của Video struct Resolution { var width: Int var height: Int init(width: Int, height: Int) { self.width = width self.height = height

} class VideoMode { // Lớp đối tượng VideoMode var resolution: Resolution var interlaced: Bool var frameRate: Double var name: String?

// Hàm khởi tạo các thuộc tính cho lớp đối tượng init(resolution: Resolution, interlaced: Bool, frameRate: Double) { self.resolution = resolution self.interlaced = interlaced self.frameRate = frameRate

} let videoResolution = Resolution(width: 640, height: 480) let videoMode = VideoMode(resolution: videoResolution, interlaced: false, frameRate: 25.0) //videoMode là một đối tượng (instance) của lớp VideoMode

Trong Swift, biến hằng videoResolution thuộc loại Value Types, nghĩa là khi được gán cho biến khác hoặc truyền vào hàm, giá trị của nó sẽ được sao chép Ngược lại, biến hằng videoMode là đối tượng của một lớp và thuộc loại Reference Types, cho phép nhiều tham chiếu đến cùng một đối tượng Điều này có nghĩa là mặc dù videoMode là biến hằng, ta vẫn có thể thay đổi giá trị của đối tượng mà nó tham chiếu đến, ví dụ như câu lệnh videoMode.interlaced = true là hợp lệ Tuy nhiên, việc thay đổi giá trị của videoResolution như videoResolution.width = 1080 là không hợp lệ do nó thuộc loại Value Types.

Phép so sánh định danh đối tượng trong Swift

Khi một đối tượng có nhiều tham chiếu tại một thời điểm, việc xác định hai tham chiếu có cùng trỏ đến một đối tượng hay không không thể sử dụng phép so sánh thông thường như == hoặc != Thay vào đó, cần sử dụng các toán tử === (so sánh bằng) và !== (so sánh khác) Ví dụ, nếu ta khai báo let v = videoMode, thì phép so sánh (v === videoMode) sẽ trả về true, trong khi (v !== videoMode) sẽ trả về false, vì cả v và videoMode đều tham chiếu đến cùng một đối tượng.

To compare two different instances of a structure or class based on their values, we need to redefine the comparison operators (== or !=) As illustrated in the first example, we can enhance their functionality by implementing the Equatable protocol This can be achieved by defining a static function that checks for equality, ensuring that both the height and width of the instances are equal.

} static func != (left: Resolution, right: Resolution) -> Bool { return !(left == right)

} extension VideoMode: Equatable { static func == (left: VideoMode, right: VideoMode) -> Bool { return (left.resolution == right.resolution) &&

(left.interlaced == right.interlaced) && (left.frameRate == right.frameRate) && (left.name == right.name)

} static func != (left: VideoMode, right: VideoMode) -> Bool { return !(left == right)

} let anotherVideoResolution = videoResolution let anotherVideoMode = VideoMode(resolution: videoResolution, interlaced: true, frameRate: 30)

// So sánh bằng cho hai biến thể khác nhau của struct => true var equal = (videoResolution == anotherVideoResolution)

// So sánh bằng cho hai đối tượng khác nhau của lớp => false equal = (videoMode == anotherVideoMode)

Ép kiểu

Khác với nhiều ngôn ngữ lập trình hướng đối tượng, Swift hỗ trợ việc ép kiểu hai chiều Để hiểu rõ hơn về cơ chế ép kiểu trong Swift, ta có thể xem xét các lớp thông qua một ví dụ cụ thể.

} class Movie: MediaItem { var director: String init(name: String, director: String) { self.director = director super.init(name: name)

} class Song: MediaItem { var artist: String init(name: String, artist: String) { self.artist = artist super.init(name: name)

Movie(name: "Casablanca", director: "Michael Curtiz"),

Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),

Movie(name: "Citizen Kane", director: "Orson Welles"),

Song(name: "The One And Only", artist: "Chesney Hawkes"),

Song(name: "Never Gonna Give You Up", artist: "Rick Astley")

Kiểm tra kiểu với “is”: Mảng library trên đây được Swift biên dịch thành mảng các

MediaItem cho dù các đối tượng được tham chiếu bên trong nó vẫn là Movie và Song

Do đó, khi sử dụng chúng cần nhận diện xem chúng thuộc lớp đối tượng nào (Movie hay Song) sử dụng “is”

// Kiểm tra kiểu các phần tử trong mảng dùng is var movieCount = 0 var songCount = 0 for item in library { if item is Movie { movieCount += 1

} else if item is Song { songCount += 1

} Ép kiểu dạng 1: Downcasting dùng “as?”

// Ép kiểu Downcasting for item in library { if let movie = item as? Movie { print("Movie: \(movie.name), dir \(movie.director)")

} else if let song = item as? Song { print("Song: \(song.name), by \(song.artist)")

Khi sử dụng phép ép kiểu với biến tham chiếu item có thể trỏ đến nhiều loại đối tượng thuộc lớp con như Movie hoặc Song, kết quả trả về có thể là một đối tượng đúng loại hoặc nil nếu ép sai loại Do đó, kết quả của phép ép kiểu này là một biến Optional, và chúng ta cần unwrap nó bằng cách sử dụng cấu trúc if … let.

Khi đảm bảo rằng phép ép kiểu luôn trả về kết quả đúng, chúng ta có thể sử dụng “as!” để vừa thực hiện ép kiểu vừa unwrap kết quả Cần lưu ý rằng đối tượng sau khi ép kiểu không thay đổi Ép kiểu dạng 2 sử dụng “as” để thực hiện phép ép kiểu.

Bổ sung thêm đoạn lệnh sau vào ví dụ trên:

In Swift, you can create a mixed array that holds various types of elements using the syntax `var things = [Any]()` This array can include integers, floating-point numbers, strings, tuples, custom objects, and even closures For example, you can append an integer like `0`, a double such as `0.0`, and a string like `"hello"` Additionally, you can store tuples, such as `(3.0, 5.0)`, and instances of custom classes, like a `Movie` object initialized with a name and director You can also include closures that take parameters and return values, exemplified by appending a closure that greets a given name.

Trong Swift, có hai kiểu dữ liệu đặc biệt là Any và AnyObject Kiểu Any có khả năng biểu diễn bất kỳ biến thể nào của mọi kiểu dữ liệu, bao gồm cả kiểu hàm, trong khi AnyObject chỉ có thể đại diện cho các đối tượng Ví dụ, mảng things là một mảng hỗn hợp, cho phép chứa nhiều loại dữ liệu khác nhau.

Trong Swift, một mảng có thể chứa nhiều loại phần tử khác nhau như 2 phần tử số nguyên, 2 phần tử số thực, 1 phần tử chuỗi, 1 phần tử kiểu tuple (Double, Double), 1 đối tượng kiểu Movie và một biến hàm dưới dạng Closure Tuy nhiên, khi đưa một biến kiểu Optional vào mảng, Swift sẽ đưa ra cảnh báo Để tránh tình trạng này, bạn nên ép kiểu tường minh biến Optional về dạng Any trước khi thêm vào mảng, ví dụ: let optionalNumber: Int? = 3, things.append(optionalNumber) sẽ gây cảnh báo, trong khi things.append(optionalNumber as Any) sẽ không có cảnh báo.

Khi sử dụng mảng hỗn hợp ở trên, ta cần ép kiểu chúng cho phù hợp:

When working with mixed arrays in Swift, it's essential to cast each element to its correct type The code iterates through the elements, using a switch statement to handle various cases For example, it identifies integers and doubles, printing specific messages for each It also accommodates strings and tuples, displaying their values accordingly Additionally, the code recognizes custom types like a Movie object, outputting relevant details such as the movie's name and director Finally, it includes a case for function types, demonstrating the versatility of type casting in Swift.

Protocol và cơ chế Delegate trong Swift

Trong Swift, Protocol là một kiểu dùng để định nghĩa các chức năng mà không xác định việc lưu trữ dữ liệu Điều này có nghĩa là Protocol cho phép chúng ta định nghĩa các API, giúp người dùng tự do xác định các chức năng mà họ mong muốn Nhờ đó, chương trình trở nên linh hoạt hơn, tương tự như việc sử dụng interfaces trong ngôn ngữ Java.

Cú pháp định nghĩa cho Protocol có dạng: protocol {

In this context, the define the prototype of functions (APIs) for a protocol The following example illustrates a Protocol that includes a random function for a Generator This function does not take any parameters and returns a value of type Double, serving merely as a prototype: protocol RandomNumberGenerator { func random() -> Double.

Tùy thuộc vào nhu cầu sử dụng, các lớp kế thừa sẽ tự định nghĩa nhiều loại hàm sinh số ngẫu nhiên khác nhau dựa trên các thuật toán đa dạng Chẳng hạn, chúng ta có thể tạo ra một lớp mang tên LinearCongruentialGenerator để sinh ra các số ngẫu nhiên nằm trong một khoảng giá trị xác định.

0 đến 1 (nhỏ hơn 1) theo thuật toán đồng dư tuyến tính (Linear congruential generator):

// Algorithm linear congruential generator class LinearCongruentialGenerator: RandomNumberGenerator { var lastRandom = 42.0 let m = 139968.0 let a = 3877.0 let c = 29573.0

// Thực hiện chức năng hàm random func random() -> Double { lastRandom = ((lastRandom * a + c)

} let generator = LinearCongruentialGenerator() print("Here's a random number: \(generator.random())")

// Prints "Here's a random number: 0.3746499199817101" print("And another one: \(generator.random())")

Lưu ý: Nếu muốn các API của protocol có thể thay đổi các biến thể của lớp định nghĩa nó thì cần đưa từ khoá mutating vào trước func

Cơ chế Delegate (Uỷ quyền) trong Swift

Trong Swift, có nhiều thư viện đối tượng được cung cấp sẵn, nhưng không phải tất cả các chức năng đều đáp ứng được nhu cầu đa dạng của người dùng Để khắc phục điều này, cơ chế ủy quyền là một giải pháp hiệu quả giúp tùy chỉnh và mở rộng chức năng của các thư viện này.

Để mô phỏng mối quan hệ giữa cửa hàng sản xuất bánh quy và cửa hàng bán bánh quy, chúng ta cần định nghĩa cấu trúc dữ liệu cho một chiếc bánh quy Cấu trúc này bao gồm kích thước của bánh quy, được khởi tạo với giá trị mặc định là 5, và một thuộc tính cho biết liệu bánh quy có chứa chocolate chips hay không, với giá trị mặc định là false.

Tiếp theo, chúng ta định nghĩa cửa hàng sản xuất bánh quy như sau: class Bakery

{ var cookie = Cookie() cookie.size = 6 cookie.hasChocolateChips = true

Lớp này có một hàm để tạo ra bánh quy theo yêu cầu, nhưng việc bán bánh lại thuộc chức năng của cửa hàng bán bánh, không phải cửa hàng sản xuất Điều này gây khó khăn trong việc tổ chức chương trình, vì bánh làm xong cần được bán, trong khi chức năng bán hàng không thể định nghĩa trong cửa hàng sản xuất Cơ chế ủy quyền sẽ giúp giải quyết vấn đề này.

Trước tiên, ta cần định nghĩa Protocol có chứa chức năng bán của cửa hàng bán bánh: protocol BakeryDelegate { func sellingCookie(_ cookie: Cookie)

Hàm sellingCookie() sẽ được kích hoạt mỗi khi cửa hàng làm bánh hoàn thành sản xuất Để thực hiện điều này, chúng ta cần điều chỉnh định nghĩa cho lớp cửa hàng làm bánh như sau: class Bakery.

{ var delegate:BakeryDelegate? func makeCookie()

{ var cookie = Cookie() cookie.size = 6 cookie.hasChocolateChips = true delegate?.sellingCookie(cookie)

Trong bài viết này, chúng ta có hai điều chỉnh quan trọng: đầu tiên, thêm một biến delegate với kiểu Protocol mới được định nghĩa, bao gồm chức năng bán bánh; thứ hai, gọi chức năng bán bánh mỗi khi cửa hàng sản xuất xong Cửa hàng bán bánh không cần biết cách thức bán bánh, mà sẽ ủy quyền cho cửa hàng thực hiện nhiệm vụ này Định nghĩa lớp cửa hàng bán bánh như sau: class CookieShop: BakeryDelegate.

{ print("Buy cookie, with size \(cookie.size)!")

Cuối cùng, để chạy chương trình, bạn cần tạo một cửa hàng bán bánh bằng cách sử dụng `let shop = CookieShop()` Tiếp theo, tạo một cửa hàng làm bánh với `let bakery = Bakery()` Sau đó, uỷ quyền cho cửa hàng bán bánh bằng cách gán `bakery.delegate = shop` Cuối cùng, thực hiện sản xuất bánh với lệnh `bakery.makeCookie()`.

Sau khi bánh được sản xuất xong, cửa hàng sẽ tiến hành bán ngay lập tức, và thông báo trên màn hình sẽ hiển thị: "Mua bánh quy, kích thước 6!"

Câu hỏi và bài tập chương 1

1 Khi đoạn code sau được thực hiện, giá trị biến hằng j là bao nhiêu? let i = "5" let j = i + i

2 Cho biết đoạn code sau sẽ cho kết quả thế nào? let names = ["Chris", "Joe", "Doug", "Jordan"] if let name = names[1] { print("Brought to you by \(name)")

3 Đoạn code sau đây sẽ cho kết quả thế nào? func sayHello(to name: String) -> String { return "Howdy, \(name)!"

4 Khi thực hiện đoạn code sau sẽ thu được gì? for i in 3 1 { print(i)

5 Cho biết kết quả thực hiện các câu lệnh sau đây là gì? struct Starship { var name: String

} let tardis = Starship(name: "TARDIS") var enterprise = tardis enterprise.name = "Enterprise" print(tardis.name)

6 Khi thực hiện đoạn chương trình sau, kết quả thu được là gì? let names = ["Serenity", "Sulaco", "Enterprise", "Galactica"] for name in names where name.hasPrefix("S") { print(name)

7 Cho biết sau khi thực hiện đoạn code sau ta thu được gì? var motto = "Bow ties are cool" motto.replacingOccurrences(of: "Bow", with: "Neck") print(motto)

8 Hãy cho biết nếu thực hiện đoạn code sau đây, ta thu được gì? final class Dog { func bark() { print("Woof!")

} class Corgi : Dog { override func bark() { print("Yip!")

} let muttface = Corgi() muttface.bark()

In the given code snippet, the variable `third` will contain the combined values of the arrays `first` and `second` Specifically, `third` will result in an array with the elements `["Sulaco", "Nostromo", "X-Wing", "TIE Fighter"]`, effectively merging the two original arrays into one cohesive list.

10 Hãy cho biết kết quả khi thực hiện đoạn chương trình sau? let i = 3 switch i { case 1: print("Number was 1") case 2: print("Number was 2") case 3: print("Number was 3")

11 Viết hàm thực hiện phép nhân cho n số bất kỳ!

12 Cho biết kết quả thực hiện đoạn code sau đây? class Starship { var name: String

} let tardis = Starship(name: "TARDIS") var enterprise = tardis enterprise.name = "Enterprise" print( tardis name )

13 Cho biết kết quả thực hiện đoạn code sau? Hãy so sánh với câu 12 và câu 5! class Starship { var name: String init(name: String) { self.name = name

} let tardis = Starship(name: "TARDIS") var enterprise = tardis enterprise.name = "Enterprise" print( tardis name )

14 Sau khi thực hiện đoạn chương trình sau, ta thu được gì? Giải thích! func sum(numbers: Int ) -> Int { var result = 0 for number in numbers { result += number

15 Cho biết kết quả nếu thực hiện đoạn chương trình sau? var crew = ["Captain": "Malcolm", "Doctor": "Simon"] crew = [:] print(crew.count)

Khi thực hiện đoạn chương trình trên, kết quả sẽ là "X was 556" Điều này xảy ra vì biến `point` được gán giá trị là (556, 0), và trong cấu trúc `switch`, trường hợp đầu tiên khớp với giá trị của `point`, do đó giá trị của `x` được in ra.

17 Cho biết kết quả nếu thực hiện đoạn chương trình sau? Giải thích! func greet(names: String ) {

44 print("Criminal masterminds:", names.joined(separator: ", "))

} greet(names: "Malcolm", "Kaylee", "Zoe")

18 Cho biết nếu thực hiện đoạn chương trình sau sẽ thu được kết quả gì? Giải thích! struct Spaceship { var name: String func setName(_ newName: String) { name = newName

} var enterprise = Spaceship(name: "Enterprise") enterprise.setName("Enterprise A") print(enterprise.name)

19 Cho biết nếu chạy đoạn chương trình sau, chuyện gì sẽ xảy ra? Giải thích! class Starship { var name: String override init(initialName: String) { name = initialName

} let serenity = Starship(initialName: "Serenity") print(serenity.name)

In the given code snippet, the variable `name` is set to "Simon" When the switch statement is executed, it matches the case "Simon" and triggers the `fallthrough`, which means it will continue to the next case without checking the condition Consequently, it will print "Crew" since it falls through to the next case that includes "Malcom", "Zoe", and "Kaylee" If the name had not matched any case, it would have printed "Not crew".

21 Cho biết biến kết quả (result) khi chạy đoạn chương trình sau? Giải thích! let names: [String?] = ["Barbara", nil, "Janet", nil, "Peter", nil,

"George"] let result = names.compactMap { $0 }

In the provided code snippet, the variable `testVar` is assigned the value of each element in the `names` dictionary during the enumeration process Since `names` is defined as a dictionary with string keys and values, the type of `testVar` is inferred to be a string This is because, in Swift, the type of a variable is determined by the value it holds at the time of assignment, which in this case is the string values associated with the keys "Pilot" and "Doctor."

23 Cho biết kết quả nếu chạy đoạn chương trình sau? Giải thích! let status = "shiny" for (position, character) in status.reversed().enumerated() where position % 2 == 0 { print("\(position): \(character)")

24 Cho biết kết nếu chạy đoạn chương trình sau? Giải thích! struct User { let name: String

} let users = [User(name: "Eric"), User(name: "Maeve"), User(name:

"Otis")] let mapped = users.map(\.name) print(mapped)

25 Hãy giải thích kết quả khi thực hiện đoạn chương trình sau? struct TaylorFan { static var favoriteSong = "Shake it Off" var name: String var age: Int

} let fan = TaylorFan(name: "James", age: 25) print(fan.favoriteSong)

27 Nếu thực hiện đoạn chương trình sau sẽ có kết quả thế nào? Giải thích! func square(_ value: T) -> T { return value * value

28* Hãy giải thích kết quả nếu thực hiện đoạn chương trình sau? let i = 101 if case 100 101 = i { print("Hello, world!")

THIẾT KẾ GIAO DIỆN VÀ XỬ LÝ SỰ KIỆN TRÊN IOS

Thiết kế giao diện với Storyboard

2.1.1 Các thành phần chính trong iOS Project

Sau khi tạo mới một Project trong Xcode, màn hình giao diện giống như sau xuất hiện:

Hình 2.1.1.1 Các thành phần chính của iOS Project

Màn hình giao diện chính có thể chia thành 4 vùng khác biệt: Navigation Area, Editor Area, Debug Area và Utility Area (Hình 2.1.1.1)

Navigation Area cho phép lập trình viên điều hướng đến các file hoặc các chức năng khác nhau trong Xcode Có 9 điều hướng quan trọng:

Project Navigator cho phép người dùng thực hiện các thao tác quản lý file trong dự án, bao gồm mở, thêm, xóa và gom nhóm các file Tính năng này giúp tổ chức mã nguồn hiệu quả và hỗ trợ quá trình viết chương trình một cách thuận lợi hơn.

Source Control Navigator cho phép bạn xem lịch sử thay đổi của các phiên bản của dự án khi sử dụng các phần mềm quản lý phiên bản code như SVN hoặc Git.

- Symbol Navigator: Xem cấu trúc cây tổ chức lớp của ứng dụng

- Find Navigator: Công cụ cho phép tìm kiếm nhanh bên trong Project

- Issue Navigator: Hiện tất cả các cảnh báo (Warnings), các lỗi (errors) mà trình biên dịch tìm thấy trong Project

- Test Navigator: Công cụ cho phép tạo, quản lý, thực hiện và quan sát các Unit Test

- Debug Navigator: Kiểm tra các tiến trình và các thông tin liên quan trong quá trình thực hiện chương trình

- Break Point Navigator: Quản lý tất cả các điểm Breakpoint mà lập trình viên đã thiết lập trong khi debug chương trình trong toàn bộ Project

- Report Navigator: Xem lịch sử biên dịch của Project

Editor Area cho phép lập trình viên biên soạn các tài nguyên trong Project:

- Source Editor: Công cụ cho phép biên soạn và chỉnh sửa code trong ngôn ngữ Swift

Interface Builder là công cụ hỗ trợ lập trình viên trong việc thiết kế và tùy chỉnh giao diện người dùng cho ứng dụng iOS, đồng thời kết nối chúng với mã nguồn của dự án Để sử dụng tính năng này, người dùng cần chọn Main.storyboard trong khu vực Navigation, đây là file chứa thiết kế giao diện của ứng dụng iOS.

- Project Editor: Cho phép xem và thiết lập cấu hình của Project iOS

Debug Area là khu vực hiển thị vết chạy chương trình (call stack) cùng với tên các phương thức đang thực hiện tại điểm dừng Trong khi đó, Utility Area chứa các thuộc tính của từng đối tượng được chọn.

Hình 2.1.1.2 Màn hình Debug chương trình trong iOS

Cấu trúc một iOS Project (Hình 2.1.1.3):

Hình 2.1.1.3 Cấu trúc iOS Project trong Xcode (trái) và trong thư mục dự án (phải)

- Các file dạng *.xcodeproj: Là file quan trọng chứa thông tin cấu trúc và cấu hình của Project Nếu file này bị hỏng thì Project sẽ bị phá huỷ

- Một số file hệ thống mặc định (tại thời điểm này chưa cần thay đổi) như AppDelegate.swift, ScenceDelegate.swift, Info.plist (File cấu hình hệ thống)

- File chứa cấu trúc giao diện người dùng *.storyboard Trong đó Main.storyboard là giao diện chương trình còn LaunchScreen.storyboard là màn hình lúc khởi động

- Assets.xcassets chứa các tài nguyên của ứng dụng như images, fonts, icons…

- ViewController.swift: File chứa code chương trình cho các màn hình cụ thể (trong ví dụ này ứng dụng chỉ có một màn hình duy nhất)

- Products: Nơi chứa các file biên dịch của ứng dụng iOS

2.1.2 Màn hình chờ LaunchScreen.storyboard

Khi chạy một ứng dụng iOS, sẽ có một khoảng thời gian chờ trước khi ứng dụng hoàn tất Màn hình chờ LaunchScreen.storyboard giúp thiết lập giao diện ứng dụng trong thời gian này Để thay đổi giao diện, hãy chọn LaunchScreen.storyboard trong Navigation Area, sau đó trong Utility Area chọn Attributes Inspector và chọn màu nền đỏ Khi chạy thử chương trình, bạn sẽ thấy màn hình màu đỏ xuất hiện trong một khoảng thời gian ngắn trong thời gian chờ.

Hình 2.1.2.1 Cấu hình cho màn hình LaunchScreen trong ứng dụng iOS

2.1.3 Màn hình thiết kế giao diện Main.storyboard

Giao diện chính của màn hình hiện tại trong các ứng dụng iOS hiện đại thường được thiết kế qua Main.storyboard, sử dụng công cụ Interface Builder tích hợp trong Xcode Để bắt đầu, người dùng cần chọn Main.storyboard trong Navigation Area và sử dụng vùng Editor để thiết kế giao diện.

50 màn hình ứng dụng iOS hiện ra, cho phép lập trình viên xây dựng giao diện màn hình (Hình 2.1.3.1)

Hiện tại, Main.storyboard chỉ chứa giao diện thiết kế cho một màn hình, với khung hình trắng giống hình dáng một chiếc iPhone và một mũi tên chỉ vào cạnh bên trái Khung hình trắng là nơi lập trình viên thiết kế các phần tử cho màn hình ứng dụng, và những gì hiển thị trong khung hình sẽ xuất hiện trên iPhone hoặc iPad khi chạy chương trình Mũi tên được gọi là điểm vào của chương trình, chỉ ra rằng màn hình nào có mũi tên chỉ vào sẽ được thực hiện đầu tiên khi chạy ứng dụng, tương tự như các lệnh cấu hình trong Manifest của ứng dụng Android.

Hình 2.1.3.1 Màn hình thiết kế giao diện Main.storyboard

Thiết kế giao diện UI trong iOS rất đơn giản nhờ vào tính năng kéo thả và khả năng thiết lập ràng buộc cho các đối tượng giao diện Xcode cung cấp một thư viện phong phú các đối tượng giao diện, cho phép người dùng dễ dàng kéo thả vào màn hình thiết kế trong Main.storyboard Để truy cập thư viện này, người dùng chỉ cần nhấp vào biểu tượng tương ứng trong Xcode.

Xcode mới), khi đó giao diện cho phép lựa chọn và kéo thả các đối tượng trong thư viện (Object Library) sẽ xuất hiện (Hình 2.1.3.2)

Hình 2.1.3.2 Thư viện các đối tượng cho thiết kế giao diện (Object Library)

Trong màn hình Dialog, lập trình viên có thể sử dụng công cụ tìm kiếm để nhanh chóng tìm tên thư viện cần thiết Ví dụ, khi gõ "but", các đối tượng loại Button như UIButton, Bar Button Item, và Fixed Space Bar Button Item sẽ được lọc ra Chỉ cần kéo-thả đối tượng tương ứng từ thư viện vào màn hình trong Main.storyboard, chúng ta đã hoàn thành giai đoạn 1 của thiết kế giao diện.

Hình 2.1.3.3 Kéo – thả đối tượng từ thư viện vào màn hình thiết kế giao diện

Sau khi đưa đối tượng vào màn hình thiết kế giao diện, bạn có thể dễ dàng điều chỉnh các thuộc tính của nó theo yêu cầu thông qua bảng thuộc tính trong khu vực Utility Area.

- Nhấp đúp chuột lên trên đối tượng: Có thể thay đổi Title của đối tượng

- Lựa chọn đối tượng trên màn hình thiết kế giao diện => Mở Utility Area (Hình 2.1.3.4)

Lựa chọn biểu tượng để vào

Identity Inspector của đối tượng

Để điều chỉnh các thuộc tính liên quan đến lớp đối tượng, người dùng có thể truy cập bảng Attributes Inspector nhằm thay đổi các tính chất như màu sắc, căn chỉnh lề, font chữ và màu chữ Bảng Size Inspector cũng cho phép điều chỉnh kích thước của đối tượng, bao gồm chiều dài và chiều rộng Ngoài ra, còn nhiều bảng thuộc tính khác sẽ được giới thiệu trong các phần tiếp theo Lập trình viên có thể kéo và di chuyển đối tượng trong màn hình thiết kế giao diện đến vị trí mong muốn, hoặc thay đổi kích thước đối tượng bằng cách kéo các điểm giới hạn tại các cạnh hoặc góc, tương tự như trong nhiều ngôn ngữ lập trình khác.

Hình 2.1.3.4 Bảng thuộc tính đối tượng trong thiết kế giao diện iOS

Hình 2.1.3.5 Ví dụ thay đổi thuộc tính đối tượng trực tiếp trên màn hình thiết kế

2.1.4 Case Study: Tạo ứng dụng Calculate

Trong giáo trình này, chúng tôi sẽ hướng dẫn người học cách phát triển hai ứng dụng thực tiễn Ở phần đầu tiên, chúng ta sẽ bắt tay vào thực hiện ứng dụng đầu tiên, cụ thể là giai đoạn 1: Thiết kế giao diện cho ứng dụng máy tính bỏ túi Calculator.

Bước 1: Tạo một iOS Project mới có tên Calculator2020

Hình 2.1.4.1 Tạo ứng dụng iOS Calculator2020 (a)

Hình 2.1.4.2 Tạo ứng dụng iOS Calculator2020 (b)

Hình 2.1.4.3 Tạo ứng dụng iOS Calculator2020 (c)

Bước 2: Thiết kế giao diện cho ứng dụng Calculator2020 giai đoạn 1

First, in the Navigation Area, group the files AppDelegate.swift, SceneDelegate.swift, LaunchScreen.storyboard, Info.plist, and the Assets folder into a single group named System Files (see Figures 2.1.4.4 and 2.1.4.5).

Hình 2.1.4.4 Gom nhóm các file hệ thống vào một nhóm để quản lý

Hình 2.1.4.5 Kết quả gom nhóm các file hệ thống trong System files

Mở thư viện đối tượng và thực hiện kéo thả các đối tượng vào giao diện, sau đó điều chỉnh các thuộc tính của chúng theo ý muốn mà không cần chú ý đến sự cân đối của màn hình hiện tại.

- Đối tượng UIButton: Có Tiltle tương ứng với giá trị số của nó trên máy tính (Ví dụ

7), kích thước font chữ 30 kiểu in đậm đen, kích thước đối tượng 64x64, mầu xám nhạt

- Đối tượng UILabel: Thay đổi kích thước cho phù hợp độ rộng các Button, mầu sắc

Xanh da trời, kích thước font chữ 30 kiểu in đậm, căn lề bên phải, giá trị 0

Kết quả bước đầu của phần thiết kế giao diện sẽ giống như Hình 2.2.2.1 của bước tiếp theo trong mục 2.2.2

Lựa chọn máy ảo loại iPhone 8 Plus và chạy thử để xem kết quả.

Xử lý sự kiện trên iOS

2.2.1 Kết nối các đối tượng với code

Giống như việc phát triển ứng dụng trên Android, trong iOS, bạn cần lấy các đối tượng trong layout trước khi viết mã xử lý Quá trình này được thực hiện bằng cách "Kết nối" các đối tượng trong storyboard với các file xử lý sự kiện kiểu *Controller.swift Để thực hiện kết nối, trước tiên bạn cần chuyển sang chế độ kết nối.

- Trên thanh công cụ chọn Editor => Assistant

Màn hình liên kết code xuất hiện với giao diện storyboard bên trái và code editor cho file *Controller.swift tương ứng với nó bên phải (Hình 2.2.1.1)

Hình 2.2.1.1 Màn hình liên kết code trong lập trình iOS

Trên giao diện storyboard, để liên kết code với đối tượng (Button 7), bạn cần nhấn giữ phím Ctrl và kéo đối tượng từ storyboard vào màn hình code Editor, thả vào vị trí mong muốn Nếu bạn muốn tham chiếu đến đối tượng, hãy thả trong khu Properties của lớp; nếu là hàm thực hiện hành vi, hãy thả trong phần định nghĩa các methods của lớp Khi thực hiện, một Dialog sẽ xuất hiện.

Hình 2.2.1.2 Thiết lập tham số cho việc liên kết code với đối tượng trong layout

- Ở mục Connection: Lựa chọn là Action (liên kết code dạng thực hiện hành vi cho đối tượng trong layout)

- Ở mục Name: Gõ tên của hàm sẽ được gọi mỗi khi đối tượng này được tác động

- Ở mục Type: Lựa chọn kiểu của đối tượng trong layout là UIButton

- Ở mục Event: Lựa chọn loại sự kiện tác động lên đối tượng Ở đây ta lựa chọn Touch

Up Inside có nghĩa là khi bạn chạm vào một đối tượng và giữ tay trên đó mà không di chuyển ra ngoài, sau đó mới thả tay, hàm này sẽ được kích hoạt.

Nhấp chọn Connect để thiết lập liên kết code và ta được kết quả mong muốn trong phần Code Editor (Hình 2.2.1.3)

Hình 2.2.1.3 Kết quả liên kết code dạng hành vi (Action) của đối tượng

Khi chọn Outlet trong mục Connection thay vì Action, một liên kết code dạng tham chiếu sẽ được tạo ra Biến tham chiếu này cho phép chúng ta truy xuất các đối tượng trên layout như một biến tham chiếu đến bất kỳ đối tượng nào.

2.2.2 Cách viết hàm trong iOS với ngôn ngữ Swift

Hàm buttonPress() được tạo ra để liên kết code hành vi với button 7, tương tự như các hàm trong ngôn ngữ Swift Điểm khác biệt là chỉ báo biên dịch @IBAction được đặt trước từ khoá func, cho biết hàm này liên kết với giao diện storyboard của ứng dụng (Interface Builder) Các hàm và phương thức khác vẫn tuân theo quy tắc và cú pháp của Swift đã được trình bày trong chương 1.

Để hiện giá trị của số tương ứng với nút được chạm vào trên màn hình giao diện (số 7) lên màn hình console khi chạy chương trình, chúng ta cần bổ sung hai dòng lệnh vào thân hàm.

@IBAction func buttonPress(_ sender: UIButton) { let digit = sender.currentTitle! print("The button \(digit) duoc nhan!")

Khi chạy chương trình và nhấn nút button 7 trên giao diện máy ảo iPhone, hàm buttonPress(sender:) sẽ được gọi, với biến sender là đối tượng button 7 Biến digit = sender.currentTitle! sẽ chứa giá trị số 7 dưới dạng String, và kết quả hiển thị trên màn hình console sẽ là: “The button 7 duoc nhan!”.

Thực hiện copy nút button 7 và dán vào giao diện và sửa đổi tiêu đề của chúng để có màn hình giao diện như dưới đây (Hình 2.2.2.1)

Trong iOS, khi sao chép các đối tượng trong layout, những thuộc tính liên kết với mã code của chúng cũng được sao chép Ví dụ, tất cả 10 nút trên giao diện đều gọi cùng một hàm buttonPress(sender:) Hãy chạy chương trình trên Simulator iPhone 8 Plus, nhấn vào các nút và rút ra kết luận!

2.2.3 Hoàn thiện ứng dụng Calculate giai đoạn 1

Trong giai đoạn 1 của ứng dụng, khi người dùng chạm vào một nút cụ thể, giá trị số được chạm sẽ hiển thị trực tiếp trên màn hình kết quả của máy tính thay vì trên console của Xcode Ngoài ra, ứng dụng cũng cần xử lý các trường hợp như số không đầu tiên và các phím chức năng.

Đầu tiên, chúng ta cần hiển thị kết quả của các phím số trên màn hình của ứng dụng Calculator Để làm điều này, hãy kết nối màn hình Calculator UILabel với mã code theo dạng tham chiếu và đặt tên là calDisplay.

Bước 1: Cập nhật hàm buttonPess(sender:) để khi người dùng chạm vào bất kỳ nút nào, giá trị số tương ứng sẽ được ghi liên tục lên màn hình của máy tính.

Hình 2.2.2.1 Kết quả điều chỉnh giao diện giai đoạn 1

@IBAction func buttonPress(_ sender: UIButton) { let digit = sender.currentTitle! let currentCalDisplay = calDisplay.text! calDisplay.text = currentCalDisplay + digit

Biến currentCalDisplay sẽ lưu giá trị hiện tại trên màn hình Calculator trước khi kết nối với số vừa được chạm vào Khi chạy chương trình, kết quả thu được chưa đúng mong muốn vì số 0 ban đầu vẫn được nối vào trước số được chọn Điều này không phản ánh thực tế, đặc biệt khi nút đầu tiên được chạm là số 0; nó không nên hiển thị trên màn hình Calculator trừ khi đã có các số khác 0 Do đó, cần sửa lại hàm buttonPress(sender:) để đạt được kết quả như mong muốn.

@IBOutlet weak var calDisplay: UILabel! var isTyping = false

@IBAction func buttonPress(_ sender: UIButton) {

60 let digit = sender.currentTitle! if isTyping { let currentCalDisplay = calDisplay.text! calDisplay.text = currentCalDisplay + digit

} else { if digit != "0" { calDisplay.text = digit isTyping = true

Hãy chạy chương trình, tap vào các phím số với mọi khả năng có thể có và cho kết luận!

Bước 2: Thêm các phím chức năng và kết nối mã hành vi cho chúng thông qua hàm calFunctions(sender:), sử dụng sự kiện Touch Up Inside giống như các phím số trước đó để đạt được kết quả như trong Hình 2.2.2.3 Lưu ý rằng để đưa giá trị cho các phím chức năng như ∏ và e, bạn cần truy cập bảng ký tự đặc biệt trong Xcode (Hình 2.2.2.4) Trong bước này, chúng ta sẽ tạm thời thêm ba phím chức năng: phím lấy giá trị Pi, e và phím lấy căn bậc hai.

Mỗi khi một phím chức năng được chạm thì chức năng tương ứng sẽ được thực hiện Trong trường hợp của chúng ta thì:

- Chức năng ∏ : Màn hình Calculator sẽ hiện giá trị của số Pi

- Chức năng e: Màn hình Calculator sẽ hiện giá trị của số e

Hình 2.2.2.2 Kết quả điều chỉnh chương trình

Màn hình Calculator hiển thị kết quả phép tính căn bậc hai của giá trị hiện tại Để đảm bảo ứng dụng hoạt động mượt mà, cần xử lý khéo léo giữa phím chức năng và phím số Điều này đòi hỏi việc điều chỉnh code trong hàm kết nối mới.

The `calFunctions` method handles button actions in a calculator app, where it checks the title of the pressed button If the button's title is "∏", it displays the value of pi; if it's "e", it shows Euler's number For the square root symbol "√", it calculates the square root of the current display value If the button does not correspond to a recognized function, it logs a message indicating that the function is not valid.

Tổ chức code theo mô hình MVC

2.3.1 Phân tích ứng dụng theo mô hình MVC

Trong chương 1, chúng ta đã tìm hiểu về mô hình MVC Bây giờ, chúng ta sẽ áp dụng mô hình này để phân tích, thiết kế và triển khai ứng dụng máy tính Calculator, nhằm nâng cao tính hoàn thiện của ứng dụng.

Để bổ sung thêm các tính năng cho máy tính như các hàm tính toán (±, sin, cos, ) và các phép toán cơ bản (+, -, x, ÷, ), cần thực hiện một số điều chỉnh trong thiết kế Độ phức tạp của việc điều chỉnh phụ thuộc vào cấu trúc hiện tại của phần mềm Để hạn chế tối đa việc điều chỉnh, sinh viên nên áp dụng mô hình MVC (Model-View-Controller) trong quá trình phát triển, giúp tách biệt các thành phần và dễ dàng mở rộng chức năng mà không ảnh hưởng đến toàn bộ hệ thống.

Để tối ưu hóa chức năng của máy tính, cần tách biệt các vai trò thành ba lớp riêng biệt: M (Model) cho các hoạt động tính toán, V (View) cho tương tác của người dùng, và C (Controller) để điều phối giữa hai lớp trên Mọi yêu cầu từ người dùng thông qua V sẽ được gửi đến C, sau đó C chuyển tiếp yêu cầu đến M để thực hiện tính toán và nhận kết quả Kết quả này sẽ được C chuyển lại cho V để hiển thị Do đó, việc thiết kế các thuộc tính và phương thức cần được thực hiện một cách cẩn thận để đảm bảo quá trình trao đổi thông tin diễn ra suôn sẻ và bao quát mọi tình huống có thể xảy ra.

2.3.2 Xây dụng ứng dụng theo mô hình MVC

Bước 1: Tách lớp Model cho ứng dụng Calculator

Tạo một group mới có tên "models" và trong đó, tạo một file Swift mới với lớp mang tên "CalculatorBrain" Tất cả các hoạt động nghiệp vụ của máy tính sẽ được xây dựng, cập nhật và mở rộng trong lớp này.

Nội dung lớp CalculatorBrain sẽ như sau: import Foundation class CalculatorBrain {

// To store the template calculated value private var accumulator: Double?

// To get the current Value of calculator's Screen func setOperand(_ operand: Double) { accumulator = operand

// To perform the requested Calculation func requestCalculate(mathSymbol: String) { switch mathSymbol { case "∏": accumulator = Double.pi case "e": accumulator = M_E case "√": if let operand = accumulator { accumulator = sqrt(operand)

} default: print("This is not a function")

// Return the result of the Caculation var result:Double? { get {

Bước 2: Điều chỉnh lại lớp ViewController cho phù hợp với vai trò của một Controller:

Nội dung điều chỉnh sẽ như sau (Các phần khác không có sự thay đổi): var calBrain = CalculatorBrain()

@IBAction func calFunctions(_ sender: UIButton) {

// Transform calculator's screen value to the Brain if isTyping { calBrain.setOperand(disPlayValue) isTyping = false

// Request the Brain to perform the Calculation if let mathOperation = sender.currentTitle { calBrain.requestCalculate(mathSymbol: mathOperation)

// Get the resrult and set to the Screen if let result = calBrain.result { disPlayValue = result

Bài tập: Hãy thực hiện chương trình và cho nhận xét!

2.3.3 Mở rộng và hoàn thiện ứng dụng Calculate

Trong bài viết này, chúng ta sẽ mở rộng chức năng cho máy tính Calculator bằng cách thêm các hàm một ngôi như sin, cos, ± và các hàm hai ngôi bao gồm cộng, trừ, nhân, chia Tất cả các cập nhật và điều chỉnh sẽ được thực hiện trong lớp CalculatorBrain, đảm bảo không ảnh hưởng đến các lớp khác, ngoại trừ những thay đổi liên quan đến giao diện.

Với các chức năng mới, code cũ không còn phù hợp, vì biến truyền vào hàm requestCalculate() không thể phân biệt giữa các chức năng hiển thị hằng số (như Pi, e) và các hàm tính toán một tham số (như sin, cos, ±) hay hai tham số (+, -, x, ÷) Đặc biệt, cần phân biệt khi nào truyền tham số và khi nào yêu cầu thực hiện phép toán với phím = Nếu thực hiện theo cách thông thường, cấu trúc chương trình sẽ trở nên phức tạp với nhiều điều kiện lồng nhau Tuy nhiên, với Swift, chúng ta có thể sử dụng các cấu trúc dữ liệu thay thế để làm cho chương trình trở nên rõ ràng hơn.

Để phân biệt các loại phép toán như hằng số, phép toán một ngôi, phép toán hai ngôi và dấu bằng, chúng ta sẽ sử dụng cấu trúc dữ liệu enum giống như trong Java Để xử lý từng loại thành phần trong enum, chúng ta áp dụng cấu trúc Dictionary Đặc biệt, chúng ta có thể thay thế cấu trúc switch bằng Dictionary để thực hiện các chức năng liên quan đến Pi và số e một cách dễ dàng.

// Define the data structure of math operations private var operations: Dictionary = [

// To perform the requested Calculation func requestCalculate(mathSymbol: String) { if let constant = operations[mathSymbol] { accumulator = constant

Khi chức năng là một phép toán một ngôi như sqrt, sin, cos, vấn đề chưa được giải quyết vì chúng là hàm chứ không phải giá trị Double Do đó, cần định nghĩa thêm một cấu trúc dữ liệu enum để phân loại các loại chức năng này.

// Define the type of Operation private enum Operation { case constant(Double) case unaryOperation((Double)->Double) case binaryOperation case equal

// Define the data structure of math operations private var operations: Dictionary = [

To execute the desired calculation, the function `requestCalculate(mathSymbol: String)` checks if the provided mathematical symbol corresponds to a defined operation If a match is found, it processes the operation accordingly; for constant values, it sets the accumulator to that constant, while for unary operations, it applies the specified mathematical function to the current accumulator value, if available.

The enum's constant will accept a Double value as an input, such as Pi or e, while unaryOperation will take a function variable for unary operations (Double) -> Double In the requestCalculate() function, it is essential to identify the corresponding operation type, whether it be a constant, unary operation, binary operation, or operator.

Mỗi ký tự toán học như “ ∏ ”, “ e ”, “ √ ” được truyền từ Controller có thể dễ dàng xử lý thông qua cấu trúc Dictionary Tuy nhiên, để quản lý từng trường hợp cụ thể của các phép toán, cần sử dụng cấu trúc switch.

Bài tập: Thực hiện chương trình và cho nhận xét!

Máy tính Calculator được nâng cấp với các chức năng mới như sin, cos, và ± cho phép thực hiện các phép toán một ngôi, cùng với các phép toán hai ngôi như +, -, x, ÷ Giao diện của máy tính sẽ được hiển thị như hình 2.3.3.1.

Bằng cách sử dụng phép toán một ngôi như “√” và “cos”, chúng ta có thể truy cập các hàm có sẵn trong thư viện thông qua tên hàm trong cấu trúc Dictionary Tuy nhiên, để thực hiện các phép toán khác, chúng ta cần định nghĩa các hàm tương ứng, ví dụ như hàm `changeSign` với cú pháp `import Foundation func changeSign(_ operand: Double) ->`.

Double { return -operand } func mul(_ a: Double, b: Double) -> Double

{ return a * b } func div(_ a: Double, _ b: Double) ->

Double { return a/b } func add(_ a: Double, _ b: Double) ->

Double { return a + b } func sub(_ a: Double, _ b: Double) ->

Hình 2.3.3.1 Giao diện chức năng bổ sung

Hãy điều chỉnh lớp CalculatorBrain để hỗ trợ thực hiện tất cả các phép toán một ngôi như cos và ± trên giao diện máy tính đã được cập nhật.

Tổ chức hoạt động: Tìm giải pháp thực hiện cho phép toán hai ngôi!

Khi thực hiện phép toán hai ngôi, người dùng cần nhập toán hạng thứ nhất, phép toán và sau đó là toán hạng thứ hai, rồi nhấn phím "=" để nhận kết quả Lớp CalculatorBrain chỉ lưu một biến để giữ giá trị hiện tại (biến accumulator), do đó khi nhập toán hạng thứ hai, toán hạng thứ nhất sẽ bị mất Để khắc phục tình trạng này, cần một cấu trúc dữ liệu để lưu trữ trạng thái trước đó của máy tính (bao gồm toán hạng thứ nhất và tên hàm) cùng với một phương thức để thực hiện phép toán với toán hạng thứ hai.

Với những phân tích đó, rõ ràng ở đây ta cần dùng một struct hoặc một class để thực hiện Trong trường hợp này ta dùng struct:

//Struct to store information of binaryOperations private struct PendingBinaryOperation {

// Store the function let theBinaryFunction: (Double, Double) -> Double

// Store the first operand let theFisrtOperand: Double

// Function to perform the function with the second operand func performCalculate(secondOperand: Double) -> Double { return theBinaryFunction(theFisrtOperand, secondOperand)

Và hàm requestCalculate được thay đổi như sau:

Autolayout trong iOS

2.4.1 Vấn đề giao diện trong ứng dụng Calculate

Khi xoay màn hình máy ảo, giao diện ứng dụng có thể không hiển thị như mong đợi, thậm chí đôi khi bị vỡ hình ảnh, đặc biệt nếu ứng dụng sử dụng các loại hình ảnh với kích thước khác nhau.

Để giải quyết vấn đề thay đổi hướng nhìn của các giao diện iOS, cần cải tiến giao diện cho ứng dụng Calculate bằng cách áp dụng phương pháp Autolayout Phương pháp này cho phép các giao diện tự động điều chỉnh hình dáng để phù hợp với nhiều kiểu màn hình khác nhau.

Bước 1: Đưa các đối tượng Button vào trong các StackView, theo từng hàng

Lựa chọn các buttons trên từng hàng => Editor => Embed In => Stack View

Hình 2.4.2.1 Nhúng các đối tượng trong Stack View

Trong Attributes Inspector thay đổi thuộc tính: Spacing

To create an organized layout, set the distribution to "Fill Equally" for all rows Next, select all rows and embed them within a new Stack View In the Attributes Inspector, adjust the spacing to 10 and ensure the distribution settings are properly configured.

To achieve a uniform layout, place all buttons within a separate Stack View on the computer screen In the Attributes Inspector, adjust the settings by setting Spacing to 10, Distribution to Fill, and Alignment to Fill The final result will resemble the layout shown in Figure 2.4.2.2.

Bước 2: Thêm ràng buộc để các đối tượng tự điều chỉnh kích thước khi thay đổi độ phân giải hoặc hướng màn hình Lưu ý rằng các đối tượng hiện tại được xem như một StackView.

Hình 2.4.2.2 Kết quả sau khi nhúng các đối tượng trong Stack View

Nhiệm vụ của chúng ta là thiết lập các ràng buộc cho StackView với bốn cạnh của màn hình điện thoại Để thực hiện điều này, hãy chọn Stack View cuối cùng và nhấn vào biểu tượng phía dưới màn hình, sau đó màn hình thêm ràng buộc cho layout sẽ hiện ra (Hình 2.4.2.3).

Hình 2.4.2.3 Thêm dàng buộc cho Autolayout

Sửa các giá trị cho lề trái, phải, trên dưới đều là 20 rồi nhấn “Add 4 Contraints” để hoàn thành cho Autolayout

Khi đó giao diện sẽ tự động co dãn sao cho lề trái, lề phải, lề trên và lề dưới của nó đều có giá trị là 20

Lưu ý: Để xem nhanh cấu trúc cây của các View trong màn hình, nhấn giữ Shift + Phải chuột trên đối tượng view muốn xem

Chạy chương trình và xoay màn hình ta có các kết quả như hình 2.4.2.4

Bài tập: Hãy điều chỉnh giao diện sao cho chiều cao của các button và chiều cao của màn hình máy tính là giống nhau!

Hình 2.4.2.4 Autolayout với các chiều khác nhau của màn hình iPhone

Case Study: Thiết kế ứng dụng Quản lý món ăn

Quản lý món ăn là một ứng dụng phức tạp cho người mới học lập trình iOS, nhưng giáo trình sẽ giúp sinh viên nắm vững các kiến thức và kỹ năng cần thiết để phát triển ứng dụng iOS cơ bản Qua việc phân tích thiết kế, sinh viên sẽ học cách thiết kế giao diện màn hình, thêm các control phức tạp, tạo nhiều màn hình khác nhau, xử lý sự kiện, ủy quyền và bổ sung chức năng cho các đối tượng ủy quyền Bên cạnh đó, giáo trình cũng đề cập đến thiết kế cơ sở dữ liệu và cách tích hợp bản đồ trực tuyến vào ứng dụng, giúp sinh viên dễ dàng tiếp thu và thực hành các kiến thức đã học.

2.5.1 Luyện tập thiết kế giao diện cơ bản và autolayout trong iOS

First, create a new project named FoodManagement2020 (including Test) with a single screen Focus on designing the new screen interface with specific constraints: include a Text Field with the placeholder text "Enter meal name!" centered, set the Return key to "Done," and select the "Auto-enable Return key" option to ensure the Done key is only activated when the Text Field contains data Additionally, set a left margin of 47.

Để tạo giao diện như hình 2.5.1.1, bạn cần thiết lập các ràng buộc cho Image View với kích thước 320x320 và áp dụng tỷ lệ khung hình để giữ nguyên tỷ lệ ảnh Đối với Intrinsic Size, chọn Placeholder với chiều dài và chiều rộng là 320 để ảnh có thể co dãn theo kích thước thực khi chạy Cuối cùng, thiết lập Button với tiêu đề "Go to Map" và ràng buộc lề trái và lề phải là 100.

Lưu ý: Khi đưa các đối tượng vào Stack View trong chế độ Autolayout để Spacing của Stack View là 8, Allignment là Center, Distribution là Fill

Sau khi các đối tượng được đưa vào Stack View, bạn cần thiết lập các ràng buộc cho chúng, bao gồm lề trái, lề phải, tỷ lệ khung (Aspect Ratio) và chọn Placeholder cho kích thước nội tại (Intrinsic Size) của hình ảnh.

Khi chạy thử chương trình, Image view sẽ không xuất hiện do chưa có ảnh Để thử đưa ảnh Default

Hình 2.5.1.1 Giao diện màn hình chi tiết của một món ăn

Hướng dẫn đưa ảnh Default vào ứng dụng iOS:

Chọn Assets.xcassets trong Navigation Area => Add a group or image set => Đổi tên images set là default => Kéo thả ảnh tương ứng vào ô 3x (Hình 2.5.1.2)

Hình 2.5.1.2 Đưa bộ ảnh default vào ứng dụng iOS

Trong iOS, có ba dạng màn hình với ba chế độ phân giải khác nhau Để hỗ trợ cả ba độ phân giải, bạn cần cung cấp đầy đủ bộ ba ảnh cho các ô 1x, 2x và 3x Trong ví dụ này, chúng ta đang sử dụng máy ảo với độ phân giải 3x Để thêm bộ ảnh mới vào image view, hãy chọn image view trong storyboard, truy cập vào Attributes Inspector và trong mục image, chọn bộ ảnh mặc định.

Bài tập: Chạy lại chương trình và cho nhận xét! Hãy thực hiện bài tập số 5 của chương

2 sao cho đối tượng text field và đối tượng image view đều có cùng lề trái và phải là 8

2.5.2 Xử lý sự kiện với các Component cơ bản

Trước khi viết code xử lý sự kiện cho các đối tượng trong ứng dụng iOS, cần tìm hiểu về vòng đời của View controller Tương tự như Activity trong Android, Controller trong iOS có vòng đời riêng và tự động gọi một số hàm callbacks tại những thời điểm nhất định khi trạng thái của Controller thay đổi.

Hình 2.5.2.1 Các trạng thái và cách chuyển trạng thái của một Controller

Vòng đời của một Controller trong iOS:

Phương thức viewDidLoad() được gọi khi giao diện của Controller được khởi tạo từ storyboard, và thường được sử dụng để thiết lập các cấu hình bổ sung cho giao diện Lưu ý rằng phương thức này chỉ được gọi một lần duy nhất trong quá trình khởi tạo Controller.

Phương thức viewWillAppear() được gọi ngay trước khi giao diện của Controller được thêm vào cấu trúc hiển thị của ứng dụng Điều này cho phép phương thức này thực hiện các công việc cần thiết trước khi giao diện của Controller xuất hiện trên màn hình iPhone.

Phương thức viewDidAppear() được kích hoạt ngay sau khi giao diện của Controller xuất hiện trong cấu trúc hiển thị của ứng dụng Đây là thời điểm lý tưởng để thực hiện các tác vụ cần thiết, chẳng hạn như lấy dữ liệu hoặc khởi động các hoạt cảnh, ngay khi giao diện Controller hiển thị trên màn hình iPhone.

- Phương thức viewWillDisappear(): Được gọi ngay trước khi giao diện của

Controller sẽ bị loại bỏ khỏi cấu trúc hiển thị của ứng dụng, vì vậy phương thức này thường được sử dụng để thực hiện các công việc dọn dẹp trước khi giao diện của Controller không còn hiển thị trên màn hình iPhone.

Phương thức viewDidDisappear() được gọi ngay sau khi giao diện của Controller bị xóa khỏi cấu trúc hiển thị của ứng dụng Do đó, phương thức này thường được sử dụng để thực hiện các công việc còn lại sau khi giao diện của Controller không còn hiển thị trên màn hình iPhone.

Đối tượng Text Field cho phép người dùng nhập tên món ăn, và khi người dùng chạm vào nó, bàn phím phần mềm sẽ xuất hiện trên màn hình iPhone Bàn phím này được cấu hình với phím "Done", chỉ hoạt động khi có dữ liệu trong Text Field Chức năng của phím "Done" phụ thuộc vào nhu cầu cụ thể của người sử dụng, do đó Swift không thể xác định trước cách xử lý mà sẽ sử dụng cơ chế ủy quyền như đã đề cập trong chương 1.

Hãy tạo một biến liên kết code dạng tham chiếu có tên txtFoodName với đối tượng

Text field này! Để có thể thực hiện cơ chế uỷ quyền của đối tượng TextField với lớp ViewController, hãy thực hiện các bước sau:

Bước 1: Cho lớp ViewController thực hiện Protocol UITextFieldDelegate class ViewController: UIViewController, UITextFieldDelegate {…

Bước 2: Thực hiện việc uỷ quyền trong hàm viewDidLoad(): override func viewDidLoad() { super.viewDidLoad()

// Delegation of the TextField txtFoodName.delegate = self

Bước 3: Tiến hành viết các hàm đã được ủy quyền xử lý cho Textfield, trong đó có 8 phương thức được ủy quyền Đối với ứng dụng này, chúng ta sẽ định nghĩa hai phương thức quan trọng: textFieldShouldReturn(_ textField: UITextField) -> Bool và textFieldDidEndEditing(_ textField: UITextField).

Khi người dùng tương tác với đối tượng TextField, nó trở thành “The first responder”, tức là đối tượng ưu tiên nhận các sự kiện như sự kiện bàn phím, sự kiện chuyển động và thông điệp hành động Lúc này, iOS sẽ hiển thị bàn phím để người dùng có thể bắt đầu soạn thảo.

Khi người dùng muốn kết thúc việc soạn thảo trên iPhone bằng cách nhấn phím "Return" hoặc chọn "Done" trên bàn phím, phương thức textFieldShouldReturn sẽ được kích hoạt, giúp Textfield thoát khỏi trạng thái ưu tiên.

//MARK: Textfield Delegate Functions func textFieldShouldReturn(_ textField: UITextField) -> Bool {

// Hide the keyboard txtFoodName.resignFirstResponder() return true // Luôn xử lý mỗi khi người dùng tap “Done”

Câu hỏi và bài tập chương 2

1 Tại sao khi phát triển các ứng dụng trên di động cần chú trọng nhiều đến việc thiết kế giao diện chương trình (có thể tốn đến gần 50% công sức)?

2 Thực hiện mọi hoạt động và bài tập được yêu cầu trong toàn bộ chương 2!

3 Tìm hiểu về Closure trong Swift và cải tiến chương trình Calcultor!

4 Sửa lại ứng dụng Calculator sao cho sau khi thực hiện một phép toán, nếu nhấn vào phím 0 thì màn hình trở lại như ban đầu (Xoá kết quả, trở về màn hình 0) nhưng các trường hợp còn lại vẫn phải hoạt động như bình thường

5 Để image view trong màn hình chi tiết của món ăn luôn hiển thị ảnh với cùng kích thước (không thay đổi theo kích thước của ảnh) thì phải làm gì? Hãy sửa và chạy thử! Sửa thuộc tính Content Mode của ImageView thành Scale to Fill Chạy lại chương trình và cho nhận xét

6 Thực hiện chương trình trong hoàn thiện trong 2.5.2, điều chỉnh xoá bỏ lựa chọn User Interaction Enable và cho biết kết quả? Giải thích!

7 Sau khi thực hiện chức năng trong 2.5.5, hãy chọn StackView của RatingCotrol, vào bảng thuộc tính điều chỉnh Distribution = Fill Equally Quan sát sự thay đổi trên màn hình giao diện storyboard và cho nhận xét!

8 Tìm hiểu trong ứng dụng iOS, mỗi ảnh đưa vào ứng dụng có bao nhiêu trạng thái? Đó là những trạng thái nào ngoài 3 trạng thái đang sử dụng?

9 Trong mục 2.5.5 hướng dẫn cách đưa các thuộc tính vào bảng Attributes Inspector Câu hỏi đặt ra là với những loại biến nào có thể đưa được vào đây? Với các kiểu dữ liệu tự tạo có thể đưa vào trong này không? Tại sao?

10 Nếu bước 5 trong mục 2.5.6 không có hai đoạn lệnh kiểm tra ràng buộc các thuộc tính thì vấn đề già sẽ xảy ra? Giải thích!

11 Tìm hiểu về Table View nhiều Section trong iOS Cải tiến màn hình MealTableViewController sao cho có thể hiển thị nhiều Sections khác nhau!

12 Trong bước 2, mục 2.5.7 khi thực hiện chức năng Edit và xoá một phần tử trong Table View thì giao diện của phần tử bị đẩy một phần ra ngoài màn hình Hãy dựa trên những điều đã học về Autolayout, hiệu chỉnh giao diện cho Prototype Cell để không còn tình trạng đó xảy ra nữa

13 Tại sao bước 5, mục 2.5.7 biến meal lại là biến Optional? Giair thích!

14 Hãy tìm hiểu và giải thích tại sao tại bước 4, mục 2.5.7 trong hàm prepare lại sử dụng câu lệnh: let name = txtFoodName.text ?? "" ? Ý nghĩa của nó là gì?

15 * Hãy viết ứng dụng Quản lý nhân sự!

16 * Hãy viết ứng dụng trắc nghiệm khách quan với 4 dạng câu hỏi: Multi quesion multi-choices, multi-question one-choice, matching question và true-false question!

17 Phân biệt các dạng chuyển màn hình khác nhau trong iOS!

18 Viết ứng dụng Calculator (giống Android) sao cho việc chuyển màn hình sử dụng dạng Show segue! Hãy tìm hiểu và thực hiện truyền tham số giữa các màn hình để hoàn thiện ứng dụng!

LÀM VIỆC VỚI CƠ SỞ DỮ LIỆU

Thiết kế Data model cho ứng dụng

3.1.1 Phân tích ứng dụng Quản lý món ăn

Mỗi món ăn trong hệ thống bao gồm ba trường: tên món ăn (kiểu chuỗi String), ảnh món ăn (kiểu UIImage) và giá trị đánh giá (kiểu Int, từ 0 đến 5) Trường tên món ăn không được để trống, trường ảnh có thể có hoặc không có dữ liệu (biến Optional), và trường đánh giá bắt buộc phải nằm trong khoảng giá trị cho phép.

3.1.2 Thiết kế Datamodel cho ứng dụng

Dựa trên các phân tích trước, chúng ta sẽ xây dựng cấu trúc dữ liệu cho các món ăn bằng cách sử dụng đoạn mã sau: import UIKit class Meal {

//MARK: Properties var mealName: String var mealImage: UIImage? var ratingValue: Int

//MARK: Initialization init?(name: String, image: UIImage?, rating: Int) {

// Check the conditions guard !name.isEmpty else { return nil

} guard (rating >= 0) && (rating Bool { var ok: Bool = false if db != nil { let sql = "CREATE TABLE " + TABLE_NAME + "( "

+ TABLE_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "

+ MEAL_RATING + " INTEGER)" if db!.executeStatements(sql) { ok = true os_log("Table is created!")

} else { os_log("Can not create the table!")

} else { os_log("Database is nil!")

// Open database func open() -> Bool { var ok: Bool = false if db != nil { if db!.open() { ok = true os_log("The database is opened!")

} else { print("Can not open the Database:

} else { os_log("Database is nil!")

// Close database func close(){ if db != nil { db!.close()

3.5.2 Thiết kế các API cho tầng trên

API cơ sở dữ liệu là các hàm cần thiết để các ứng dụng tương tác với cơ sở dữ liệu mà không cần biết chi tiết về loại cơ sở dữ liệu đang sử dụng Trong ứng dụng Quản lý món ăn, chúng ta cần các API cho phép đọc dữ liệu từ cơ sở dữ liệu để hiển thị danh sách món ăn, ghi từng món ăn hoặc danh sách món ăn vào cơ sở dữ liệu, cập nhật thông tin món ăn, và xóa món ăn không cần thiết Để nâng cao khả năng quản lý, cần có API hỗ trợ tìm kiếm sự tồn tại của một món ăn trong cơ sở dữ liệu nhằm tránh việc ghi trùng lặp.

Để sử dụng ứng dụng Quản lý món ăn, cần kiểm tra xem cơ sở dữ liệu đã lưu danh sách món ăn hay chưa Nếu có, toàn bộ dữ liệu sẽ được đọc và đưa vào mảng dữ liệu món ăn, làm nguồn dữ liệu cho Table View Hàm sẽ được thiết kế với từ khoá inout, cụ thể là func readMealList(meals: inout [Meal]){, và nếu cơ sở dữ liệu không nil, sẽ thực hiện truy vấn SQL để lấy toàn bộ dữ liệu từ bảng.

// Query do { results = try db!.executeQuery(sql, values: nil)

} catch { print("Fail to read data: \(error.localizedDescription)")

To read data from the results, check if results are not nil If results are available, iterate through each entry For each entry, retrieve the meal name, meal image, and meal rating using their respective column names.

// Transform string image to UIImage let dataImage: Data = Data(base64Encoded: stringImage!, options: ignoreUnknownCharacters)! let mealImage = UIImage(data: dataImage)

// Create a meal to contain the values let meal = Meal(name: mealName!, image: mealImage!, rating: Int(mealRating)) meals.append(meal!)

} else{ os_log("Database is nil!")

Để ghi dữ liệu vào cơ sở dữ liệu trong ứng dụng Quản lý món ăn, cần xây dựng một hàm có khả năng lưu trữ thông tin về món ăn mỗi khi được gọi Hàm này sẽ nhận một tham số kiểu món ăn và có cấu trúc như sau: `func insertMeal(meal: Meal)` Đảm bảo rằng cơ sở dữ liệu đã được khởi tạo trước khi thực hiện việc ghi dữ liệu.

// Transform the meal image to String let imageData: NSData = meal.mealImage!.pngData()! as NSData let mealImgageString = imageData.base64EncodedData(options:

To insert a meal into the database, use the SQL command to add the meal's name, image, and rating If the insertion is successful, a log message confirms that the meal has been added to the database.

} else { os_log("Fail to insert the meal!")

} else { os_log("Database is nil!")

} c Cập nhật dữ liệu vào cơ sở dữ liệu

Khi điều chỉnh một món ăn, cần cập nhật thông tin vào cơ sở dữ liệu Hàm cập nhật yêu cầu món ăn mới để ghi vào cơ sở dữ liệu và món ăn cũ để xác định vị trí cần cập nhật Cụ thể, hàm sẽ kiểm tra xem cơ sở dữ liệu có tồn tại hay không, sau đó thực hiện câu lệnh SQL để cập nhật tên món ăn.

To update the meal's image and rating in the database, use the SQL query: "SET MEAL_IMAGE = ?, MEAL_RATING = ? WHERE MEAL_NAME = ? AND MEAL_RATING = ?" First, convert the new meal's image to a string format by using the following code: `let newImageData: NSData = newMeal.mealImage!.pngData()! as NSData` and then encode it with `let newStringImage = newImageData.base64EncodedString(options: )`.

lineLength64Characters) // Try to query the database do{ try db!.executeUpdate(sql, values: [newMeal.mealName, newStringImage, newMeal.ratingValue, oldMeal.mealName, oldMeal.ratingValue]) os_log("Successful to update the meal!")

} catch{ print("Fail to update the meal:

} else { os_log("Database is nil!")

} d Xoá dữ liệu từ cơ sở dữ liệu

When deleting a meal from the menu, it is essential to also remove it from the database The function requires a single parameter, the meal to be deleted: `func deleteMeal(meal: Meal)` If the database is accessible, the SQL command is constructed to delete the specified meal based on its name and rating: `let sql = "DELETE FROM \(TABLE_NAME) WHERE \(MEAL_NAME) = ? AND \(MEAL_RATING) = ?"` The operation is executed with `try db!.executeUpdate(sql, values: [meal.mealName, meal.ratingValue])`, and upon successful deletion, a log message confirms: "The meal is deleted!"

} catch { os_log("Fail to delete the meal!")

} else { os_log("Database is nil!")

} e Tìm kiếm dữ liệu từ cơ sở dữ liệu

Bài tập yêu cầu xây dựng API để tìm kiếm món ăn trong cơ sở dữ liệu bằng cách sử dụng hàm đọc, cập nhật và xóa dữ liệu Hàm tìm kiếm sẽ nhận vào một biến đại diện cho món ăn cần tìm Nếu món ăn có tên và rating trùng khớp với cơ sở dữ liệu, hàm sẽ trả về kết quả true; ngược lại, kết quả sẽ là false.

Sử dụng tầng DAL cho ứng dụng iOS

Sau khi hoàn thành thiết kế tầng DAL, chúng ta có thể sử dụng nó ở tầng trên để lưu trữ dữ liệu cho ứng dụng Quản lý món ăn Tất cả các thao tác liên quan đến danh sách món ăn đều được thực hiện tại màn hình MealListController, do đó, việc sử dụng các Database APIs cũng diễn ra tại lớp này Trong file MealListController, cần khai báo hai biến: private var dao = FoodManagementDatabase() và static private var tableCreated: Bool = false.

Biến dao (Database Access Object) là một đối tượng trong cơ sở dữ liệu, cho phép sử dụng các API của cơ sở dữ liệu để thực hiện các thao tác như đọc, ghi, cập nhật, tìm kiếm và xóa món ăn trong cơ sở dữ liệu SQLite của ứng dụng Biến tableCreated được sử dụng để đảm bảo rằng bảng dữ liệu không bị tạo lại nếu đã tồn tại.

Trong hàm viewDidLoad, cần điều chỉnh mã nguồn để thêm dữ liệu giả cho Table view Nếu dữ liệu chưa có, hãy khởi tạo bảng dữ liệu và đọc dữ liệu từ cơ sở dữ liệu vào datasource của table view Nếu không có dữ liệu, hãy tạo dữ liệu món ăn giả và ghi vào cơ sở dữ liệu.

//Load from database if dao.open() { if !MealTableViewController.tableCreated {

// Create an example of meal let image = UIImage(named: "default") if let meal = Meal(name: "Mon Hue", image: image, rating: 3) { meals += [meal] if dao.open() { dao.insertMeal(meal: meal)

Di chuyển đến hàm thực hiện xoá món ăn trong menu Edit và điều chỉnh thành:

// Delete the meal from database if dao.open() { dao.deleteMeal(meal: meals[indexPath.row])

// Delete the row from the data source meals.remove(at: indexPath.row) tableView.deleteRows(at: [indexPath], with: fade)

Mỗi khi một món ăn bị xoá khỏi table view thì nó cũng được xoá khỏi cơ sở dữ liệu

Bài tập: Tại sao lại cần thực hiện xoá trong cơ sở dữ liệu trước khi xoá trong datasource và trong table view?

Di chuyển đến hàm unWindToMealList và điều chỉnh thành: if sourceViewController.navigationType == newMeal {

// The new indexPath of the new meal in the table let newIndexPath = IndexPath(row: meals.count, section: 0)

// Put the new meal into the datasource of table view meals += [newMeal]

// Update the new meal in the table view tableView.insertRows(at: [newIndexPath], with: automatic)

// Insert into database if dao.open() { dao.insertMeal(meal: newMeal)

// For edit meal segue else if sourceViewController.navigationType == editMeal{ if let selectedIndexPath = tableView.indexPathForSelectedRow {

// Update in the database if dao.open() { dao.updateMeal(oldMeal: meals[selectedIndexPath.row], newMeal: newMeal)

// Update in datasource array meals[selectedIndexPath.row] = newMeal

// Update in table view tableView.reloadRows(at: [selectedIndexPath], with: none)

Trong phần chỉnh sửa bữa ăn, việc ghi thông tin vào cơ sở dữ liệu trước là cần thiết để đảm bảo tính chính xác và tránh xung đột dữ liệu Khi thêm một món ăn mới, việc ghi thông tin vào cơ sở dữ liệu trước có thể gây ra vấn đề về trùng lặp Để cải tiến, nên tích hợp API tìm kiếm món ăn nhằm kiểm tra trước khi ghi vào cơ sở dữ liệu, giúp ngăn ngừa việc ghi trùng lặp Do đó, đoạn chương trình ghi vào cơ sở dữ liệu nên được đặt sau khi xác nhận thông tin món ăn không bị trùng lặp.

Câu hỏi và bài tập chương 3

1 Thiết kế lại cấu trúc của bảng dữ liệu nếu ta muốn lưu các món ăn dưới dạng tên, đường dẫn đến file ảnh, giá trị đánh giá! Tìm hiểu và viết lại chương trình!

2 Thực hiện mọi yêu cầu bài tập trong toàn bộ chương 3!

3 Khi nào dùng SQLite hiệu quả hơn? Khi nào dùng Core Data? Khi nào dùng Firebase?

4 Sửa lại hàm init sao cho trước khi khởi tạo cơ sở dữ liệu cần kiểm tra sự tồn tại của file chứa database trước?

5 Viết Database API mới dùng để tìm kiếm một món ăn trong cơ sở dữ liệu theo tên món ăn và trả về một danh sách các món ăn trùng tên hoặc nil?

6 Viết Database API mới dùng để tìm kiếm những món ăn có rating lớn hơn (nhỏ hơn) món ăn nào đó, hàm trả về danh sách các món ăn thoả điều kiện hoặc nil?

BẢN ĐỒ TRỰC TUYẾN TRÊN IOS

Một số dạng bản đồ trực tuyến thông dụng

Trên iOS, người dùng có nhiều lựa chọn hơn cho việc sử dụng bản đồ trực tuyến và xác định vị trí so với Android Ngoài các dịch vụ mạnh mẽ từ Google, Apple cũng phát triển các dịch vụ riêng với tính năng tương đương, nhưng dễ sử dụng hơn thông qua Map Kit.

MapKit là dịch vụ bản đồ và định vị do Apple phát triển, cung cấp các tính năng như hiển thị bản đồ trực tuyến, tìm kiếm vị trí và chỉ đường cho ứng dụng iOS Khác với Google Maps, MapKit tập trung vào việc xây dựng các tuyến đường Mặc dù trước đây MapKit còn nhiều hạn chế, hiện nay nó đã hoàn toàn có thể thay thế Google Maps và được sử dụng rộng rãi nhờ giao diện đẹp và dễ sử dụng, tích hợp sẵn trong các thư viện của Xcode Hơn nữa, MapKit hỗ trợ Siri hiệu quả hơn so với OK Google.

Dịch vụ bản đồ trực tuyến của Google cung cấp đầy đủ tính năng cho ứng dụng trên Android và iOS, với khả năng truy cập dựa trên vị trí và địa điểm Tuy nhiên, việc tích hợp dịch vụ này vào dự án iOS có thể gây khó khăn cho lập trình viên.

Sử dụng bản đồ trực tuyến trong các ứng dụng iOS

Trong bài viết này, chúng tôi sẽ hướng dẫn người dùng cách sử dụng các dịch vụ Bản đồ và Vị trí của Map Kit để phát triển ứng dụng trên iOS.

4.2.1 Tích hợp MapKit vào ứng dụng Để tích hợp MapKit vào ứng dụng iOS, chỉ cần thực hiện một câu lệnh đơn giản để import thư viện MapKit vào trong ứng dụng (Với Google map thì phức tạp hơn nhiều) Nếu muốn sử dụng các dịch vụ về Location thì cũng cần import thư viện tương ứng: import MapKit import CoreLocation

4.2.2 Tạo mới bản đồ trong ứng dụng iOS Để sử dụng bản đồ trực tuyến trên iOS, chúng ta chỉ cần thực hiện hai thao tác cơ bản đó là: Khai báo bản đồ (bằng lệnh var mapView: MKMapView!) và khởi tạo cho bản đồ trực tuyến đó (init và định nghĩa lại hàm viewDidLayoutSubview)

Bước 1: Tạo màn hình MapViewController mới

Khi người dùng nhấn nút "Go to Map" trong ứng dụng, bản đồ trực tuyến sẽ được hiển thị, cho phép thực hiện các chức năng cơ bản như đánh dấu và tìm đường Để thực hiện điều này, cần tạo một màn hình mới có tên MapViewController bằng cách kéo thả một View Controller từ thư viện đối tượng vào Storyboard, sắp xếp màn hình này nằm bên phải màn hình MealDetailController Tiếp theo, sử dụng Ctrl + Drag từ nút "Go to Map" trong MealDetailController sang màn hình MapViewController mới tạo và chọn Show để hoàn tất.

Để tiếp tục, bạn cần tạo lớp MapViewController.swift, kế thừa từ UIViewController và liên kết với màn hình mới Mở file MapViewController.swift để khai báo bản đồ trực tuyến và thực hiện khởi tạo cho nó.

Hình 4.2.2.1 Tạo màn hình MapViewController

Bước 2: Tích hợp MapKit, CoreLocation và khởi tạo cho bản đồ trực tuyến

Lớp MapViewController ban đầu có dạng như sau: import UIKit import MapKit import CoreLocation class MapViewController: UIViewController {

//MARK: Properties var mapView: MKMapView! override func viewDidLoad() { super.viewDidLoad()

//MARK: Initialization for Map mapView = MKMapView.init() view.addSubview(mapView)

//MARK: For Map override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() mapView.frame = CGRect.init(origin: CGPoint.zero, size: view.frame.size) }

Khi khởi tạo bản đồ trực tuyến, nếu không chỉ định kích thước (chiều dài, chiều rộng) trong hàm viewDidLayoutSubviews, bản đồ sẽ không hiển thị khi chạy chương trình Để khắc phục điều này, chúng ta cần đặt kích thước cho bản đồ từ tọa độ (0, 0) ở góc trên bên trái màn hình đến tọa độ tối đa ở góc dưới bên phải màn hình.

Bài tập: Chạy thử chương trình di chuyển đến màn hình MapViewController và cho nhận xét!

4.2.3 Đánh dấu trên bản đồ dùng Annotation

Trên bản đồ trực tuyến, việc đánh dấu các điểm (Marker) cho nhiều mục đích khác nhau là rất cần thiết Mỗi điểm đánh dấu cần có thông tin như tọa độ (kinh độ, vĩ độ), tên và hình ảnh Để thực hiện việc này, trước tiên, chúng ta cần định nghĩa lớp Map annotation có tên MyMapAnnotation, với cú pháp như sau: import UIKit import MapKit class MapAnnotation: NSObject, MKAnnotation {

//MARK: MKAnnotation protocol public var coordinate: CLLocationCoordinate2D public var title: String? public var subtitle: String?

//MARK: Initializer init(coordinate: CLLocationCoordinate2D, title: String?, subtitle: String?) { self.coordinate = coordinate self.title = title self.subtitle = subtitle super.init()

Trong hàm viewDidLoad hãy thực hiện những công việc sau để đánh dấu toạ độ của trường Cao đẳng Công nghệ Thủ Đức trên bản đồ:

// Add a marker on the Map let tdc = CLLocation(latitude:

106.75824763345204) let tdcMarker = MyMapAnnotation(coordinate: tdc.coordinate, title: "TDC", subtitle: "Cao dang Cong nghe Thu Duc") mapView.addAnnotation(tdcMarker)

Chạy chương trình với kết quả bài tập số 1 trong chương này, ta sẽ nhận được kết quả như hình 4.2.3.1, trong đó biến tdc lưu trữ tọa độ (kinh độ, vĩ độ) của Trường Cao đẳng Công nghệ Thủ Đức, với phạm vi nhìn là 500 m.

Hình 4.2.3.1 Đánh dấu trên bản đồ

4.2.4 Lấy vị trí hiện tại của người dùng trên bản đồ Để có thể lấy được vị trí hiện tại của người dùng trên bản đồ, cần thực thi các hàm cần thiết trong CLLocationManagerDelegate đồng thời cần cho phép truy xuất vào vị trí hiện tại của người dùng Để yêu cầu cấp quyền truy xuất vị trí hiện tại, mở file info.plist, rồi thêm vào Key: “Privacy - Location When In Use Usage Description” với thông báo

“Allow to access user's Location!” (Hình 4.2.4.1)

Hình 4.2.4.1 Cấp quyền cho việc truy xuất vị trí hiện tại trên bản đồ

Để quản lý vị trí hiện tại trên bản đồ, cần khai báo hai biến: một biến để chứa tọa độ vị trí hiện tại là `private var currentLocation: CLLocation?` và một biến quản lý việc truy xuất vị trí là `private var locationManager: CLLocationManager!`.

Tiếp đến viết hàm lấy toạ độ hiện tại:

// Get curent Location func getCurrentLocation(){ locationManager = CLLocationManager() locationManager.delegate = self

// Define how to update location (immediate) locationManager.desiredAccuracy = kCLLocationAccuracyBest

// Check for Location Services if CLLocationManager.locationServicesEnabled() { locationManager.requestWhenInUseAuthorization() locationManager.startUpdatingLocation()

Hàm này sẽ được gọi trong viewDidLoad, sau đó, cần viết hàm ủy quyền để thực hiện việc cập nhật vị trí hiện tại liên tục, sử dụng hàm centerMapOnLocation như là kết quả của bài tập 1.

//MARK: For Location func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { defer { currentLocation = locations.last locationManager.stopUpdatingLocation()

// Zoom to user location if let userLocation = locations.last { centerMapOnLocation(location: userLocation, radius: 500)

Để thiết lập máy ảo, bạn cần chọn các tính năng và vị trí Tiếp theo, chọn "Custom Location" và nhập tọa độ hiện tại, vì máy ảo không có cảm biến Khi chạy chương trình, bạn sẽ nhận được yêu cầu cho phép truy cập vị trí hiện tại trên bản đồ Hãy chọn "Allow While Using App" để tiếp tục.

Bài tập: Chạy chương trình trên máy ảo, chạy chương trình trên máy thật, di chuyển và cho nhận xét!

4.2.5 Tương tác với các Annotation trên bản đồ

Với các Annotation được đánh dấu trên bản đồ, người dùng có thể tùy chỉnh và tương tác một cách dễ dàng Để thực hiện điều này, trước tiên cần ủy quyền các hàm trong MKMapViewDelegate.

Hình 4.2.4.2 Yêu cầu cấp quyền truy xuất

Bài tập: Tìm hiểu và thực hiện tương tác với các Annotation!

4.2.6 Xác định vị trí và di chuyển trên bản đồ

Để cải thiện chương trình ở mục 4.2.4, chúng ta cần cập nhật vị trí hiện tại của người dùng trên bản đồ mỗi khi họ di chuyển Việc này sẽ cho phép hiển thị hướng di chuyển của người dùng một cách chính xác, giúp người dùng dễ dàng theo dõi vị trí của mình trong không gian.

Gợi ý: Sử dụng lệnh hiển thị vị trí hiện tại của người dùng trên bản đồ:

// Show the current location as a blue point mapView.showsUserLocation = true

4.2.7 Đánh dấu vị trí bằng sự kiện LongPressGesture

Một trong những thao tác quan trọng trên ứng dụng di động là sự kiện long press, tức là nhấn và giữ tay trên màn hình điện thoại trong một khoảng thời gian.

// For long press gesture func addLongPressGesture(){ let longPressRecognizer:UILongPressGestureRecognizer =

UILongPressGestureRecognizer(target: self, action:#selector(handleLongPress(_:))) longPressRecognizer.minimumPressDuration = 1.0 mapView.addGestureRecognizer(longPressRecognizer)

Câu hỏi và bài tập chương 4

1 Trong mục 4.2.2 sau khi khởi tạo và hiển thị bản đồ trên màn hình điện thoại, hãy viết hàm thực hiện công việc sau: Truyền vào hàm hai tham số toạ độ của điểm cần đến (Có kiểu là CLLocation ) và phạm vi khu vực cần hiển thị (Có kiểu là CLLocationDistance), khi gọi hàm bản đồ sẽ di chuyển đến đúng toạ độ được truyền bởi tham số thứ nhất và bao phủ một vùng xung quanh toạ độ đó với khoảng cách bao phủ bằng tham số thứ 2

2 Cải tiến chương trình trong mục 4.2.3 để đánh dấu các danh lam thắng cảnh nổi tiếng của Việt Nam và trên thế giới!

3 Thực hiện bài tập trong 4.2.5 sao cho mỗi khi chạy ứng dụng, tap vào nút “Go to Map” sẽ chuyển sang màn hình MapViewController, đánh dấu tại một điểm xác định (vị trí của quán có bán món ăn đó) Mỗi khi tap lên điểm Marker đó sẽ hiển thị ảnh của món ăn tương ứng, nếu tap lên nút (!) sẽ hiển thị thông tin về quán (Hình 4.3.1)

Hình 4.3.1 Cải tiến chức năng 4.2.5

4 Cải tiến chương trình trong mục 4.2.7 sao cho tại vị trí hiện tại người dùng đang đứng trên bản đồ sẽ đánh dấu bằng một Annotation (khác mầu sắc, hình ảnh…) và mỗi khi người sử dụng LongPress tại một vị trí nào khác trên bản đồ thì sẽ được đánh dấu bằng một Annotation khác

Chương trình cần được điều chỉnh để khi chuyển từ màn hình MealDetail sang màn hình MapView, thông tin về món ăn sẽ được truyền đi Nếu món ăn đã có tọa độ của quán bán, một Annotation sẽ hiển thị tại vị trí của quán trên bản đồ Khi người dùng tương tác với Annotation này, họ sẽ nhận được thông tin chi tiết về quán ăn Đối với món ăn mới chưa có tọa độ, người dùng có thể LongPress vào vị trí trên bản đồ để xác định tọa độ cho quán ăn đó.

6* Cải tiến chương trình mục 4.2.8 và kết hợp với chương trình cải tiến trong bài tập số

Chương trình sẽ điều chỉnh để vẽ đường đi từ vị trí hiện tại đến quán ăn nếu đã có tọa độ quán Nếu người dùng thực hiện Long Press tại một vị trí khác, hệ thống sẽ hiển thị thông tin tương ứng.

Dialog 128 sẽ hỏi người dùng có muốn thay đổi địa chỉ quán hay không Nếu người dùng đồng ý, địa chỉ mới sẽ được cập nhật Đối với những món ăn chưa có tọa độ, LongPess sẽ hỏi liệu người dùng có muốn lấy tọa độ làm địa chỉ quán hay không, và nếu có, địa chỉ mới cũng sẽ được cập nhật.

Ngày đăng: 11/10/2022, 21:59

HÌNH ẢNH LIÊN QUAN

Hình 1.1.1 Kiến trúc của iOS - Giáo trình lập trình di động trên iOS Dành cho bậc Cao đẳng
Hình 1.1.1 Kiến trúc của iOS (Trang 11)
Hình 1.1.4.2 Giao diện lựa chọn một Template cho ứng dụng iOS - Giáo trình lập trình di động trên iOS Dành cho bậc Cao đẳng
Hình 1.1.4.2 Giao diện lựa chọn một Template cho ứng dụng iOS (Trang 13)
Hình 1.1.4.4 Màn hình khởi tạo của Project iOS mới - Giáo trình lập trình di động trên iOS Dành cho bậc Cao đẳng
Hình 1.1.4.4 Màn hình khởi tạo của Project iOS mới (Trang 15)
Hình 1.1.4.5 Các vùng cơ bản trong bộ công cụ phát triển ứng dụng trên iOS - Giáo trình lập trình di động trên iOS Dành cho bậc Cao đẳng
Hình 1.1.4.5 Các vùng cơ bản trong bộ công cụ phát triển ứng dụng trên iOS (Trang 15)
Bảng 1.2.1 Một số cú pháp cơ bản của ngôn ngữ Swift - Giáo trình lập trình di động trên iOS Dành cho bậc Cao đẳng
Bảng 1.2.1 Một số cú pháp cơ bản của ngôn ngữ Swift (Trang 17)
Cấu trúc một iOS Project (Hình 2.1.1.3): - Giáo trình lập trình di động trên iOS Dành cho bậc Cao đẳng
u trúc một iOS Project (Hình 2.1.1.3): (Trang 56)
- ViewController.swift: File chứa code chương trình cho các màn hình cụ thể (trong ví dụ này ứng dụng chỉ có một màn hình duy nhất) - Giáo trình lập trình di động trên iOS Dành cho bậc Cao đẳng
iew Controller.swift: File chứa code chương trình cho các màn hình cụ thể (trong ví dụ này ứng dụng chỉ có một màn hình duy nhất) (Trang 57)
màn hình ứng dụng iOS hiện ra, cho phép lập trình viên xây dựng giao diện màn hình (Hình 2.1.3.1) - Giáo trình lập trình di động trên iOS Dành cho bậc Cao đẳng
m àn hình ứng dụng iOS hiện ra, cho phép lập trình viên xây dựng giao diện màn hình (Hình 2.1.3.1) (Trang 58)
Hình 2.1.4.1 Tạo ứng dụng iOS Calculator2020 (a) - Giáo trình lập trình di động trên iOS Dành cho bậc Cao đẳng
Hình 2.1.4.1 Tạo ứng dụng iOS Calculator2020 (a) (Trang 61)
Hình 2.1.3.5 Ví dụ thay đổi thuộc tính đối tượng trực tiếp trên màn hình thiết kế - Giáo trình lập trình di động trên iOS Dành cho bậc Cao đẳng
Hình 2.1.3.5 Ví dụ thay đổi thuộc tính đối tượng trực tiếp trên màn hình thiết kế (Trang 61)
Hình 2.1.4.3 Tạo ứng dụng iOS Calculator2020 (c) - Giáo trình lập trình di động trên iOS Dành cho bậc Cao đẳng
Hình 2.1.4.3 Tạo ứng dụng iOS Calculator2020 (c) (Trang 62)
Hình 2.1.4.5 Kết quả gom nhóm các file hệ thống trong System files - Giáo trình lập trình di động trên iOS Dành cho bậc Cao đẳng
Hình 2.1.4.5 Kết quả gom nhóm các file hệ thống trong System files (Trang 63)
Kết quả bước đầu của phần thiết kế giao diện sẽ giống như Hình 2.2.2.1 của bước tiếp theo trong mục 2.2.2 - Giáo trình lập trình di động trên iOS Dành cho bậc Cao đẳng
t quả bước đầu của phần thiết kế giao diện sẽ giống như Hình 2.2.2.1 của bước tiếp theo trong mục 2.2.2 (Trang 64)
Hình 2.2.2.4 Lấy các ký tự đặc biệt trong Xcode - Giáo trình lập trình di động trên iOS Dành cho bậc Cao đẳng
Hình 2.2.2.4 Lấy các ký tự đặc biệt trong Xcode (Trang 69)
Hình 2.2.2.3 Bổ sung phím chức năng cho máy tính - Giáo trình lập trình di động trên iOS Dành cho bậc Cao đẳng
Hình 2.2.2.3 Bổ sung phím chức năng cho máy tính (Trang 69)

TỪ KHÓA LIÊN QUAN

TRÍCH ĐOẠN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

w