Cách lập trình tạo bản sao Spotify cho iOS với AutoLayout

Trong bài đăng này, chúng tôi sẽ cố gắng tạo lại bố cục màn hình chính Spotify trong Swift theo chương trình. Tại sao phải lập trình? Tôi nghĩ luôn luôn tốt nếu biết cách xây dựng mọi thứ theo những cách khác nhau và tôi thích viết mã để thực hiện mọi thứ theo lập trình. Những kỹ năng này đặc biệt hữu ích nếu bạn đang làm việc với nhóm hoặc sử dụng kiểm soát phiên bản.

Đây là màn hình chính thực tế của ứng dụng di động Spotify. Vì vậy, để đạt được kiểu bố cục này, chúng tôi sẽ sử dụng UICollectionViewvà chúng tôi cũng có thể sử dụng TabBarControllerđể tạo trình điều hướng tab.

Yêu cầu cơ bản: Đầu tiên hãy đảm bảo bạn đã cài đặt Xcode +10 và nhanh chóng +4.

Hãy bắt đầu bằng cách tạo một dự án Xcode mới bằng Xcode:

Và điều đầu tiên chúng ta cần làm ViewController.swiftlà thay đổi superClass thành UICollectionViewControllerthay   UIViewControllervì vì class của chúng ta sẽ dựa trên collectionView.

// // ViewController.swift // spotifyAutoLayout // // Created by admin on 10/31/19. // Copyright © 2019 Said Hayani. All rights reserved. // import UIKit class ViewController: UICollectionViewController { override func viewDidLoad() { super.viewDidLoad() collectionView.backgroundColor = .purple // Do any additional setup after loading the view. } } 

Nếu bạn cố gắng chạy ứng dụng, bản dựng sẽ không thành công. Chúng ta cần thêm một số mã vào AppDelegate.swifttệp trong didFinishLaunchingWithOptionshàm sau đoạn mã này trước   returncâu lệnh:

 let layout = UICollectionViewFlowLayout() window = UIWindow() window?.rootViewController = ViewController(collectionViewLayout: layout)

Và mã sẽ như thế này:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. let layout = UICollectionViewFlowLayout() window = UIWindow() window?.rootViewController = ViewController(collectionViewLayout: layout) return true }

Bây giờ, bạn sẽ có thể chạy ứng dụng và thấy backgroundColorthay đổi thành purple:

Bước tiếp theo là phân bố bố cục và chia đều không gian giữa các phần.

Hãy xác định các phương pháp của chúng tôi CollectionView.

Các bước:

  • Đăng ký một ô có thể sử dụng lại với số nhận dạng duy nhất
  • Xác định số lượng các mục trong phần
  • Sử dụng ô đã đăng ký  

Để sử dụng một số CollectionViewphương thức, chúng ta cần phải luôn tuân theo UICollectionViewDelegateFlowLayoutdưới dạng superClass và nhận được tính năng Tự động hoàn thành của các phương thức. Vì vậy, hãy bắt đầu với việc đăng ký CollectionViewCell.

Bên trong, View.DidLoad()chúng tôi gọi collectionView.register()phương thức để đăng ký ô có thể sử dụng lại:

 collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)

Sau đó, chúng tôi xác định số lượng ô mà chúng tôi sẽ có bên trong collectionViewsử dụng numberOfItemsInSection. Hiện tại, chúng ta chỉ cần làm cho nó 5 mục:

 override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 5 }

Bước tiếp theo là xác định ô có thể sử dụng lại bằng cách sử dụng ô cellForItemAtđó sẽ trả về UICollectionViewCellvà có một id duy nhất được gọi cellId. Mã trông như thế này:

 override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) cell.backgroundColor = .red return cell }

Mã đầy đủ sẽ giống như sau:

import UIKit class ViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout { let cellId : String = "cellId" override func viewDidLoad() { super.viewDidLoad() collectionView.backgroundColor = .purple collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId) } override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 5 } override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) cell.backgroundColor = .red return cell } }

Bạn sẽ có thể thấy 5 mục có nền màu đỏ trên màn hình:

Thêm chiều rộng và chiều cao tùy chỉnh vào các ô

Bây giờ chúng ta cần đặt các ô theo đúng thứ tự và cho chúng dấu widthheight. Mỗi ô sẽ có widthmàn hình là width.

Chúng tôi may mắn có sizeForItemAtphương pháp để chúng tôi có thể cung cấp cho các tế bào một tùy chỉnh widthheight. Đó là một phương thức sẽ trả về một CGSizekiểu:

 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let width = view.frame.width let height = CGFloat(200) return CGSize(width: width, height: height) }

Vì vậy, chúng tôi đã   Cellchiếm dụng widthmàn hình bằng cách sử dụng view.frame.widthvà tùy chỉnh heightvới là một CGFloatloại.

Bây giờ bạn có thể thấy kết quả bên dưới trong Trình mô phỏng của mình:

Mọi thứ có vẻ tốt cho đến nay. Lần này, hãy tạo một ô tùy chỉnh có thể được sử dụng lại. Tạo một tệp Swift mới có tên CustomCell:

CustomCell.swift sẽ giống như sau:

 import UIKit class CustomCell: UICollectionViewCell { override init(frame: CGRect) { super.init(frame: frame) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } 

Bây giờ, điều tiếp theo chúng ta phải làm là sửa đổi hai phương thức để hỗ trợ ô có thể tái sử dụng collectionView.registercellForItemAt. Đầu tiên hãy sửa đổi phương thức đăng ký. Thay thếUICollectionViewCell.selfvới CustomCell:

 collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId) 

Tiếp theo, chúng ta cần cellForItemAtép CustomCellkiểu như sau:

 let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! CustomCell

Nếu bạn chạy ứng dụng, có thể bạn sẽ không nhận thấy bất kỳ thay đổi nào, vì vậy hãy cung cấp cho CustomCell một màu nền backgroundColor = .yellow. Đừng quên xóa dòng cell.backgroundColor = .redtrong cellForItemAt. Bạn sẽ thấy màu nền thay đổi thành màu vàng?

Bây giờ là lúc cho một ít muối vào CutomCell: D

Nếu bạn nhìn vào màn hình chính Spotify, mỗi phần CustomCelltrong ví dụ của chúng tôi chứa tiêu đề phần, các ô phụ và nằm ngang:

Thêm tiêu đề phần

Let's add a title label to the cell. Create the titleLabel element inside the CutomCell class:

let titleLabel: UILabel = { let lb = UILabel() lb.text = "Section Title" lb.font = UIFont.boldSystemFont(ofSize: 14) lb.font = UIFont.boldSystemFont(ofSize: 14) return lb }()

Then add the element to the view inside init() block:

addSubview(titleLabel)

If you run the app you won't see any changes, and that's because we didn't put any constraint to the element yet. So let's add some constraints – add this property              lb.translatesAutoresizingMaskIntoConstraints = falsetotitleLabel to be able to apply constraints to the element:

After we add titleLabel to the view, we define the constraints:

 addSubview(titleLabel) titleLabel.topAnchor.constraint(equalTo: topAnchor, constant: 8).isActive = truetitleLabel.leftAnchor.constraint(equalTo: leftAnchor,constant: 8 ).isActive = true

Always make sure to add .isActive = true property – without it the constraint won't work!

Before we move on to the next part, let's first change the background color of the screen to black and also remove the yellow color for the cells:

Now comes the big part: putting sub cells into each cell. To achieve that we are going to add a CollectionView inside CustomCell.

To add a CollectionView inside UICollectionViewCell we need to add  properties UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, and UICollectionViewDataSource as superClass to CustomCell.

Let's create the collectionView element as any simple view:

 let collectionView : UICollectionView = { // init the layout let layout = UICollectionViewFlowLayout() // set the direction to be horizontal layout.scrollDirection = .horizontal // the instance of collectionView let cv = UICollectionView(frame: .zero, collectionViewLayout: layout) // Activate constaints cv.translatesAutoresizingMaskIntoConstraints = false return cv }()

Notice that we add layout to the collectionView as layer in the initializer as we did the first time with the viewController.swift. Here we also specify the direction of the FlowLayout to be .horizontal.

Let's add the collectionView element to the view as subView.

We gonna make a function that do that for us to make the code a little bit cleaner.

 fileprivate func setupSubCells(){ // add collectionView to the view addSubview(collectionView) collectionView.dataSource = self collectionView.delegate = self // setup constrainst // make it fit all the space of the CustomCell collectionView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor).isActive = true collectionView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true collectionView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true collectionView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true } 

Make sure to set delegate to self for the collectionView and the dataSource as well:

 collectionView.dataSource = self

  collectionView.delegate = self

Then call the function within init block.

Xcode will display some errors if you trying to build the app because we are not conforming to UICollectionViewDelegate and UICollectionViewDelegateFlowLayout protocols. To fix that we need first to register the sub cell as a reusable cell.

Create a variable at the top of the class and give it a name of cellId so we can use it when we need the cell identifier:

let cellId : String = "subCellID"

collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)

Now we're missing two more methods to make the errors go away: numberOfItemsInSection that define the number of cells in the section and cellForItemAt that define the reusable cell. These methods are necessary for  collectionView to work properly:

 // number of cells func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 4 } // reusable Cell func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) cell.backgroundColor = .yellow return cell }

The results should look like this:

As you can see, the collectionView are in purple as background and sub cells are yellow.

Những điều cuối cùng chúng ta có thể làm trước khi kết thúc bài viết này là tạo subCellschiều cao của phần và chiều rộng. Một lần nữa, chúng tôi đang sử dụng sizeForItemAtđể xác định heightwidthcủa ô.

 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let width = frame.height let height = frame.height return CGSize(width: width, height: height) }

Và chúng tôi đây ?:

ĐẸP! Tôi sẽ dừng lại ở điểm này để bài viết này không quá dài. Tôi sẽ làm phần thứ hai, nơi chúng ta sẽ thêm một số hình ảnh chế nhạo và điền vào nó một số dữ liệu.

Mã nguồn đầy đủ? đây

Vui lòng vui lòng nếu bạn có bất kỳ bổ sung, câu hỏi hoặc chỉnh sửa nào, hãy đăng nó trong phần bình luận bên dưới? hoặc đánh tôi trên Twitter.

Đăng ký danh sách email của tôi để được thông báo khi phần thứ hai của hướng dẫn này được xuất bản