SwiftUI: ScrollView

ScrollView, içine yerleştirdiğimiz içeriğe uyacak şekilde kendisini otomatik olarak boyutlandıran görünümler oluşturmamızı sağlar.

Sinan Ulusan
TurkishKit

--

Bir SwiftUI uygulamasında ScrollView, yatay ve dikey hizada kaydırılabilen elemanlar oluşturmamızı sağlayan bir arayüz elemanıdır. ScrollView elemanını UIKit “framework”ündeki UIScrollView elemanının SwiftUI versiyonu gibi düşünebilirsiniz.

Hadi o zaman ScrollView elemanını öğrenmeye başlayalım!

ScrollView Objesini Oluşturma

İlk olarak yeni bir Xcode projesi oluşturalım. Projemizi bir SwiftUI projesi olarak kaydetmeyi unutmayalım. Projemizi oluşturduktan sonra Xcode’un bizim için varsayılan olarak verdiği kodlardan Text elemanını ve padding “modifier”ını silelim. Sonrasında body içerisine ScrollView elemanını oluşturalım.

ScrollView {
VStack(spacing: 20) {
ForEach(0..<10) {
Text("Box \($0)")
.foregroundColor(.white)
.font(.largeTitle)
.frame(width: 200, height: 200)
.background(Color.blue)
}
}
}

Bu kodu yazdıktan sonra elde ettiğimiz görüntü dikey yani “vertical” bir görüntü olarak karşımıza çıkacak. Bunun sebebi ise ScrollView objemizin eğer belirtmezsek başlangıç değeri “vertical” olarak karşımıza çıkacaktır.

Projemizde oluşturduğumuz “200x200” kutuları aşağıya doğru kaydırabildiğimizi görüyoruz. Bunun yanında sayfamızı aşağıya doğru kaydırırken bir şey dikkatinizi çekebilir, kutularımızın yanında kaydırma çubuğu olarak adlandırabileceğimiz gösterge bulunmakta, gelin bu göstergeyi görünmez hale getirelim ve bu sayede daha hızlı bir sayfa akışı görelim.

Göstergeyi silmemiz için şu kodu ScrollView objesinin showIndicators parametresini false değerine eşitleyelim.

ScrollView(showsIndicators: false) {
....

“Preview” sayfasından projemizin anlık durumuna baktığımızda sonuç aşağıdaki gibi olacaktır.

ScrollView yapımızın dikey bir şekilde kullanımını gördük, gelin bir de yatay görünüm elde edebilmek için kodumuzu küçük bir dokunuşla değiştirelim.

ScrollView(.horizontal, showIndicators: false) {
HStack(spacing: 20) {
ForEach(0..<10) {
Text("Box \($0)")
.foregroundColor(.white)
.font(.largeTitle)
.frame(width: 200, height: 200)
.background(Color.blue)
}
}
}

Bir önceki kodumuzu revize ettiğimizde bu sefer de yatayda kaydırabileceğimiz bir yapı oluşturmuş olduk.

Kaydırma çubuğumuzun görünmemesini sağlamak için showIndicators parametresini false olarak tekrar girdik.

Şimdiye kadar ScrollView objesini basitçe ele aldık. Şimdi ise Apple’ın WWDC20'de tanıttığı ScrollViewReader objesini öğreneceğiz.

ScrollViewReader

Belirli bir konumdaki objeye doğru ScrollView objesini kaydırmak için ScrollViewReader objesini kullanırız. ScrollViewReader objesi, içerisinde id “modifier”ı ile tanımlanmış objelerin “id” bilgisinden yararlanarak istenilen bir objeye kaydırma işlemi yapmamızı sağlar.

Aşağıdaki kod bloğundaki gibi belli bir “id” bilgisine sahip objeleri -aşağıdaki örnekte bu Text oluyor- otomatik olarak kaydırarak kullanıcının görebileceği şekilde ekranda gösterebiliyoruz.

ScrollViewReader { scrollView in
ScrollView(showsIndicators: false) {
VStack(spacing: 20) {
Button("Aşağıya Kaydır") {
withAnimation {
scrollView.scrollTo(3, anchor: .center)
}
}

ForEach(0..<10) { index in
Text("Box \(index)")
.id(index)
.foregroundColor(.white)
.font(.largeTitle)
.frame(width: 200, height: 200)
.background(Color.blue)
}
}
}
}

Burada ne yaptık? Belirli bir konuma kaydırmamızı sağlayan ScrollViewReader yapımızın içerisine ScrollView oluşturduk. Başlangıç özelliği “vertical” olan yapımızın hizalamasını değiştirdik. İstediğimiz konuma gitmesini sağlamak için Button ekledik. scrollTo metodunu butonun içerisinde çağırarak üçüncü indeksteki elemana gidilmesi için ilk parametreye “3” yazdık ve anchor parametresini .center olarak ayarladık.

Artık kaydırma görünümü içeriğini belirli bir konuma taşıyabileceğimizi öğrendik, ancak arayüz elemanlarımızın konum bilgilerine nasıl erişebiliriz? Kullanıcı içeriği kaydırırken görünümü nasıl güncel tutabiliriz? Gelin bu soruların cevaplarını öğrenelim.

İlk önce ScrollView objemizin içerisindeki elemanların konum bilgilerini anlık olarak bilmek için PreferenceKey protokolü ile bir yapı oluşturalım. Burada CGPoint türünden alacağımız bilgileri derleyeceğiz.

struct ScrollOffsetPreferenceKey: PreferenceKey {
static var defaultValue: CGPoint = .zero

static func reduce(value: inout CGPoint, nextValue: () -> CGPoint) {}
}

Bu ScrollOffsetPreferenceKey yapısını kullanabilmek için kendi özel ScrollView objemizi oluşturalım. Burada ekstra View objeleri ekleyebilmek için de generic bir View yapısı tanımlıyoruz: “Content”. init metodunda kendisini @ViewBuilder “property wrapper”ı ile tanımlıyoruz, bu sayede birden fazla View objesini içerisinde barındırabilecek.

struct OffsetScrollView<Content: View>: View {
let axes: Axis.Set
let showsIndicators: Bool
let offsetChanged: (CGPoint) -> Void
let content: Content

init(
axes: Axis.Set = .vertical,
showsIndicators: Bool = true,
offsetChanged: @escaping (CGPoint) -> Void = { _ in },
@ViewBuilder content: () -> Content
) {
self.axes = axes
self.showsIndicators = showsIndicators
self.offsetChanged = offsetChanged
self.content = content()
}
}

Artık OffsetScrollView objesini alınan özellikler ile oluşturabiliriz. Görebileceğiniz üzere, onPreferenceChange “modifier”ında oluşturmuş olduğumuz SrollOffsetPreferenceKey elemanını kullanıyoruz.

struct OffsetScrollView<Content: View>: View {
/ ...
var body: some View {
OffsetScrollView(axes, showsIndicators: showsIndicators) {
GeometryReader { geometry in
Color.clear.preference(
key: ScrollOffsetPreferenceKey.self,
value: geometry.frame(in: .named("scrollView")).origin
)
}.frame(width: 0, height: 0)
content
}
.coordinateSpace(name: "scrollView")
.onPreferenceChange(ScrollOffsetPreferenceKey.self, perform: offsetChanged)
}
}

Şimdi sıra bu oluşturduğumuz ` objesini ana View objemizde kullanmaya geldi.

ScrollView objesine benzer şekilde bir OffsetScrollView objesi tanımlayabiliyoruz, sadece tanımlamamız gereken üç tane parametremiz var.

struct ContentView: View {
var body: some View {
OffsetScrollView(
axes: [.horizontal, .vertical],
showsIndicators: false,
offsetChanged: { print($0) }
) {
ForEach(0..<100) { i in
Text("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.")
}
}
}
}

Sonuç olarak, parametre değerlerimizi de ContentView içerisine ekledik ve döngümüzü yazmış olduk. Uygulamamızın son hali aşağıdaki gibidir. 👇🏻

Bu yazımızda ScrollView objesini nasıl kullanabileceğimizi öğrendik. Umarım sizler için yararlı bir yazı olmuştur, bir sonraki yazımızda görüşmek üzere! 🤙🏻

Bu yazımızda yapmış olduğumuz projemize aşağıdaki linkten ulaşabilirsiniz:

Bizi daha yakından takip etmek istiyorsanız, sosyal medya hesaplarımıza aşağıdan ulaşabilirsiniz!

Twitter | Instagram | Facebook

--

--