Ana içeriğe geç
  1. 100 Günde SwiftUI Notları/

26.Gün - SwiftUI Stepper, DatePicker, Date ve Create ML

SwiftUI Stepper Kullanımı #

SwiftUI kullanıcıların sayı girmesine izin veren iki yola sahiptir;

  • Stepper : Bir sayı seçmek için dokunulabilen basit bir - ve + butonudur.
  • Slider : Bir dizi değer arasından seçim yapmamıza olanak tanır.

Stepper’ı her türlü sayı ile kullanabiliriz. Bu sebeple onları Int, Double ve daha fazlasına bağlayabiliriz, otomatik olarak uyum sağlayacaktır. Örneğin aşağıdaki gibi bir property oluşturabiliriz;

@State private var sleepAmount = 8.0

Daha sonra bunu bir stepper’e bağlayabiliriz, böylece mevcut değeri şu şekilde gösterebiliriz.

Stepper("\(sleepAmount) hours", value: $sleepAmount)

Stepper Example in UI

Bu kod çalıştığında 8.000000 hours yazısını görürüz ve - ve + tuşlarına dokunarak 7,6,5 ve negatif sayılara inebilir ya da 9, 10, 11 gibi sayılara çıkabiliriz.

Varsayılan olarak stepper’ler yalnızca depolama alanlarının aralığı ile sınırlıdır. Bu örnekte bir Double kullanıyoruz, bu da stepper’ın maksimum değerinin çok büyük olacağı anlamına geliyor.

Stepper aşağıdaki gibi bir aralık sağlayarak kabul etmek istediğimiz değerleri sınırlamamıza izin veriyor.

Stepper("\(sleepAmount) hours", value: $sleepAmount, in: 4...12)

Yukarıdaki kod ile stepper 8’den başlayacak, ardından kullanıcının 4 ile 12 arasında hareket etmesine izin verecek ancak ötesine geçmeyecektir.

Stepper için dördüncü bir kullanışlı parametre vardır, bu da bir adım değeridir (step). Bu adım değeri sayesinde her - veya + ‘ya dokunulduğunda ne kadar hareket ettirileceğini belirleyebiliriz. Bu herhangi bir sayı olabilir, ancak stepper’i oluşturduğumuz türle eşleşmelidir. Yani stepper’ı bir tamsayıya bağlıyorsak adım değeri olarak Double kullanamayız.

Bu örnekte, kullanıcıların 0,25 adımlar ile 4 ve 12 arasında herhangi bir değeri seçebileceğini söyleyebiliriz.

Stepper("\(sleepAmount) hours", value: $sleepAmount, in: 4...12, step: 0.25)

Devam etmeden önce, 8,000000 yazısını biraz düzeltelim ve fazla sıfırlardan kurtulalım. Bunu düzeltmek için formatted() kullanarak Double’ı biçimlendirebiliriz.

Stepper("\(sleepAmount.formatted()) hours", value: $sleepAmount, in: 4...12, step: 0.25)

SwiftUI DatePicker Kullanımı #

SwiftUI bize date property’ye bağlanabilen DatePicker adında özel bir seçici türü sunuyor. Tabiki bunun içinde Date türümüz bulunmakta.

Bunu kullanmak için aşağıdaki gibi bir @State property ile başlayabiliriz.

@State private var wakeUp = Date.now

Daha sonra bunu aşağıdaki gibi bir date picker’a bağlayabiliriz.

DatePicker("Please enter a date", selection: $wakeUp)

SwiftUI DatePicker

Yukarıdaki ekran görüntüsünü incelerseniz, “Please enter a date” yazısının çirkin göründüğünü düşünebilir ve aşağıdaki kodla değiştirmeyi düşünebilirsiniz.

DatePicker("", selection: $wakeUp)

Ancak bunu yaparsak iki sorunla karşılaşırız: DatePicker boş olsa bile bir etiket için yer açacaktır ayrıca ekran okuyucusu etkin olan kullanıcılar (VoiceOver) tarih seçicinin ne işe yaradığı hakkında herhangi bir fikre sahip olmayacaktır.

Boş string göndermek yerine daha iyi bir alternatif, labelsHidden() modifier’ını aşağıdaki gibi kullanmaktır.

DatePicker("Please enter a date", selection: $wakeUp)
    .labelsHidden()

VoiceOver’ın kullanabilmesi için orijinal etiket hala mevcut, ancak artık ekranda görünmüyorlar.

Date Picker nasıl çalışacaklarına dair bir kaç yapılandırma seçeneği sunar. Kullanıcının ne tür seçenekler görmesi gerektiğine karar vermek için displayedComponenets ’i kullanabiliriz.

  • Bu parametreyi sağlamazsak, kullanıcılar gün saat ve dakika seçeneklerini görür.
  • Eğer .date kullanırsak kullanıcılar ay, gün ve yılı görür.
  • Eğer .hourAndMinute kullanırsak, kullanıcılar sadece saat ve dakika bileşenlerini görürler.

Aşağıdaki kod ile kesin bir zaman seçebiliriz.

DatePicker("Please enter a time", selection: $wakeUp, displayedComponents: .hourAndMinute)

SwiftUI DatePicker Only Time

Son olarak Stepper ile aynı şekilde çalışan bir in parametresi vardır. Bu parametreye bir tarih aralığı verebiliriz ve date picker bunun ötesine geçemez.

Bir süredir range’leri kullanıyoruz ve 1…5 veya 0..<10 gibi şeyler görmeye alışığız ancak Swift’te tarihi de range ile kullanabiliriz.

func exampleDates() {
    // create a second Date instance set to one day in seconds from now
    // şu andaki tarihe 86400 saniye yani 24 saat ekleyerek bir sonraki günü oluşturur
    let tomorrow = Date.now.addingTimeInterval(86400)

    // create a range from those two
    // bu ikisinden bir tarih oluşturur
    let range = Date.now...tomorrow
}

DatePicker ile bu gerçekten kullanışlıdır, ancak daha da iyi bir şey var. Swift tek taraflı aralıklar oluşturmamıza izin verir. Başlangıcı veya sonu belirttiğimiz ancak her ikisini de belirtmediğimiz aralıklar.

Örneğin, aşağıdaki gibi bir date picker oluşturabiliriz.

DatePicker("Please enter a date", selection: $wakeUp, in: Date.now...)

Yukarıdaki kod, gelecekteki tüm tarihlere izin verir, ancak geçmişteki tarihlerin hiçbirine izin vermez. Bunu “mevcut tarihten herhangi bir şeye kadar” olarak okuyabiliriz.

Date Türü ile Çalışma #

Kullanıcıların tarih girmesini sağlamak, DatePicker ’e Date türünde bir @State property’yi bağlamak kadar kolaydır, ancak sonrasında işler biraz daha karmaşık hale gelir.

Şu basit örneğe bakalım;

let now = Date.now
let tomorrow = Date.now.addingTimeInterval(86400)
let range = now...tomorrow

Yukarıdaki kod bugünden yarın aynı saate kadar bir aralık oluşturur. (86400 bir gündeki saniye sayısıdır)

Bu yeterince kolay görünebilir, ancak tüm günlerin 86400 saniyesi var mı? Yaz saati uygulamasını düşünün saatler bazen ileri bazen geri gider, yani o günlerde 23 ya da 25 saatimiz olabilir.

Aşağıdaki görüntüde cal ve cal 9 1752 komutları ile terminalde oluşturulmuş 2 adet takvim görebilirsiniz. Fakat ikinci takvimde, Julian’dan Gregorian geçiş nedeniyle 12 gün eksik olduğunu fark edeceksiniz.

Different Calendars

Tarih işlemleri biraz karmaşık olabilmesi sebebiyle bu noktada Apple’ın framework’lerine güvenmemiz gerekmekte.

Yaptığımız projede (BetterRest) tarihleri üç şekilde kullanacağız;

  1. Mantıklı bir varsayılan “uyanma” saati seçmek.
  2. Uyanmak istedikleri saati ve dakikayı okutmak.
  3. Önerilen yatma saatlerini düzgün bir şekilde göstermek.

İsteseydik tüm bunları elle yapabilirdik, ancak o zaman yaz saati uygulamasını, artık saniyeleri ve Gregoryan takvimi düşünmemiz gerekecekti.

Tüm bu işleri İOS’a bırakmak hem daha kolay hem de çok daha iyi.

Makul bir uyanma saati seçmekle başlayarak bunların her birini ayrı ayrı ele alalım.

Gördüğünüz gibi, Swift bize tarihlerle çalışmamız için Date ’i veriyor ve bu da yıl, ay, tarih, saat, dakika, saniye, zaman dilimi ve daha fazlasını kapsıyor. Ancak, biz bunların çoğunu düşünmek istemiyoruz, “bugün hangi gün olursa olsun, bana sabah 8’de uyanma saati ver” demek istiyoruz.

Swift’in bu amaç için DateComponenets adında farklı bir türü vardır. Bu tür ile bir tarihin tamamı yerine belirli kısımlarını okumamızı veya yazmamızı sağlar.

Yani, bugün sabah 8’i temsil eden bir tarih istiyorsak, şöyle bir kod yazabiliriz;

var components = DateComponents()
components.hour = 8
components.minute = 0
let date = Calendar.current.date(from: components)

Tarih doğrulama ile ilgili zorluklar nedeniyle, date(from:) methodu aslında optional bir tarih döndürür. Bu nedenle “bu başarısız olursa, bana geçerli tarihi geri ver” demek için nil coalescing kullanmak iyi bir fikirdir.

let date = Calendar.current.date(from: components) ?? .now

İkinci zorluk ise uyanmak istedikleri saati nasıl okuyabileceğimizdir. DatePicker ’ın bize birçok bilgi veren bir Date ’e bağlı olduğunu unutmayın. Bu nedenle sadece saat ve dakika bileşenlerini çekmenin bir yolunu bulmamız gerekir.

Yine DateComponents imdadımıza yetişiyor. iOS’tan bir tarihten belirli bileşenleri sağlamasını isteyebilir, arından bunları geri okuyabiliriz. DateComponents ’in çalışma şekli nedeniyle istediğimiz değeler ile aldığımız değeler arasında bir kopukluk olabilir. saat ve dakika isteyebiliriz, ancak bize tüm özellikler için optional bir DateComponents instance geri verilecektir. Evet saat ve dakikanın orda olacağını biliyoruz çünkü istediklerimiz bunlardı, ancak yine de optional değeleri açmamız veya varsayılan değerleri sağlamamız gerekiyor.

Yani şöyle bir kod yazabiliriz;

let components = Calendar.current.dateComponents([.hour, .minute], from: someDate)
let hour = components.hour ?? 0
let minute = components.minute ?? 0

Son zorluk ise tarih ve saatleri nasıl biçimlendirebileceğimizdir ve burada iki seçeneğimiz var

İlki, geçmişte bizim için çok iyi çalışan format parametresine güvenmektir. Burada tarihin hangi kısımlarını göstermek istediğimizi söyleyebiliriz.

Örneğin, bir tarihten sadece saati almak isteseydik bunu yazardık;

Text(Date.now, format: .dateTime.hour().minute())

Ya da gün, ay ve yıl bilgisini istiyorsak bunu yazmamız gerekir;

Text(Date.now, format: .dateTime.day().month().year())

Bunun farklı tarih biçimlerini işlemeye nasıl uyum sağladığını merak edebilirsiniz. Örneğin Türkiye’de gün/ay/yıl olarak kullanıyoruz, ancak bazı ülkeler ay/gün/yıl kullanıyor. İşin güzel kısmı bu konuda endişelenmemize gerek olmamasıdır. day().month().year() yazdığımızda, verileri düzenlemek yerine bu verileri isteriz ve iOS, kullanıcının tercihlerini kullanarak bu verileri otomatik olarak biçimlendirir.

Alternatif olarak, formatted() methodunu doğrudan tarihlerde kullanabilir ve hem tarihin hem de saatin nasıl biçimlendirilmesini istediğimize ilişkin yapılandırma seçeneklerini aşağıdaki gibi iletebiliriz;

Text(Date.now.formatted(date: .long, time: .shortened))

Create ML ile Model Eğitme #

İki Apple Framework’ü sayesinde, cihaz üzerinde makine öğrenimi şaşırtıcı derecede kolay: Core ML ve Create ML. Bunlardan ilki makine öğrenimini kullanarak uygulama yapmamızı sağlarken, ikincisi kendi özel makine öğrenimi modellerimizi oluşturmamıza olanak tanıyor. Tüm bu çalışmaların bir sonucu olarak, artık herkes uygulamalarına makine öğrenimini ekleyebilir.

Core ML görüntüleri, sesleri ve hatta hareketleri tanıma gibi çeşitli öğrenim (tarining) görevlerini yerine getirebilir, bu örnekte tabular regression’u inceleyeceğiz. Bu makine öğreniminde yaygın olan bir isimdir, Create ML’ye elektronik tablo benzeri veri atıp çeşitli değeler arasındaki ilişkiyi bulmasını isteyebileceğimiz bir yöntemdir.

Makine öğrenimi iki adımda gerçekleştirilir; modeli eğitiriz ardından modelden tahminler yapmasını isteriz. Eğitim, bilgisayarın elimizdeki tüm değeler arasındaki ilişkiyi bulmak için tüm verilerimize bakması sürecidir ve büyük veri setlerinde uzun zaman alabilir. Tahmin (prediction) cihaz üzerinde yapılır, yani eğitilmiş modele verdiğimiz verileri kullanarak yeni veriler hakkında tahminde bulunmasını isteriz.

Şimdi training sürecine başlayalım. XCode menüsüne gidip Open Developer Tool > Create ML’yi seçerek Create ML uygulamasını başlatabiliriz.

Create ML App Open

Açılan sayfadan, New Document’e basın.

create ml new document

Açılan pencereden Tabular Regression seçeneğini seçin ve ileriye basın.

Create ML Tabular Regression

Proje adı için BetterRest yazın ve ileriye basın, konumu seçtikten sonra oluştur tuşuna basın.

İlk olarak, Create ML’ye bazı eğitim verilerini sağlayacağız. Bu Create ML’nin bakması gereken ham istatistiklerdir ve bizim örneğimizde dört değerden oluşur: ne zaman uyanmak istediği (wake), ne kadar uyumak istediği (estimatedSleep), ne kadar kahve içtiği (coffee), gerçekte ne kadar uyuduğu (actualSleep)

Bu veriler BetterRest.csv dosyasında bulunmaktadır. Bu dosya Create ML’nin çalışabileceği veri kümesidir ve ilk işimiz bu dosyayı içeri aktarmaktır. Create ML uygulamasında bulunan Training Data sekmesinden bu içeri aktarma işlemini aşağıdaki ekran görüntüsündeki gibi yapabilirsiniz.

Create ML Choose Data

Bir sonraki işimiz bilgisayarın tahmin etmeyi öğrenmesini istediğimiz hedefi (target) ve incelenmesini istediğimiz özellikleri (Features) belirlemektir.

Bunu yapabilmek için aşağıdaki gibi hedef (Target) için actualSleep ‘ı, özellikler (Fatures) için ise wake , estimatedSleep, coffee seçin.

Create ML Target and Features

Parameters bölümünün altında, Algorithm seçebiliriz burada Automatic, Random Forest, Boosted Tree, Decision Tree ve Linear Regression bulunmaktadır. Her biri verileri analiz etmek için farklı bir yaklaşım benimsiyor. Otomatik seçeneği ise her zaman doğru değildir, ancak bu proje için oldukça yeterlidir.

Create ML Algorithm Options

Algoritmaların ne işe yaradığına dair genel bir izlenim elde etmek istiyorsanız Paul Hudson’ın YouTube videosuna göz atabilirsiniz.

Bu adımları tamamladıktan sonra sol üst köşede bulunan Train butonuna basabiliriz. Birkaç saniye sonra işlem tamamlanmış olacak.

Create ML bize hem eğitim training hem de validation istatistikleri sağlar ve her ikisi de önemlidir. Verilerimizi kullanarak training yapmasını istediğimizde, verileri otomatik olarak böldü. Bir kısmını makine öğrenimi modelini eğitmek için kullandı, ancak daha sonra doğrulama için bir parça ayırdı. Bu doğrulama verileri daha sonra modeli kontrol etmek için kullanılır. Girdiye dayalı bir tahmin yapar, ardından bu tahminin verilerden gelen gerçek değerden ne kadar uzak olduğunu kontrol eder.

Daha da iyisi, Output sekmesine giderseniz, bitmiş modelimizin 545 byte boyutuna sahip olduğunu göreceksiniz. CreateML 180Kb’lık veriyi almış ve sadece 545 byte’a indirmiştir. Bu mümkün çünkü Create ML aslında değerlerin ne olduğunu umursamıyor, sadece ilişkilerin ne olduğunu önemsiyor. Bu nedenle en iyi algoritmayı öğrenir ve bunu depolar.

Modelimiz eğitildiğine göre, Output sekmesinde sağ üst köşede bulunan Get butonu ile modelimizi kaydedebiliriz.


Bu yazıyı İngilizce olarak da okuyabilirsiniz.
You can also read this article in English.

Bu yazı, SwiftUI Day 26 adresinde bulunan yazılardan kendim için aldığım notları içermektedir. Orjinal dersi takip etmek için lütfen bağlantıya tıklayın.