---
title: 'Sekme ve Araç Çubuğu'
source: 'https://academia.sh/tr/kurslar/swiftui/sekme-ve-arac-cubugu'
course: 'SwiftUI ile Arayüz'
language: tr
updated: '2026-10-21T05:03:14+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Sekme seçiminin kökte tutulan yerel bir durum olduğunu ölçüyle gösterir'
    - 'Seçimi bağlamayla alıp yalnız yazan sekme içeriğinin okuyan sayılmadığını ayırır'
    - 'Araç çubuğu düğmesinin etkinliğini bir nesne özelliğine bağlamanın gözlem tetiği ürettiğini ölçer'
    - 'Seçili olmayan sekmenin seçimi okumadığı sürece atlandığını açıklar'
---

# Sekme ve Araç Çubuğu

Sekme seçiminin kökte durduğunun, seçimi bağlamayla yalnız yazan içeriğin okuyan sayılmadığının ve seçili olmayan sekmenin seçimi okumadığı için atlandığının ölçümü.

Sekme çubuğu ve araç çubuğu, ekranın üstünde ve altında duran iki ayrı kabuktur. Birincisi hangi bölümün göründüğünü, ikincisi o bölümün hangi eylemleri sunduğunu söyler. İkisi de bir değere dayanır. Sekme çubuğunda bu **seçim** (selection), kökte tutulan bir değerdir; araç çubuğunda ise düğmenin etkin olup olmadığı genellikle bir nesnenin özelliğinden gelir. Bu derste bakılan tetik **yerel**: seçim kökte durur ve onu kök okur; sekme içerikleri seçimi bağlamayla alsa da gövdelerinde okumaz.

Ölçüm çekirdek dosyayla yapılır.

```swift
// Invalidation.swift — bir degisikligin hangi gorunumu hangi kaynak yoluyla yeniden hesaplattigini siniflandirir
enum Storage {
    case state(owner: String)
    case model(object: String)
    case environment
}

struct ViewNode {
    let name: String
    let parent: String?
    let reads: Set<String>

    init(_ name: String, parent: String?, reads: Set<String>) {
        self.name = name
        self.parent = parent
        self.reads = reads
    }
}

struct Recompute {
    let view: String
    let cause: String
}

struct ViewTree {
    let nodes: [ViewNode]
    let storage: [String: Storage]

    func ancestors(of name: String) -> [String] {
        var found: [String] = []
        var current = nodes.first { $0.name == name }?.parent
        while let next = current {
            found.append(next)
            current = nodes.first { $0.name == next }?.parent
        }
        return found
    }

    func cause(of node: ViewNode, key: String) -> String {
        guard let kind = storage[key] else { return "olcum-disi" }
        switch kind {
        case .environment:
            return "ortam"
        case .model:
            return "gozlem"
        case .state(let owner):
            if owner == node.name { return "yerel" }
            if ancestors(of: node.name).contains(owner) { return "ebeveyn" }
            return "kural-disi"
        }
    }

    func change(_ label: String, key: String) -> [Recompute] {
        let hits = nodes.filter { $0.reads.contains(key) }
            .map { Recompute(view: $0.name, cause: cause(of: $0, key: key)) }
        let skipped = nodes.filter { !$0.reads.contains(key) }.map(\.name)
        let recomputed = hits.isEmpty ? "yok" : hits.map { "\($0.view) \($0.cause)" }.joined(separator: ", ")
        let rest = skipped.isEmpty ? "yok" : skipped.joined(separator: ",")
        print("\(label): \(key) degisti | yeniden \(recomputed) | atlanan \(rest)")
        return hits
    }
}

func countCauses(_ recomputes: [Recompute]) {
    for cause in ["yerel", "ebeveyn", "gozlem", "ortam"] {
        print("\(cause): \(recomputes.filter { $0.cause == cause }.count)")
    }
}
```

Çekirdek, seçimi `state` anahtarı, düğmenin etkinliğini taşıyan nesne özelliğini `model` anahtarı olarak yazar.

## Seçim Kökte Durur

Bir `TabView` seçimi bir bağlama olarak alır. Her sekmenin içeriği `.tag` ile bir değer taşır; çerçeve seçim değeriyle etiketi eşleştirip hangi içeriği göstereceğine karar verir. Aşağıdaki kaynak parçada kök, seçimin sahibidir ve içeriklere onu bağlama olarak iletir.

```swift
import SwiftUI

struct ShellScreen: View {
    @State private var selectedTab = 0

    var body: some View {
        TabView(selection: $selectedTab) {
            InboxTab(selectedTab: $selectedTab)
                .tag(0)
            ArchiveTab()
                .tag(1)
        }
    }
}

struct InboxTab: View {
    @Binding var selectedTab: Int

    var body: some View {
        Button("Arsive git") { selectedTab = 1 }
    }
}

struct ArchiveTab: View {
    var body: some View {
        Text("Arsiv")
    }
}
```

Seçim `ShellScreen`'dedir. `InboxTab` seçimi bağlamayla alır, çünkü bir düğmeyle başka sekmeye geçebilmesi gerekir; ama değeri gövdesinde okumaz, yalnız düğmenin eyleminde yazar. İkinci dersin kuralı burada da geçerlidir: yalnız yazan görünüm okuyanlar arasına girmez. `ArchiveTab` seçimi hiç almaz. Önce kök ve iki sekmeyle bir seçim değişikliği ölçülür: gelen kutusundan arşive, arşivden geri.

```swift
// main.swift — sekme secimi kokte durur, secimi okumayan sekme atlanir
let storage: [String: Storage] = [
    "selectedTab": .state(owner: "ShellScreen"),
]

let screen = ViewTree(nodes: [
    ViewNode("ShellScreen", parent: nil, reads: ["selectedTab"]),
    ViewNode("InboxTab", parent: "ShellScreen", reads: []),
    ViewNode("ArchiveTab", parent: "ShellScreen", reads: []),
], storage: storage)

let recomputes = [
    screen.change("arsive-gecildi", key: "selectedTab"),
    screen.change("gelene-donuldu", key: "selectedTab"),
].flatMap { $0 }

countCauses(recomputes)
```

```
arsive-gecildi: selectedTab degisti | yeniden ShellScreen yerel | atlanan InboxTab,ArchiveTab
gelene-donuldu: selectedTab degisti | yeniden ShellScreen yerel | atlanan InboxTab,ArchiveTab
yerel: 2
ebeveyn: 0
gozlem: 0
ortam: 0
```

İki değişiklik, iki yeniden hesaplama; her satırda yalnız kök yerel, iki sekme atlanan. Sayım yerel iki.

İki sekme de seçim değerini okumuyor; bu yüzden hangi sekmenin göründüğünü bilmeleri gerekmez. Çerçeve seçimi etiketlerle eşleştirir, içeriklerin kendisi bu eşleştirmeye katılmaz. Bir sekmenin içeriği, seçimi gövdesinde okumadığı sürece atlanır. `InboxTab` bağlamayı taşısa da yalnız yazdığı için atlandı; seçimi gövdede okuyan bir içerik olsaydı ebeveyn tetiği alırdı, bu ağaçta öyle bir içerik yok.

**DR19.** Bu ölçüm iki değişiklik ve iki sekme için. Çerçevenin seçili olmayan sekmenin içeriğini hiç kurmayıp kurmadığı ya da bellekte tutup tutmadığı ölçülmedi; ölçüm yalnız hangi görünümün yeniden hesaplandığını sayar.

## Araç Çubuğu Düğmesi Nesneden Okur

Araç çubuğundaki bir düğmenin etkin olup olmadığı, bölümün gösterdiği nesneye bağlıdır: gelen kutusunda seçili ileti varsa "Sil" düğmesi etkindir. Düğme `.toolbar` içinde tanımlanır ve etkinliğini nesne özelliğinden alır. Bu bir nesne özelliğidir; ikinci dersin bağlamasıyla değil, üçüncü dersin gözlem tetiğiyle ilgilidir.

```swift
import SwiftUI
import Observation

@Observable
final class MailboxStore {
    var hasSelectedMessage = false
}

struct InboxToolbar: ToolbarContent {
    let store: MailboxStore

    var body: some ToolbarContent {
        ToolbarItem {
            Button("Sil") { }
                .disabled(!store.hasSelectedMessage)
        }
    }
}
```

Ölçümde kök, gelen kutusu sekmesi, araç çubuğu düğmesi ve arşiv sekmesi vardır. Nesne özelliğini yalnız araç çubuğu düğmesi okur; seçimi ise yalnız kök okur.

```swift
// main.swift — arac cubugu dugmesinin etkinligi nesne ozelligine baglidir
let storage: [String: Storage] = [
    "selectedTab": .state(owner: "ShellScreen"),
    "hasSelectedMessage": .model(object: "MailboxStore"),
]

let screen = ViewTree(nodes: [
    ViewNode("ShellScreen", parent: nil, reads: ["selectedTab"]),
    ViewNode("InboxTab", parent: "ShellScreen", reads: []),
    ViewNode("DeleteButton", parent: "InboxTab", reads: ["hasSelectedMessage"]),
    ViewNode("ArchiveTab", parent: "ShellScreen", reads: []),
], storage: storage)

let recomputes = [
    screen.change("ileti-secildi", key: "hasSelectedMessage"),
    screen.change("arsive-gecildi", key: "selectedTab"),
].flatMap { $0 }

countCauses(recomputes)
```

```
ileti-secildi: hasSelectedMessage degisti | yeniden DeleteButton gozlem | atlanan ShellScreen,InboxTab,ArchiveTab
arsive-gecildi: selectedTab degisti | yeniden ShellScreen yerel | atlanan InboxTab,DeleteButton,ArchiveTab
yerel: 1
ebeveyn: 0
gozlem: 1
ortam: 0
```

`ileti-secildi` yalnız araç çubuğu düğmesini yeniden hesaplattı ve gözlem: kök, gelen kutusu ve arşiv atlandı. `arsive-gecildi` ise yalnız kökü yerel yeniden hesaplattı; gelen kutusu, düğme ve arşiv seçimi okumuyor, atlandı. Sayım yerel bir, gözlem bir.

İki değişikliğin kümeleri ayrık: biri düğmeye, öbürü yalnız köke dokunuyor. Bu, durumun nerede durduğunun ölçülebilir sonucudur: seçim kökte olduğu için kök de yeniden hesaplanır; etkinlik bir nesnede olduğu için kök hiç dokunulmaz.

**DR20.** Bu ölçüm iki anahtar ve tek bir araç çubuğu düğmesi için. Araç çubuğunun yerleşiminin sekme değişince değişmesi ve düğmenin birden çok sekmede ortak olduğu durum ölçülmedi.

## Sınırlayıcı Ölçüm

Seçilen sekme değişince önceki sekmenin içeriği ekrandan çıkar, ama bu onun bir yeniden hesaplama aldığı anlamına gelmez. Bir görünümün yeniden hesaplanması için okuduğu değerin değişmesi gerekir. Seçili olmayan sekme seçimi okumuyorsa, seçim her değiştiğinde atlanır. Ölçüm bunu üç sekmeyle gösterir: ilki seçimi bağlamayla alır ama yalnız yazar, öbür ikisi seçimi hiç almaz.

```swift
// main.swift — secili olmayan sekmenin icerigi secimi okumadikca atlanir
let storage: [String: Storage] = [
    "selectedTab": .state(owner: "ShellScreen"),
]

let screen = ViewTree(nodes: [
    ViewNode("ShellScreen", parent: nil, reads: ["selectedTab"]),
    ViewNode("InboxTab", parent: "ShellScreen", reads: []),
    ViewNode("ArchiveTab", parent: "ShellScreen", reads: []),
    ViewNode("SettingsTab", parent: "ShellScreen", reads: []),
], storage: storage)

let recomputes = [
    screen.change("ayarlara-gecildi", key: "selectedTab"),
].flatMap { $0 }

countCauses(recomputes)
```

```
ayarlara-gecildi: selectedTab degisti | yeniden ShellScreen yerel | atlanan InboxTab,ArchiveTab,SettingsTab
yerel: 1
ebeveyn: 0
gozlem: 0
ortam: 0
```

Üç sekmenin üçü de, hem önceki hem sonraki seçimden bağımsız olarak atlandı; bağlamayı taşıyan gelen kutusu sekmesi de bunlara dahil. Yeniden hesaplanan yalnız kök. Sayım yerel bir.

Tasarım sonucu buradan çıkar: bu modelde yeniden hesaplamayı belirleyen, bağlamanın verilmesi değil değerin gövdede okunmasıdır. Seçimi yalnız yazan bir içeriğe bağlama vermek hesaplamayı kökün dışına taşımaz; içerik seçimi gövdesinde okumaya başladığı anda ebeveyn tetiği alır. Bağlamanın kendisinin çerçevede bir maliyeti olup olmadığı ölçülmedi.

**DR21.** Bu ölçüm tek bir seçim değişikliği ve üç sekme için. Seçili olmayan sekmelerin ekrandan çıkarken çerçevenin yaptığı iş, örneğin görünüm durumunun korunması, ölçülmedi.

## Özet

- Sekme seçimi kökte tutulan bir durumdur; seçim değişince kök yerel tetik alır.
- Seçimi bağlamayla alıp yalnız yazan sekme içeriği okuyan sayılmaz ve atlanır; seçimi gövdede okuyan içerik ebeveyn tetiği alırdı.
- Araç çubuğu düğmesinin etkinliği bir nesne özelliğinden geliyorsa gözlem tetiği üretir ve köke dokunmaz.
- Seçili olmayan bir sekme, seçimi okumadığı sürece her seçim değişikliğinde atlanır.
- Bu dersin tetiği: **yerel**.

## Sonraki Adım

Bu konu durumun nerede durduğunu, kimi yeniden hesaplattığını ve ekranlar arasında nasıl taşındığını ölçtü. Sıradaki konu parmağın kendisine bakar: bir sürükleme sırasında ofset değeri her karede değişir ve bu değişiklik de aynı zincirin bir halkasıdır. İlk ders, Hareketler, sürükleme ofsetinin hangi görünümü yeniden hesaplattığını ölçer.
