Mac uygulamanızdaki bir listeyi yeniden sıralamak için SwiftUI’nin onMove kaynak ve hedef değerlerini doğrudan move(fromOffsets:toOffset:) yöntemine aktarın. Hedef, özgün koleksiyondaki bir sınırı belirtir; bu nedenle aşağı yöndeki taşımalar için elle bir çıkarmak yanlış sonuç verir.
Bu anlatımda sentetik etiketler kullanan küçük bir yerel macOS listesi oluşturuluyor, hedef indeksin hesabı açıklanıyor ve yanlış konumlanan satırları teşhis etmek için bir test matrisi sunuluyor. Kapsam, öğelerin kendi uygulamanızın listesi içinde yeniden sıralanmasıdır.
onMove hedefi ne anlama gelir?
Apple, iki onMove(perform:) bağımsız değişkenini dinamik görünümün temelindeki koleksiyona göre belirlenen ofsetler olarak tanımlar. İlk bağımsız değişken, kaynak ofsetlerini içeren bir IndexSet; ikincisi ise hedef ofsetidir. Bu konumlar, ilgili ForEach tarafından kullanılan koleksiyona aittir. Apple’ın onMove(perform:) belgeleri.
move(fromOffsets:toOffset:) için Apple, hedefi taşınan öğelerin önüne ekleneceği ofset olarak tanımlar; count değeri de koleksiyonun sonu anlamına gelebilir. Bunu, özgün koleksiyondaki öğeler arasındaki bir sınır olarak düşünün. Apple’ın move(fromOffsets:toOffset:) belgeleri.
Özgün sıralamanın A, B, C, D, E olduğunu varsayalım. Öğe ofsetleri 0, 1, 2, 3, 4 şeklindeyken ekleme sınırları 0 ile 5 arasında değişir. 4 sınırı, E öğesinin hemen önündedir; 5 sınırı ise son öğeden sonradır.
B öğesini 1 kaynak ofsetinden 4 hedefine taşımak, bu nedenle A, C, D, B, E sonucunu verir. Taşınan öğe 3 indeksinde biter; hedef bağımsız değişkeni 4 olsa da. Her iki değer de sıfır tabanlı konumları kullanır; ancak işlemin farklı aşamalarını tanımlar.
Bu ayrım, aşağı yöndeki taşımalarda sık yapılan hatayı açıklar. Koleksiyonun taşıma yöntemini çağırmadan önce hedefi 4 değerinden 3 değerine değiştiren kod, farklı bir sınır ister ve B öğesini D öğesinin önüne yerleştirir. Apple’ın taşıma yöntemini kullanırken geri çağırım değerlerini değiştirmeden aktarın.
Yeniden sıralanabilir eksiksiz bir macOS listesi oluşturma
Xcode’da yerel bir macOS SwiftUI uygulama projesi kullanın. Aşağıdaki eksiksiz örnek, SwiftUI App yaşam döngüsünü kullandığı için macOS 11 veya üstünü hedefler; Apple, onMove öğesinin kendisini macOS 10.15’ten itibaren kullanılabilir olarak listeler. Oluşturulan uygulama ve içerik görünümü bildirimlerini bu kodla değiştirin ve hedefte yalnızca bir @main giriş noktası bırakın. Apple’ın App belgeleri, Apple’ın onMove(perform:) belgeleri.
import SwiftUI
import Foundation
struct DemoItem: Identifiable {
let id: Int
let label: String
static let samples: [DemoItem] = [
DemoItem(id: 0, label: "A"),
DemoItem(id: 1, label: "B"),
DemoItem(id: 2, label: "C"),
DemoItem(id: 3, label: "D"),
DemoItem(id: 4, label: "E")
]
}
@main
struct ReorderDemoApp: App {
var body: some Scene {
WindowGroup {
ReorderView()
}
}
}
struct ReorderView: View {
@State private var items = DemoItem.samples
@State private var selection = Set<Int>()
@State private var lastMove = "No move yet"
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text("Drag rows to change their order")
List(selection: $selection) {
ForEach(items) { item in
Text(item.label)
.tag(item.id)
}
.onMove(perform: moveItems)
}
Text("Order: " + items.map(\.label).joined(separator: ", "))
Text(lastMove)
Button("Reset") {
items = DemoItem.samples
selection.removeAll()
lastMove = "No move yet"
}
}
.padding()
.frame(minWidth: 420, minHeight: 300)
}
private func moveItems(from source: IndexSet, to destination: Int) {
lastMove = "Source: \(Array(source)); destination: \(destination)"
items.move(fromOffsets: source, toOffset: destination)
}
}
ForEach, işleyicinin değiştirdiği dizinin aynısını görüntüler. Her öğenin, geçerli konumundan bağımsız sabit bir tanımlayıcısı vardır. Apple, ForEach için tanımlanabilir öğeler veya açık bir kimlik anahtarı gerektiğini belirtir; taşıma sırasında her tanımlayıcının korunması, görünümün aynı öğeyi temsil etmeyi sürdürmesini sağlar. Apple’ın ForEach belgeleri.
Dizi, görünümün yerel durumudur. Dizi değiştiğinde SwiftUI, liste ve görüntülenen sıralama dahil olmak üzere ona bağlı görünümleri günceller. Bu örnek sıralamayı bellekte tutar ve kalıcılık uygulaması içermez. Apple’ın State belgeleri.
Uygulamayı çalıştırın, B öğesini E öğesinin hemen önündeki sınıra sürükleyin ve hem sıralamayı hem de geri çağırım görüntüsünü inceleyin. Başka bir senaryoyu denemeden önce Reset düğmesini kullanın. Açık seçim bağlaması birden fazla satırın seçilmesini de destekler; Apple, seçim kümelerini ve Mac’te düzenleme moduna geçmeden seçim yapmayı belgeler. Apple’ın List belgeleri.
Bu yerel macOS örneğine EditButton() eklemeyin. Apple’ın güncel kullanılabilirlik bilgileri iOS ve Mac Catalyst’i kapsar, ancak yerel macOS’u kapsamaz. Bir öğreticideki mobil düzenleme denetimlerini, kullanılabilirliklerini kontrol etmeden Mac hedefine kopyalamayın. Apple’ın EditButton belgeleri.
Nihai ekleme indeksini nasıl hesaplarsınız?
Bu hesaplamaya yalnızca kaldırma ve ekleme işlemlerini kendiniz uygularken veya taşımayı başka bir model işlemine dönüştürürken ihtiyaç duyarsınız. Bu, Apple’ın belgelenmiş ekleme anlambiliminden türetilmiş açıklayıcı bir modeldir; yerleşik taşıma yöntemini çağırmadan önce uygulanacak ek bir düzeltme değildir.
Hesaplama şöyledir:
kaldırma sonrası ekleme indeksi = özgün hedef
- hedeften önceki kaynak ofsetlerinin sayısı
Kaynak {1} ve hedef 4 olduğunda, kaldırılan öğelerden biri sınırın özgün konumunda önündedir. Küçültülmüş dizi A, C, D, E olur ve ekleme indeksi 4 - 1 = 3 değeridir. B öğesi buraya eklendiğinde A, C, D, B, E sonucu elde edilir.
Birden fazla kaynak olduğunda tek bir aşağı yön düzeltmesi yeterli olmaz. Kaynaklar {1, 3} ve hedef 5 olduğunda taşınan öğeler B, D olur; geriye kalan dizi A, C, E şeklindedir. İki kaynak ofseti 5 hedefinden önce geldiğinden ekleme indeksi 3 olur. Sonuç A, C, E, B, D şeklindedir.
Özel bir uygulama, önce taşınan öğeleri özgün sıralarında yakalamalı, ardından daha önce yapılan kaldırmaların sonraki kaynak konumlarını geçersiz kılmasına izin vermeden özgün öğeleri kaldırmalıdır. Ardından düzeltilmiş ekleme konumunu hesaplayıp yakalanan bloğu tek seferde ekleyebilir. Dizi tabanlı bu liste için Apple’ın mevcut yöntemi, bu adımları kendiniz uygulama gereğini ortadan kaldırır.
Hangi uç durumları test etmelisiniz?
Aşağıdaki durumlar, özgün sıralamanın A, B, C, D, E olduğu varsayımıyla hazırlanmış örnek hesaplamalardır. Her durumdan önce bu sıralamayı geri yükleyin. Son sütun, bırakma göstergesinin nasıl göründüğünden bağımsız olarak beklenen model sonucunu verir.
| Kaynak ofsetleri | Özgün hedef | Kaldırma sonrası ekleme indeksi | Beklenen sıralama |
|---|---|---|---|
{1} |
4 |
3 |
A, C, D, B, E |
{3} |
1 |
1 |
A, D, B, C, E |
{1} |
5 |
4 |
A, C, D, E, B |
{1} |
1 |
1 |
A, B, C, D, E |
{1} |
2 |
1 |
A, B, C, D, E |
{1, 3} |
5 |
3 |
A, C, E, B, D |
{1, 3} |
3 |
2 |
A, C, B, D, E |
Tek öğenin yerini değiştirmediği iki durum önemlidir: bir öğeyi kendi sınırına veya ondan sonraki sınıra eklemek sıralamasını değiştirmez. Apple bu davranışı açıkça belgeler. Son sınır da geçerlidir; ancak bir öğeye erişmek için indis olarak kullanılamaz. Apple’ın move(fromOffsets:toOffset:) belgeleri.
Bu yedi durumun temelindeki hesaplama modeli, küçük ve deterministik bir Python hesabıyla kontrol edildi. SwiftUI programı incelendi, ancak Swift araç zinciri ve macOS çalışma zamanı burada mevcut olmadığından derlenip çalıştırılmadı. Gerçek sürükleme etkileşimini hedef macOS sürümünüzde doğrulayın.
Satırlar neden yine de yanlış taşınabilir?
Koleksiyon sınırıyla başlayın, ardından kimliği ve değişikliği kontrol edin. Bu sıra, hedef indeks hesaplaması sorunlarını görüntülenen satırlarla saklanan veriler arasındaki uyumsuzluklardan ayırır.
ForEach’e aktarılan koleksiyonu kontrol edin. Filtrelenmiş bir dizi görüntülüyorsa ofsetleri bu filtrelenmiş diziye göredir. Örneğin, görünürB, Dsatırlarının ofsetleri0, 1şeklindedir;0kaynağını dizinin tamamına uygulamakAöğesini taşır. Bu sonuç, Apple’ın belgelenmiş ve koleksiyona göre belirlenen geri çağırım sözleşmesinden kaynaklanır. Apple’ınonMove(perform:)belgeleri.- Görüntüleme sırasında verilerin yeniden sıralanıp sıralanmadığını kontrol edin. Her yeniden çizimde etiketlere göre sıralama yapılıyorsa, arka plandaki dizinin sırasını değiştirmek kalıcı bir manuel görüntüleme sırası oluşturamaz. Bu görünümde yeniden sıralamayı etkinleştirmeden önce manuel sıralama politikasını belirleyin.
- Tanımlayıcıları kontrol edin. Taşıma sırasında her öğenin tanımlayıcısını koruyun. Görüntüleme sırasında tanımlayıcıları yeniden oluşturmayın ve taşınabilir öğelerin kimliği olarak değişen konumları kullanmayın. Apple’ın
ForEachbelgeleri. - İşlemin birden fazla kez uygulanmadığını kontrol edin. Hedefi elle azaltmayı kaldırın ve işleyicinin taşımayı yetkili koleksiyona bir kez uyguladığını doğrulayın. İkinci bir taşıma, zaten değişmiş bir sıralamaya uygulanır.
Filtrelenmiş veya bölümlere ayrılmış arayüzlerde indeksleri dönüştürmeden önce amaçlanan davranışı tanımlayın. Taşımanın yalnızca görünür öğeleri mi yeniden sıralayacağına, tüm koleksiyonu mu değiştireceğine, yoksa bir öğeyi bölümler arasında mı aktaracağına karar verin. Görünür öğeleri model kimlikleriyle eşleştirin ve hedefi bu politikaya göre belirleyin. Tek bir sayısal ofset bu seçeneklerin tümünü ifade edemez.
Geri çağırım ofsetlerini, tanımladıkları koleksiyon anlık görüntüsüne bağlı tutun. Uygulama, dizi başka düzenlemelerle değiştikten sonra işlemi erteliyorsa eski konumları yeniden kullanmak yerine amaçlanan öğeleri ve hedefi tekrar belirleyin. Örnek, yerel diziyi hemen değiştirerek bu karmaşıklığı önler.
AppKit veya TextKit hedef kurallarını değiştirir mi?
Zaten NSTableView kullanan bir uygulamada AppKit’in kendi bırakma sözleşmesi vardır. NSTableView.DropOperation.above, bir satırın üstüne ekleme anlamına gelir; .on ise öğenin o satırın üzerine bırakılmasını ifade eder. Apple, son satırın altına bırakmayı satır n ve .above işlemi olarak belgeler; burada n satır sayısıdır. Bir AppKit bırakma işlemini model taşımasına dönüştürmeden önce hem satırı hem de işlemi yorumlayın. Apple’ın NSTableView.DropOperation belgeleri.
TextKit, NSTextView ve metin düzeni yöneticileri gibi bileşenler aracılığıyla metin depolama, düzen ve düzenleme işlevlerini ele alır. Bu örnekteki dizi yeniden sıralama sözleşmesini sağlamaz. Bir satırda düzenleyici de varsa, metin düzenleme uygulamasını satır sırasını değiştiren koleksiyon işleminden ayrı tutun. Apple’ın TextKit belgeleri.
Basit bir Mac listesi için manuel olarak sıralanmış tek bir koleksiyon kullanın, öğe tanımlayıcılarını koruyun ve onMove bağımsız değişkenlerini doğrudan koleksiyonun taşıma yöntemine aktarın. Sonucu teşhis etmeniz veya işlemi başka bir modele dönüştürmeniz gerektiğinde sınır hesabını ve test matrisini kullanın.
Kaynaklar
- onMove(perform:) — Apple
- move(fromOffsets:toOffset:) — Apple
- App — Apple
- ForEach — Apple
- State — Apple
- List — Apple
- EditButton — Apple
- NSTableView.DropOperation — Apple
- TextKit — Apple