Neomorfisme menggunakan SwiftUI. Bagian 1

Salut, orang Khabrovit! Untuk mengantisipasi peluncuran kursus lanjutan "Pengembang iOS", kami telah menyiapkan terjemahan lain yang menarik.




Desain non-morfik mungkin merupakan tren paling menarik dalam beberapa bulan terakhir, meskipun, sebenarnya, Apple menggunakannya sebagai motif desainnya di WWDC18. Pada artikel ini, kita akan melihat bagaimana Anda dapat mengimplementasikan desain non-morfik menggunakan SwiftUI, mengapa Anda mungkin ingin melakukan ini, dan - yang paling penting - bagaimana kita dapat memperbaiki desain ini untuk meningkatkan aksesibilitasnya.
Penting : Neomorfisme - juga kadang-kadang disebut neuromorfisme - memiliki konsekuensi serius untuk aksesibilitas, jadi terlepas dari godaan untuk membaca bagian pertama dari artikel ini dan melewatkan sisanya, saya mendorong Anda untuk membaca artikel sampai akhir dan mempelajari kelebihan dan kekurangannya sehingga Anda dapat melihat keseluruhan gambar .


Dasar-dasar Neomorfisme


Sebelum kita beralih ke kode, saya ingin menguraikan secara singkat dua prinsip dasar dari arah dalam desain ini, karena mereka akan relevan ketika kita bergerak bersama:

  1. Neomorfisme menggunakan silau dan bayangan untuk menentukan bentuk objek di layar.
  2. Kontras cenderung menurun; benar-benar putih atau hitam tidak digunakan, yang memungkinkan Anda untuk menyoroti highlight dan bayangan.

Hasil akhirnya adalah tampilan yang mengingatkan pada "plastik ekstrusi" - desain antarmuka pengguna yang tentu saja terlihat segar dan menarik tanpa menabrak mata Anda. Saya tidak bisa tidak mengulangi sekali lagi bahwa mengurangi kontras dan menggunakan bayangan untuk menyoroti bentuk secara serius mempengaruhi aksesibilitas, dan kami akan kembali ke sini nanti.

Namun, saya masih berpikir bahwa waktu yang dihabiskan untuk belajar tentang neomorfisme di SwiftUI tidak sia-sia - bahkan jika Anda tidak menggunakannya dalam aplikasi Anda sendiri, itu seperti kata penulisan kode untuk membantu mengasah keterampilan Anda.

Baiklah, cukup bicara diam - mari kita beralih ke kode.

Membangun peta non-morfik


Titik awal paling sederhana adalah membuat peta non-morfik: persegi panjang bulat yang akan berisi beberapa informasi. Selanjutnya, kita akan melihat bagaimana kita dapat mentransfer prinsip-prinsip ini ke bagian lain dari SwiftUI.

Mari kita mulai dengan membuat proyek iOS baru menggunakan templat aplikasi Single View. Pastikan Anda menggunakan SwiftUI untuk antarmuka pengguna, dan kemudian beri nama proyek Neumorphism.

Kiat: jika Anda memiliki akses untuk melihat pratinjau SwiftUI dalam Xcode, saya sarankan Anda mengaktifkannya segera - itu akan jauh lebih mudah bagi Anda untuk bereksperimen.

Kami akan mulai dengan menentukan warna yang mewakili rona krem. Ini bukan abu-abu murni, melainkan warna yang sangat halus yang menambahkan sedikit kehangatan atau kesejukan pada antarmuka. Anda dapat menambahkannya ke direktori aset jika Anda mau, tetapi sekarang lebih mudah dilakukan dalam kode.

Tambahkan ini di Colorluar struktur ContentView:

extension Color {
    static let offWhite = Color(red: 225 / 255, green: 225 / 255, blue: 235 / 255)
}

Ya, hampir putih, tapi cukup gelap untuk membuat putih terlihat seperti cahaya saat kita membutuhkannya.

Sekarang kita dapat mengisi tubuh ContentViewdengan menyediakannya ZStack, yang menempati seluruh layar, menggunakan warna putih-kuasi baru kita untuk mengisi seluruh ruang:

struct ContentView: View {
    var body: some View {
        ZStack {
            Color.offWhite
        }
        .edgesIgnoringSafeArea(.all)
    }
}

Untuk mewakili peta kita, kita akan menggunakan persegi panjang bulat dalam resolusi 300x300 untuk membuatnya indah dan jelas di layar. Jadi tambahkan ini ke ZStackbawah warna:

RoundedRectangle(cornerRadius: 25)
    .frame(width: 300, height: 300)

Secara default akan berwarna hitam, tetapi untuk penerapan neomorfisme kami ingin mengurangi kontras secara tajam, jadi kami akan menggantinya dengan warna yang sama dengan yang kami gunakan untuk latar belakang, bahkan membuat bentuknya tidak terlihat.

Jadi ubah seperti ini:

RoundedRectangle(cornerRadius: 25)
    .fill(Color.offWhite)
    .frame(width: 300, height: 300)

Poin penting: kami menentukan bentuk menggunakan bayangan, satu gelap dan satu cahaya, seolah-olah cahaya melemparkan sinar dari sudut kiri atas layar.

SwiftUI memungkinkan kita untuk menerapkan modifier beberapa kali, yang memfasilitasi implementasi neomorfisme. Tambahkan dua pengubah berikut ke persegi panjang bulat Anda:

.shadow(color: Color.black.opacity(0.2), radius: 10, x: 10, y: 10)
.shadow(color: Color.white.opacity(0.7), radius: 10, x: -5, y: -5)

Mereka mewakili offset bayangan gelap di sudut kanan bawah dan offset bayangan cahaya di sudut kiri atas. Bayangan cahaya terlihat karena kami menggunakan latar belakang putih kuasi, dan sekarang peta menjadi terlihat.

Kami telah menulis hanya beberapa baris kode, tetapi kami sudah memiliki peta non-morfik - Saya harap Anda akan setuju bahwa SwiftUI secara mengejutkan memfasilitasi prosesnya!



Membuat tombol non-morfik sederhana


Dari semua elemen UI, neomorfisme memiliki risiko yang cukup rendah untuk kartu - jika UI di dalam kartu Anda jelas, kartu mungkin tidak memiliki batas yang jelas, dan ini tidak akan mempengaruhi aksesibilitas. Tombol adalah masalah lain, karena mereka dirancang untuk berinteraksi, sehingga mengurangi kontrasnya bisa lebih berbahaya daripada kebaikan.

Mari kita hadapi ini dengan menciptakan gaya tombol kita sendiri, karena ini adalah cara SwiftUI memungkinkan konfigurasi tombol untuk didistribusikan di banyak tempat. Ini jauh lebih nyaman daripada menambahkan banyak pengubah ke setiap tombol yang Anda buat - kami hanya dapat mendefinisikan gaya sekali dan menggunakannya di banyak tempat.

Kita akan mendefinisikan gaya tombol yang sebenarnya akan kosong: SwiftUI akan memberi kita label untuk tombol, yang mungkin berupa teks, gambar, atau yang lainnya, dan kami akan mengirimkannya kembali tanpa perubahan.

Tambahkan struktur ini di luar ContentView:

struct SimpleButtonStyle: ButtonStyle {
    func makeBody(configuration: Self.Configuration) -> some View {
        configuration.label
    }
}

Yang ini configuration.labeladalah yang menahan isi tombol, dan segera kami akan menambahkan sesuatu yang lain. Pertama, mari kita mendefinisikan tombol yang menggunakannya sehingga Anda dapat melihat bagaimana desain berkembang:

Button(action: {
    print("Button tapped")
}) {
    Image(systemName: "heart.fill")
        .foregroundColor(.gray)
}
.buttonStyle(SimpleButtonStyle())

Anda tidak akan melihat sesuatu yang istimewa di layar, tetapi kami dapat memperbaikinya dengan menambahkan efek non-morfis kami ke gaya tombol. Kali ini kita tidak akan menggunakan persegi panjang bulat, karena untuk ikon sederhana, lingkaran lebih baik, tetapi kita perlu menambahkan lekukan sehingga area klik tombol besar dan indah.
Ubah metode Anda makeBody()dengan menambahkan beberapa lekukan dan kemudian menempatkan efek non-morf kami sebagai latar belakang tombol:

configuration.label
    .padding(30)
    .background(
        Circle()
            .fill(Color.offWhite)
            .shadow(color: Color.black.opacity(0.2), radius: 10, x: 10, y: 10)
            .shadow(color: Color.white.opacity(0.7), radius: 10, x: -5, y: -5)
    )



Ini membawa kita cukup dekat dengan efek yang diinginkan, tetapi jika Anda menjalankan aplikasi, Anda akan melihat bahwa dalam praktiknya perilaku tersebut masih belum sempurna - tombol tidak bereaksi secara visual ketika ditekan, yang terlihat hanya aneh.

Untuk memperbaikinya, kita perlu membaca properti configuration.isPresseddi dalam gaya tombol kustom kami, yang memberitahu apakah tombol saat ini ditekan atau tidak. Kita dapat menggunakan ini untuk meningkatkan gaya kita untuk memberikan beberapa indikasi visual apakah tombol ditekan.

Mari kita mulai dengan yang sederhana: kita akan menggunakan Grouptombol untuk latar belakang, lalu periksa configuration.isPresseddan kembalikan lingkaran datar jika tombol ditekan, atau lingkaran gelap kita saat ini sebaliknya:

configuration.label
    .padding(30)
    .background(
        Group {
            if configuration.isPressed {
                Circle()
                    .fill(Color.offWhite)
            } else {
                Circle()
                    .fill(Color.offWhite)
                    .shadow(color: Color.black.opacity(0.2), radius: 10, x: 10, y: 10)
                    .shadow(color: Color.white.opacity(0.7), radius: 10, x: -5, y: -5)
            }
        }
    )

Karena sebuah isPressedlingkaran dengan warna putih kuasi digunakan di negara bagian , itu membuat efek kita tidak terlihat ketika tombol ditekan.

Peringatan: karena cara SwiftUI menghitung area yang dapat direkam, kami tidak sengaja membuat area klik untuk tombol kami sangat kecil - sekarang Anda perlu mengetuk gambar itu sendiri, dan bukan pada desain unomorfik di sekitarnya. Untuk memperbaikinya, tambahkan pengubah .contentShape(Circle())segera setelah itu .padding(30), memaksa SwiftUI untuk menggunakan semua ruang tap yang tersedia.

Sekarang kita dapat membuat efek concavity buatan dengan membalik bayangan - dengan menyalin dua pengubah shadowdari efek dasar, menukar nilai X dan Y untuk putih dan hitam, seperti yang ditunjukkan di sini:

if configuration.isPressed {
    Circle()
        .fill(Color.offWhite)
        .shadow(color: Color.black.opacity(0.2), radius: 10, x: -5, y: -5)
        .shadow(color: Color.white.opacity(0.7), radius: 10, x: 10, y: 10)
} else {
    Circle()
        .fill(Color.offWhite)
        .shadow(color: Color.black.opacity(0.2), radius: 10, x: 10, y: 10)
        .shadow(color: Color.white.opacity(0.7), radius: 10, x: -5, y: -5)
}

Perkirakan hasilnya.



Buat bayangan internal untuk klik tombol


Kode kami saat ini, pada prinsipnya, sudah berfungsi, tetapi orang-orang mengartikan efeknya secara berbeda - beberapa melihatnya sebagai tombol cekung, yang lain melihat bahwa tombol tersebut masih belum ditekan, hanya saja cahaya datang dari sudut yang berbeda.

Gagasan peningkatan adalah untuk menciptakan bayangan batin yang akan mensimulasikan efek menekan tombol ke dalam. Ini bukan bagian dari kit SwiftUI standar, tetapi kami dapat menerapkannya dengan mudah.

Membuat bayangan dalam membutuhkan dua gradien linier, dan mereka akan menjadi yang pertama dari banyak gradien internal yang akan kita gunakan dalam artikel ini, jadi kami akan segera menambahkan ekstensi bantu kecil untuk LinearGradient, untuk menyederhanakan pembuatan gradien standar:

extension LinearGradient {
    init(_ colors: Color...) {
        self.init(gradient: Gradient(colors: colors), startPoint: .topLeading, endPoint: .bottomTrailing)
    }
}

Dengan ini, kami cukup menyediakan daftar variabel warna untuk mendapatkan kembali gradien liniernya dalam arah diagonal.

Sekarang tentang poin yang penting: alih-alih menambahkan dua bayangan terbalik ke lingkaran yang ditekan, kita akan melapisi lingkaran baru dengan blur (goresan), dan kemudian menerapkan lingkaran lain dengan gradien sebagai topeng. Ini sedikit rumit, tetapi izinkan saya menjelaskannya sedikit demi sedikit:

  • Lingkaran dasar kami adalah lingkaran kami saat ini dengan efek neomorfik, diisi dengan warna kuasi-putih.
  • Kami menempatkan lingkaran di atasnya, dibingkai oleh bingkai abu-abu, dan sedikit buram untuk melunakkan ujungnya.
  • Lalu kami menerapkan topeng dengan lingkaran lain ke lingkaran ini ditumpangkan di atas, kali ini diisi dengan gradien linier.

Ketika Anda menerapkan satu tampilan sebagai mask untuk yang lain, SwiftUI menggunakan saluran alpha mask untuk menentukan apa yang harus ditampilkan dalam tampilan dasar.

Jadi, jika kita menggambar goresan abu-abu buram, dan kemudian menutupinya dengan gradien linier dari hitam ke transparan, goresan buram akan terlihat di satu sisi dan secara bertahap meningkat di sisi lain - kita akan mendapatkan gradien internal yang halus. Untuk membuat efek lebih jelas, kita dapat sedikit menggeser lingkaran teduh di kedua arah. Setelah bereksperimen sedikit, saya menemukan bahwa menggambar bayangan cahaya dengan garis yang lebih tebal daripada yang gelap membantu memaksimalkan efek.

Ingat bahwa dua bayangan digunakan untuk menciptakan rasa kedalaman pada neomorfisme: satu cahaya dan satu gelap, jadi kami akan menambahkan efek bayangan batin ini dua kali dengan warna berbeda.

Ubah lingkaran configuration.isPresssebagai berikut:

Circle()
    .fill(Color.offWhite)
    .overlay(
        Circle()
            .stroke(Color.gray, lineWidth: 4)
            .blur(radius: 4)
            .offset(x: 2, y: 2)
            .mask(Circle().fill(LinearGradient(Color.black, Color.clear)))
    )
    .overlay(
        Circle()
            .stroke(Color.white, lineWidth: 8)
            .blur(radius: 4)
            .offset(x: -2, y: -2)
            .mask(Circle().fill(LinearGradient(Color.clear, Color.black)))
    )

Jika Anda menjalankan aplikasi lagi, Anda akan melihat bahwa efek menekan tombol jauh lebih jelas dan terlihat lebih baik.



Pada bagian ini, bagian pertama dari terjemahan berakhir. Dalam beberapa hari mendatang kami akan menerbitkan kelanjutan, dan sekarang kami mengundang Anda untuk mempelajari lebih lanjut tentang kursus mendatang .

All Articles