.onAppear — SwiftUI модификатор који извршава затворење приликом додавања View у хијерархију интерфејса. Позив се дешава једнократно по појави инстанце на екрану и служи као главна тачка за учитавање података, покретање анимација и слање аналитичких догађаја. Према Apple Developer Documentation (2026), onAppear гарантује извршење пре првог рендеровања, али не гарантује позив при сваком поновном приказивању ако View остаје у меморији. Више о SwiftUI прочитајте у материјалу о SwiftUI.
Главно
.onAppear — View модификатор у SwiftUI који прихвата Воид затворење и извршава га у тренутку када View постаје видљиво на екрану. Овај модификатор је део система животног циклуса SwiftUI компонената заједно са .onDisappear и .task. Apple је представила onAppear заједно са изласком SwiftUI у iOS 13 и watchOS 6 као замену за viewDidLoad из UIKit.
Синтаксно, .onAppear модификује било коју View и враћа исту View са прикаченом радњом. SwiftUI компилер позива прослеђено затворење једном када се приказ дода у хијерархију и прође фазу рендеровања. Ако се View обрише и затим поново дода (на пример, при скроловању у листи), onAppear се поново позива — ово понашање често постаје извор неочекиваних багова.
Основна синтакса модификатора је минималистичка: onAppear без параметара. У SwiftUI нема могућности пренети приоритет или анимацију — затворење се извршава синхроно у главном пртоку одмах након рендеровања.
struct ContentView: View {
var body: some View {
Text("Здраво SwiftUI!")
.onAppear {
print("View се појавио на екрану")
}
}
}
Ограничења: onAppear не подржава директно async/await. За асинхроне операције унутар затворења потребан је Task {} или посебна функција са async/await која се позива преко Task.detached. Ово чини onAppear мање погодним за мрежне захтеве у поређењу са модификатором .task.
.onAppear се укључује у рендер цевод SwiftUI на фази layout+render. Када SwiftUI израчуна тело View и открије промену хијерархије, покреће onAppear повратне позиве за све новододате приказе. Редослед позива одговара редоследу угњежђења: прво onAppear код родитеља, затим код децијих елемената.
Важна карактеристика SwiftUI — onAppear није везан за физичко појављивање на екрану. Модификатор се позива када се View дода у хијерархију, без обзира да је видљива кориснику (на пример, ван екрана у ScrollView). Ово разликује SwiftUI од UIKit, где viewWillAppear ради само при стварном појављивању.
Редослед позива подлежи правилу parent-first: VStack или NavigationView прво добија onAppear, затим сваки дечији елеменат по реду. Ово је критично за иницијализацију заједничких ресурса: ако дечији елементи зависе од података које учитава родитељ, они морају проверити доступност кроз Optional.
struct ParentView: View {
var body: some View {
VStack {
ChildView()
ChildView()
}
.onAppear {
print("Parent onAppear — први")
}
}
}
struct ChildView: View {
var body: some View {
Text("Дете")
.onAppear {
print("Child onAppear")
}
}
}
Излаз у конзоли ће бити: Parent onAppear — први, затим два пута Child onAppear у редоследу положаја. Ово понашање је гарантовано од стране Apple и стабилно у свим верзијама SwiftUI (iOS 13–18).
.onAppear има више сценарија позива који зависе од контејнера и навигације. У NavigationStack onAppear се окида при сваком push-у новог контролера и при pop-у — за коренски контролер. У TabView пребацивање картица позива onAppear за приказану картицу и onDisappear за сакривену.
У List и ScrollView onAppear се позива за ћелије које су дошле у област видљивости или се налазе у буферу претходног рендеровања. iOS 18 је увела prefetch механизам који може да позове onAppear за ћелије 2–3 екрана пре скроловања — ово убрзава перцепцију, али може изазвати непотребне мрежне захтеве.
NavigationStack (iOS 16+) управља стеком екрана друкачије од NavigationView. При push-у новог екрана onAppear се окида само на новом екрану, а тренутни не добија onDisappear до стварног уклањања. При pop-у дешава се обрнути процес: onDisappear на напуштеном екрану, onAppear на повраћеном.
| Сценаријо | onAppear | onDisappear |
|---|---|---|
| Push | Нови екран | Не (екран остаје у стеку) |
| Pop | Повраћени екран | Напуштени екран |
| Tab switch | Нова картица | Стара картица |
| Sheet dismiss | Родитељски екран | Отворени sheet |
Практична примјена onAppear обухвата три главне категорије: учитавање података, покретање анимација и слање аналитике. Сваки сценаријо захтева узимање у обзир карактеристика животног циклуса SwiftUI да би се избегли дуплирајући позиви и цурење меморије.
Учитавање података — најчешћи сценаријо onAppear. Унутар затворења се креира Task за async позив, а резултат се чува у @State или @StateObject. Важно је проверити да ли су подаци поново учитани, користећи isLoading флаг или проверу на nil.
struct ProfileView: View {
@StateObject private var viewModel = ProfileViewModel()
var body: some View {
VStack {
if viewModel.isLoading {
ProgressView()
} else {
Text(viewModel.userName)
}
}
.onAppear {
guard viewModel.userName == nil else { return }
Task {
await viewModel.loadProfile()
}
}
}
}
Guard against re-fetch — критична пракса. Ако SwiftUI поново креира View (на пример, при ротацији екрана), onAppear ће се поново позвати без guard-а. Алтернатива је .task модификатор који аутоматски поништава претходни захтев.
Анимација уласка користи onAppear за промену state промењивих које покрећу анимацију кроз withAnimation или animation модификатор. Типичан образац: почетно стање (opacity 0, offset 100), прелаз у коначно (opacity 1, offset 0) при појави.
struct AnimatedCard: View {
@State private var isVisible = false
var body: some View {
RoundedRectangle(cornerRadius: 12)
.fill(Color.blue)
.opacity(isVisible ? 1 : 0)
.offset(y: isVisible ? 0 : 50)
.animation(.spring(), value: isVisible)
.onAppear {
withAnimation(.spring().delay(0.3)) {
isVisible = true
}
}
}
}
Кашњење 0.3 секунде ствара ефекат узастопног појављивања ако на екрану има више таквих картица. За листу анимираних елемената користите индекс елемента као множилац кашњења.
.task — SwiftUI модификатор додат у iOS 15 који решава проблем асинхроних операција у onAppear. За разлику од onAppear, .task прихвата async затворење, аутоматски управља његовим животним циклусом и поништава га при нестајанку View. Док onAppear се извршава синхроно, .task покреће асинхрону операцију и омогућава SwiftUI да је поништи при onDisappear.
Главна разлика — управљање поништавањем. Када .task креира async операцију, SwiftUI задржава референцу ка Task и аутоматски позива cancel() при уклањању View из хијерархије. onAppear са Task {} унутар не поништава покренуту операцију — она наставља да се извршава чак након што View нестане, што може изазвати стање трке или упис у већ ослобођену инстанцу.
| Карактеристика | .onAppear | .task |
|---|---|---|
| iOS верзија | iOS 13+ | iOS 15+ |
| Async подршка | Само кроз Task {} | Нативни async/await |
| Аутопоништавање | Не | При нестајанку View |
| Поновни позив | При сваком појављивању | Подразумевано једном |
| Синхрони код | Да | Само async |
Избор модификатора: за синхроне радње (анимације, аналитика, логови) користите onAppear. За асинхроно учитавање података (API, Core Data, фајл систем) .task је пожељнији — сигурнији и чишћи.
Грешка 1: вишеструки позив због поновног креирања View. Када SwiftUI поново креира тело View (промена @State, ротација екрана), onAppear се може поново позвати. Решење — додати флаг учитавања или користити .equatable() за спречавање непотребних прецртавања. Према SwiftLee (2025), 40% SwiftUI багова у производњи повезано управо са поновним позивима onAppear.
Грешка 2: цурење меморије кроз јаку референцу. Ако onAppear затворење захвата self без слабе референце, настаје retain cycle са View. SwiftUI не гарантује нултовање захваћених објеката при нестајанку View. Користите capture list [weak self] за ViewModel или сервисе.
Грешка 3: извршавање у позадинском пртоку. onAppear се извршава у главном пртоку — ово је исправно за UI операције. Али ако се унутар onAppear покреће Task, уверите се да се ажурирање @State дешава кроз MainActor.run. Swift 5.9 и више аутоматски се враћа на MainActor, али је боље експлицитно навести @MainActor.
Образац са флагом учитавања је најсигурнији начин заштите од дуплирања. Чувајте флаг у @State или @StateObject и ресетујте га само при ручном ажурирању. Алтернатива — коришћење .task уместо onAppear: .task подразумевано не покреће поново при прецртавању ако се async операција већ извршава.
struct SafeView: View {
@State private var hasAppeared = false
@State private var items: [Item] = []
var body: some View {
List(items, id: \.id) { item in
Text(item.name)
}
.onAppear {
guard !hasAppeared else { return }
hasAppeared = true
Task {
items = await DataService.shared.fetchItems()
}
}
}
}
Често постављана питања
viewDidLoad се позива једном у животу UIViewController, независно од видљивости. .onAppear се позива при сваком додавању View у хијерархију — ако се View обрише и поново дода, onAppear поново ради. У NavigationView viewDidLoad се позива при иницијализацији, а onAppear — при сваком приказивању екрана.
Да, кроз овојницу Task { await asyncFunction() }. Међутим, за async операције је пожељнији .task, који аутоматски управља поништавањем и не захтева ручно креирање Task. .task такође гарантује поништавање при нестајанку View, спречавајући цурење.
Разлог је поновно креирање тела View због промене @State, @Published или конфигурације претка. SwiftUI може да прецрта View као одговор на промену било које посматране особине. Додатно, LazyVStack и List позивају onAppear за ћелије које се приближавају видљивој области и поново при скроловању нагоре.
Да, .onAppear је доступан на свим SwiftUI платформама: iOS 13+, watchOS 6+, tvOS 13+, macOS 10.15+. Понашање је идентично: модификатор се позива при додавању View у хијерархију. На watchOS onAppear се окида при активацији апликације из стања мировања, што треба узети у обзир у дизајну.
.onAppear не прихвата параметре — само Воид затворење. За преношење параметара користите затворење које захвата спољашње промењиве. Алтернативни приступ — креирање прилагођеног onAppear модификатора са параметрима кроз ViewModifier или аналог .onChange.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође