Published on

Spotify kloni (2-qism): Grid va Yangi Relizlar komponentlari

Authors

Asosiy oyna ustida ishlashda davom etamiz

Oldingi darsda biz yuqori qismdagi menyu qatorini yig'ib chiqdik. Endi Spotify tarmog'iga xos bo'lgan "Yaqinda eshitilganlar" (Recent section) hamda bitta kattaroq chiqib turuvchi "Yangi reliz" (New release) qismlarini yaratamiz.

1. Yaqinda eshitilganlar (Recent Section)

Bu qism odatda 6 yoki 8 ta kichik to'rtburchak katakchalardan (cells) tashkil topgan bo'lib, ikkita ustunli (column) ko'rinishda joylashadi.

Avvalo kichik katakchani SpotifyRecentCell nomi bilan alohida yaratib olamiz:

import SwiftUI

struct SpotifyRecentCell: View {
    var imageName: String = "https://picsum.photos/600/600"
    var title: String = "Some random title"

    var body: some View {
        HStack(spacing: 16) {
            ImageLoaderView(urlString: imageName)
                .frame(width: 55, height: 55)

            Text(title)
                .font(.callout)
                .fontWeight(.semibold)
                .lineLimit(2) // Maksimum 2 qator

            Spacer()
        }
        .padding(.trailing, 8)
        .frame(maxWidth: .infinity, alignment: .leading)
        .background(Color.spotifyDarkGray)
        .cornerRadius(6)
    }
}

Bu katakchalarni ikkita ustunga chiroyli qilib joylashtirish uchun SwiftUI dagi odatiy LazyVGrid dan emas, balki oldingi safar biz o'rnatgan paketimiz — SwiftfulUI dagi NonLazyVGrid dan foydalanamiz. Kichik ro'yxatlarda u ancha chiroyliroq va xatosiz ishlaydi:

NonLazyVGrid(columns: 2, alignment: .center, spacing: 10, items: products) { product in
    if let product {
        SpotifyRecentCell(
            imageName: product.images.first ?? "",
            title: product.title
        )
    }
}

2. Takrorlanuvchi dizayn uchun ViewModifier

Bizning "Yaqinda eshitilganlar" katakchasimizning fon rangi va tekstining rangi (kulrang fon, oq tekst) xuddi yuqoridagi Kategoriyalar menyusi kabi bir xil. Ikkala joyga ham bir xil uzunlikdagi rang kodlarini yozib yotmaslik uchun, ViewModifier orqali themeColors degan yordamchi funksiya yaratib olamiz:

extension View {
    func themeColors(isSelected: Bool) -> some View {
        self
            .background(isSelected ? Color.spotifyGreen : Color.spotifyDarkGray)
            .foregroundColor(isSelected ? Color.spotifyBlack : Color.spotifyWhite)
    }
}

Endi shunchaki .themeColors(isSelected: false) deb chaqirsak, qolgan hamma rang kodlari avtomatik tushadi.

3. "Yangi Reliz" (New Release) komponenti

Bu huddi bitta katta yangilik kabi ko'rinadigan qism. Unda artist rasmi, albom cover'i (rasmi) va ikkita tugma bor.

Biz qismlarni qanchalik ko'p "Component" larga bo'lib tashlasak (modullashtirsak), asosiy kodimiz (Home View) shunchalik toza va ixcham bo'ladi:

struct SpotifyNewReleaseCell: View {
    var imageName: String
    var headline: String?
    var subheadline: String?
    var title: String?
    var subtitle: String?

    // Tugmalar uchun maxsus opsional harakatlar (closures)
    var onAddToPlaylistPressed: (() -> Void)? = nil
    var onPlayPressed: (() -> Void)? = nil

    var body: some View {
        VStack(spacing: 16) {
            // ... (Tepada Artist rasmi va ismi bo'lgan HStack)

            HStack {
                ImageLoaderView(urlString: imageName)
                    .frame(width: 140, height: 140)

                VStack(alignment: .leading, spacing: 32) {
                    VStack(alignment: .leading, spacing: 2) {
                        Text(title ?? "")
                            .font(.callout)
                            .fontWeight(.semibold)
                            .foregroundColor(.spotifyWhite)

                        Text(subtitle ?? "")
                            .font(.callout)
                            .foregroundColor(.spotifyLightGray)
                    }

                    HStack(spacing: 0) {
                        Image(systemName: "plus.circle")
                            .foregroundColor(.spotifyLightGray)
                            .font(.title3)
                            .padding(4)
                            .onTapGesture {
                                onAddToPlaylistPressed?() // Bosilganda funksiya ishlaydi
                            }

                        Spacer()

                        Image(systemName: "play.circle.fill")
                            .foregroundColor(.spotifyWhite)
                            .font(.title)
                    }
                }
                .padding(.trailing, 16)
            }
            .themeColors(isSelected: false)
            .cornerRadius(8)
            .onTapGesture {
                onPlayPressed?()
            }
        }
    }
}

E'tibor bergan bo'lsangiz, tugmalar mantiqini (onPlayPressed) shu komponentning ichiga "qotirib" yozmadik. Uning o'rniga, funksiyani tashqaridan qabul qilib oladigan qilib ochiq qoldirdik.

Bu eng to'g'ri (Best practice) usuldir! Komponent qaysi tugma qanday amal bajarishini o'ylamasligi kerak, uni faqat asosiy ekranga chaqirganimizda tashqaridan biriktiramiz xolos.

Keyingi qismda asosiy ekranni tugatamiz va playlistlarni qo'shamiz!

Buy mea coffee