Sådan programmeres en Spotify-klon til iOS med AutoLayout

I dette indlæg vil vi forsøge at genskabe Spotify-startskærmens layout i Swift programmatisk. Hvorfor programmatisk? Jeg synes, det er altid godt at vide, hvordan man bygger ting på forskellige måder, og jeg kan godt lide at skrive kode for at gøre tingene programmatisk. Disse færdigheder er især nyttige, hvis du arbejder med et team eller bruger versionskontrol.

Dette er den faktiske startskærm i Spotifys mobilapp. Så for at opnå denne form for layout bruger vi UICollectionView, og vi kan også bruge TabBarControllertil at oprette fanenavigatoren.

Grundlæggende krav: Sørg først for at have Xcode +10 installeret og hurtig +4.

Lad os starte med at oprette et nyt Xcode-projekt ved hjælp af Xcode:

Og den første ting, vi skal gøre i, ViewController.swifter at ændre superklassen til i UICollectionViewControllerstedet for,   UIViewControllerfordi vores klasse vil være baseret på 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. } } 

Hvis du prøver at køre appen, mislykkes build. Vi skal tilføje noget kode til AppDelegate.swiftfilen inden for didFinishLaunchingWithOptionsfunktionen forbi dette stykke kode før   returnudsagnet:

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

Og koden skal se sådan ud:

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 }

Nu skal du kunne køre appen og se backgroundColorændringen til purple:

Det næste trin er at fordele layoutet og fordele rummet ligeligt mellem sektionerne.

Lad os definere metoderne for vores CollectionView.

Trinene:

  • Registrer en genanvendelig celle med unik identifikator
  • Definer antallet af elementerne i sektionen
  • Brug den registrerede celle  

For at bruge nogle af CollectionViewmetoderne skal vi altid tilpasse os UICollectionViewDelegateFlowLayoutsom en superklasse og for at få autoComplete af metoderne. Så lad os starte med at registrere CollectionViewCell.

Inde View.DidLoad()kalder vi collectionView.register()metoden til at registrere den genanvendelige celle:

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

Derefter definerer vi antallet af celler, vi har inde i collectionViewbrugen numberOfItemsInSection. For nu skal vi bare lave det til 5 varer:

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

Det næste trin er at definere den genanvendelige celle, cellForItemAtder skal vende tilbage UICollectionViewCellog have et unikt id kaldet cellId. Koden ser sådan ud:

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

Den fulde kode skal se sådan ud:

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 } }

Du skal kunne se 5 emner med rød baggrund på skærmen:

Tilføj en tilpasset bredde og højde til cellerne

Nu skal vi placere cellerne i den rigtige rækkefølge og give dem et widthog height. Hver celle tager widthskærmbilledet som width.

Vi er heldige at have en sizeForItemAtmetode, så vi kan give cellerne en brugerdefineret widthog height. Det er en metode, der skal returnere en CGSizetype:

 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) }

Så vi fik   Celltaget widthskærmen ved at bruge view.frame.widthog en brugerdefineret heightmed er en CGFloattype.

Nu kan du se resultatet nedenfor i din simulator:

Alt ser godt ud indtil videre. Lad os denne gang oprette en brugerdefineret celle, der kan genbruges. Opret en ny Swift-fil med navnet CustomCell:

CustomCell.swift skal se sådan ud nedenfor:

 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") } } 

Nu er de næste ting, vi skal gøre, at ændre to metoder til at understøtte den genanvendelige celle, collectionView.registerog cellForItemAt. Lad os først ændre registermetoden. ErstatteUICollectionViewCell.selfmed CustomCell:

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

Dernæst skal vi støbe for cellForItemAtat overholde CustomCellnedenstående:

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

Hvis du kører appen sandsynligvis, vil du ikke mærke nogen ændring, så giv CustomCell en baggrundsfarve backgroundColor = .yellow. Glem ikke at fjerne den linje cell.backgroundColor = .redi cellForItemAt. Du skal se baggrundsfarven ændret til gul?

Nu er det tid til at lægge noget salt i CutomCell: D

Hvis du ser på Spotify-startskærmen, indeholder hver sektion, som er et CustomCelli vores eksempel, en sektionstitel, underceller og er vandret:

Tilføj en sektionstitel

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.

De sidste ting, vi kan gøre, før vi slutter denne artikel, er at subCellshave sektionens højde og bredde. Igen vi bruger sizeForItemAttil at definere heightog widthaf cellen.

 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) }

Og her er vi?:

PÆN! Jeg stopper på dette tidspunkt, så dette indlæg er ikke for længe. Jeg laver en anden del, hvor vi skal tilføje nogle hånede billeder og udfylde den med nogle data.

Fuld kildekode? her

Venligst vær venlig at hvis du har tilføjelser, spørgsmål eller rettelser, skal du sende det i kommentarerne nedenfor? eller slå mig op på Twitter.

Abonner på min e-mail-liste for at blive underrettet, når anden del af denne tutorial udgives