Ders 04 / 14
Erişilebilirlik
Erişilebilirlik, bir uygulamanın ekran okuyucu, büyük yazı ve dokunma gibi farklı kullanım biçimlerinde de işini görmesini sağlar. Ders ekran okuyucu açıldığında bir hareketin menüye düşerek kısıtlı çalıştığını ölçer.
Yayın Güncelleme
Bu derste
- Ekran okuyucu, anlamsal özellikler, dokunma hedefi ve dinamik yazı boyutunu tanımlar
- Erişilebilirlik ayarlarının bir özelliğin yeteneklerini nasıl değiştirdiğini açıklar
- Yedeği olan ve olmayan özel bir hareketin ekran okuyucuda nasıl sınıflandığını karşılaştırır
- Görünüm ağacı değişmese de bir özelliğin kısıtlıya düşebildiğini ölçer
İçindekiler
Önceki derste yeteneği bir yapılandırma değeri belirledi. Bu derste yeteneği kullanıcının kendi ayarı belirler. Bir kullanıcı ekran okuyucuyu açmıştır, bir başkası yazı boyutunu büyütmüştür, üçüncüsü dokunmak yerine bir anahtar aygıtıyla gezinir. Bunlar nadir bir kenar durum değildir; uygulamanın gerçek koşullarının bir parçasıdır. Erişilebilirlik, uygulamanın bu koşullarda da işini görmesini sağlayan tasarım ve uygulama pratiğidir. Ders önce erişilebilirliğin parçalarını anlatır, sonra ekran okuyucu açıkken bir hareketin ne kadar çalıştığını ölçer.
Ekran Okuyucu ve Anlamsal Özellikler
Ekran okuyucu, ekrandaki öğeleri sesli ya da dokunsal olarak aktaran, platformun sunduğu yardımcı teknolojidir. Kullanıcı ekrana bakmaz; öğeler arasında gezinir ve ekran okuyucu her öğenin ne olduğunu, adını ve yapılabilecek eylemleri söyler. Ekran okuyucu bu bilgiyi görüntüden çıkarmaz. Uygulamanın verdiği anlamsal özelliklerden okur: bir öğenin etiketi, rolü, durumu ve eylemleri.
Anlamsal özellikler görünümden ayrı bir bilgi katmanıdır. Görünüm bir kartı bir resim ve iki metin olarak çizer; anlamsal özellikler aynı kartı “Sipariş kartı, başlık şu, silmek için eylem var” diye tanımlar. Bir öğenin anlamsal özelliği yoksa ekran okuyucu onu ya atlar ya da anlamsız bir ad okur. Bu yüzden etiketsiz bir düğme, görünüşte tam olsa da ekran okuyucu kullanıcısı için yoktur.
Aşağıdaki iki parça aynı kart satırının anlamsal özelliklerini iki platformda gösterir. İkisi de kartın başlığını etiket olarak verir ve silme eylemini bir hareket yerine adlandırılmış bir eylem olarak sunar. Parçalar ölçülmez.
// Platform kodu: iOS erisilebilirlik eylemi
struct CardRow: View {
let card: Card
var body: some View {
Text(card.title)
.accessibilityLabel(card.title)
.accessibilityAction(named: "Sil") { remove(card) }
}
}
// Platform kodu: Android erisilebilirlik eylemi
@Composable
fun CardRow(card: Card) {
Text(
text = card.title,
modifier = Modifier.semantics {
contentDescription = card.title
customActions = listOf(CustomAccessibilityAction("Sil") { remove(card); true })
}
)
}
Aynı fikir iki platformda iki farklı API ile söylenir. Bu, çapraz platform uygulamasının erişilebilirlikte de paylaşılan bir düşünce ile platforma özgü bir katmana ayrıldığının göstergesidir: ne söylenecek ortaktır, nasıl söyleneceği platformundur.
Dokunma Hedefi ve Dinamik Yazı Boyutu
Dokunma hedefi, bir öğenin parmakla dokunulabilir alanıdır. Küçük bir hedef, motor becerisi sınırlı bir kullanıcı için ve hareket halindeki herhangi bir kullanıcı için yanlış dokunmaya yol açar. Platform yönergeleri hedefler için en küçük bir boyut önerir; bu boyut platformdan platforma değişir ve ders bir sayı vermez. Önemli olan, görsel öğenin küçük çizilse bile dokunma alanının yönergeye uygun tutulabilmesidir.
Dinamik yazı boyutu, kullanıcının sistem ayarından yazı boyutunu büyütmesidir. Uygulama bu ayara uyar: metin büyür ve metnin içinde durduğu düzen buna yer açmak zorundadır. Sabit genişlikte ve sabit yükseklikte çizilmiş bir bileşen büyük yazıda metni keser ya da taşırır. Düzen bu yüzden metne uyum sağlayacak biçimde, esnek tasarlanır. Önceki ders bir bileşenin uyarlanmasını anlattı; dinamik yazı boyutu, uyarlamanın kullanıcının kendi ayarından da geldiğini gösterir.
Her iki ayar da özelliğin ihtiyaç duyduğu bir yeteneği ortadan kaldırabilir. Ekran okuyucu açıkken serbest bir kaydırma hareketi güvenilir biçimde yapılamayabilir, çünkü ekran okuyucu hareketlerin çoğunu kendi gezinmesi için ayırır. Büyük yazıda sabit genişlikli bir tablo satıra sığmaz. Model bu iki düşüşü iki yetenek olarak kurar.
Ayardan Koşula
Model erişilebilirlik ayarlarını bir koşula çevirir. conditionsFor(name, settings) üç yetenekle başlar: free-gesture serbest hareket yeteneğidir, fixed-width sabit genişlikli düzen yeteneğidir, semantics anlamsal özelliklerin var olmasıdır. Ekran okuyucu açıksa free-gesture yetenekten düşer; büyük yazı açıksa fixed-width düşer. Dersin çekirdeği aşağıda yeniden verilir.
// operability.mjs — bir ozelligin bir kosulda ne kadar calistigini siniflandirir export const LEVELS = ["tam", "kisitli", "ertelenen", "kapali"]; const FALLBACKS = ["kisitli", "ertelenen"]; export function feature(name, needs, fallback = {}) { return { name, needs, fallback }; } export function condition(name, has) { return { name, has }; } function judge(item, cond) { const missing = item.needs.filter((need) => !cond.has.includes(need)).sort(); let level = "tam"; for (const need of missing) { const given = FALLBACKS.includes(item.fallback[need]) ? item.fallback[need] : "kapali"; if (LEVELS.indexOf(given) > LEVELS.indexOf(level)) level = given; } return { level, feature: item.name, condition: cond.name, missing }; } export function assess(features, conditions) { const records = []; for (const cond of conditions) { for (const item of features) records.push(judge(item, cond)); } return records; } export function printLevels(records) { for (const r of records) { const missing = r.missing.length === 0 ? "-" : r.missing.join(","); console.log(`${r.level}: ${r.feature} @ ${r.condition} eksik=${missing}`); } } export function countLevels(records) { for (const name of LEVELS) { console.log(`${name}: ${records.filter((r) => r.level === name).length}`); } console.log(`etkilenen: ${records.filter((r) => r.level !== "tam").length}`); }
Ayarları koşula çeviren yardımcı ders-yerel bir dosyadır. İçinde görünüm ağacındaki düğümleri döndüren bir işlev de vardır; bu işlev ayara bakmaz.
// settings.mjs — erisilebilirlik ayarlarindan kosul ve dugum listesi uretir import { condition } from "./operability.mjs"; export function conditionsFor(name, settings = {}) { const has = ["free-gesture", "fixed-width", "semantics"]; const kept = has.filter((item) => { if (item === "free-gesture") return !settings.screenReader; if (item === "fixed-width") return !settings.largeText; return true; }); return condition(name, kept); } export function nodesFor(settings = {}) { return ["Card", "Label", "Actions"]; }
İlk ölçüm işlerliği değil, üç ayarın bıraktığı yetenekleri basar.
// main.mjs import { conditionsFor } from "./settings.mjs"; const conditions = [ conditionsFor("default"), conditionsFor("screen-reader", { screenReader: true }), conditionsFor("large-text", { largeText: true }), ]; for (const item of conditions) { console.log(`${item.name}: ${[...item.has].sort().join(",")}`); }
default: fixed-width,free-gesture,semantics screen-reader: fixed-width,semantics large-text: free-gesture,semantics
Her ayar bir yeteneği düşürür ve semantics hiçbirinde düşmez. Anlamsal özellikler uygulamanın kendi sorumluluğudur; ayar onları kaldırmaz.
PM7. Ekran okuyucunun serbest hareketi nasıl ele aldığı platformdan ve ayardan ayrıntılara göre değişir. Model bu ayrıntıyı iki duruma indirger: hareket ya yapılabilir ya yapılamaz. Büyük yazıda sabit düzenin nerede taştığı da modelde yoktur; yalnız taşıp taşmadığı bir yetenek olarak kurulur. Bu, işlerliğin kuralını gösterir, gerçek bir ekran okuyucuyu sınamanın yerini tutmaz.
Üç Koşulda İki Özellik
İkinci ölçüm iki özelliği üç ayarda sınar. CardSwipe kartı kaydırarak silen harekettir ve free-gesture ister; yedeği eylem menüsüdür, bu yüzden eksik olduğunda kısıtlı düşer. PriceTable sabit genişlikli bir fiyat tablosudur ve fixed-width ister; yedeği tek sütunlu düzendir.
// main.mjs import { feature, assess, printLevels } from "./operability.mjs"; import { conditionsFor } from "./settings.mjs"; const features = [ feature("CardSwipe", ["free-gesture"], { "free-gesture": "kisitli" }), feature("PriceTable", ["fixed-width"], { "fixed-width": "kisitli" }), ]; const conditions = [ conditionsFor("default"), conditionsFor("screen-reader", { screenReader: true }), conditionsFor("large-text", { largeText: true }), ]; printLevels(assess(features, conditions));
tam: CardSwipe @ default eksik=- tam: PriceTable @ default eksik=- kisitli: CardSwipe @ screen-reader eksik=free-gesture tam: PriceTable @ screen-reader eksik=- tam: CardSwipe @ large-text eksik=- kisitli: PriceTable @ large-text eksik=fixed-width
Her ayar yalnız ilgili özelliği etkiler. Ekran okuyucu CardSwipe’ı kısıtlıya düşürür ve PriceTable’a dokunmaz; büyük yazı ise tersini yapar. Kısıtlı kayıt çalışmayan bir özellik değildir: kart eylem menüsüyle silinebilir, tablo tek sütunda okunabilir.
PM8. Bu iki yedeğin eylem menüsü ve tek sütunlu düzen olduğu dersin kurgusudur. Yedeğin gerçekte erişilebilir olup olmadığı, yani menünün ekran okuyucuyla gerçekten kullanılabilir çıkıp çıkmadığı ayrıca sınanır; model yalnız yedeğin tanımlı olduğunu söyler.
Sınırlayıcı Ölçüm
Dersin odak kaydı CardSwipe @ screen-reader’dır. Ekran okuyucu açıldığında free-gesture yetenekten düşer ve kayıt kısıtlıya iner. Bu ölçümde görünüm ağacının düğümleri de basılır ve karşılaştırmaya, yedeği olmayan özel bir hareket olan CustomGesture eklenir.
// main.mjs import { feature, assess, printLevels, countLevels } from "./operability.mjs"; import { conditionsFor, nodesFor } from "./settings.mjs"; console.log(`dugum: ${nodesFor({ screenReader: false }).join(",")}`); console.log(`dugum: ${nodesFor({ screenReader: true }).join(",")}`); const features = [ feature("CardSwipe", ["free-gesture"], { "free-gesture": "kisitli" }), feature("CustomGesture", ["free-gesture"]), ]; const conditions = [ conditionsFor("default"), conditionsFor("screen-reader", { screenReader: true }), ]; const records = assess(features, conditions); printLevels(records); countLevels(records);
dugum: Card,Label,Actions dugum: Card,Label,Actions tam: CardSwipe @ default eksik=- tam: CustomGesture @ default eksik=- kisitli: CardSwipe @ screen-reader eksik=free-gesture kapali: CustomGesture @ screen-reader eksik=free-gesture tam: 2 kisitli: 1 ertelenen: 0 kapali: 1 etkilenen: 2
Düğüm listesi iki ayarda aynıdır: Card, Label ve Actions. Ekran okuyucu açıldığında görünüm ağacında hiçbir düğüm değişmedi, ama CardSwipe tamdan kısıtlıya düştü. CustomGesture ise yedeği olmadığı için kapalıya düştü; yedeksiz özel hareket ekran okuyucu kullanıcısı için hiç çalışmaz.
Bu sonuç M32/K02’nin sorusundan farklıdır. O ders bir güncellemenin görünüm ağacındaki düğüme etkisini sordu: hangi düğüm eklendi, değişti, kaldırıldı. Burada hiçbir düğüm değişmedi ve özellik yine de düştü. İşlerlik ağacın biçimini değil, koşulda yeteneğin var olup olmadığını sınıflar. Ağaç sabitken yetenek değişebilir, çünkü yetenek ağacın değil, kullanıcının ayarının işidir.
Etkilenen sayısı ikidir ve açılış ölçümünün dört sayısından farklıdır; iki ölçüm farklı kümeleri sayar.
Özet
- Bu dersin işlerliği: kısıtlı.
CardSwipe @ screen-readerkaydında ekran okuyucu açıkkenfree-gestureyeteneği düşer ve özellik eylem menüsüyle çalışmayı sürdürür. - Ekran okuyucu açıldığında görünüm ağacındaki düğümler aynı kaldı; özellik düğümlerden bağımsız olarak kısıtlıya düştü.
- Yedeği olmayan
CustomGestureaynı koşulda kapalı çıktı; sayımda tam 2, kısıtlı 1, ertelenen 0, kapalı 1 ve etkilenen 2 görüldü. - Büyük yazı yalnız
fixed-widthyeteneğini düşürdü;PriceTablekısıtlıya düştü,CardSwipetam kaldı. - Anlamsal özellikler hiçbir ayarda düşmedi; ekran okuyucu etiketi, rolü ve eylemleri bu katmandan okur.
Sonraki Adım
Bu derste yetenek kullanıcının ayarından düştü ve özellik bir yedekle kısıtlı kaldı. Sıradaki konunun ilk dersi, Çevrimdışı Öncelikli Tasarım, yeteneğin ağdan düştüğü durumu ele alır: uygulama yerel veriyle çalışmayı sürdürür ve kısıtlı bir akış tam bir akışın yerini alır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.