.onAppear هو مُعدِّل SwiftUI ينفذ إغلاقاً عند إضافة View إلى تسلسل الواجهة. يحدث الاستدعاء مرة واحدة لكل ظهور للمثيل على الشاشة ويعمل كنقطة رئيسية لتحميل البيانات، تشغيل الرسوم المتحركة، وإرسال أحداث التحليلات. وفقاً Apple Developer Documentation (2026)، يضمن onAppear التنفيذ قبل أول عرض، لكنه لا يضمن الاستدعاء عند كل عرض متكرر إذا بقيت View في الذاكرة. اقرأ المزيد عن SwiftUI في مقال SwiftUI.
أهم النقاط
.onAppear هو مُعدِّل View في SwiftUI يأخذ إغلاقاً Void وينفذه عندما تصبح View مرئية على الشاشة. هذا المُعدِّل جزء من نظام دورة حياة مكونات SwiftUI إلى جانب .onDisappear و .task. قدمت Apple onAppear مع إصدار SwiftUI في iOS 13 و watchOS 6 كبديل لـ viewDidLoad من UIKit.
من الناحية التركيبية، يُعدِّل .onAppear أي View ويعيد نفس View مع إجراء مرفق. يستدعي مُركِّب SwiftUI الإغلاق المُمرَّر مرة واحدة عندما تُضاف view إلى التسلسل وتجتاز مرحلة العرض. إذا حُذفت View ثم أُضيفت مرة أخرى (مثلاً عند التمرير في قائمة)، يُستدعى onAppear مرة أخرى — غالباً ما يصبح هذا السلوك مصدر أخطاء غير متوقعة.
بناء المُعدِّل الأساسي بسيط: onAppear بدون معاملات. لا يوفر SwiftUI طريقة لتمرير أولوية أو رسم متحرك — يُنفَّذ الإغلاق بشكل متزامن في الخيط الرئيسي فوراً بعد العرض.
struct ContentView: View {
var body: some View {
Text("Hello, SwiftUI!")
.onAppear {
print("View appeared on screen")
}
}
}
القيود: لا يدعم onAppear async/await مباشرة. للعمليات غير المتزامنة داخل الإغلاق تحتاج إلى Task {} أو دالة async/await منفصلة تُستدعى عبر Task.detached. هذا يجعل onAppear أقل ملاءمة لطلبات الشبكة مقارنة بالمُعدِّل .task.
.onAppear يُدمج في خط أنابيب العرض لـ SwiftUI في مرحلة layout+render. عندما يحسب Swiftui جسم View ويكتشف تغييراً في التسلسل، يُشغِّل استدعاءات onAppear لجميع views المضافة حديثاً. ترتيب الاستدعاء يتبع التداخل: onAppear للأب أولاً، ثم العناصر الابن.
ميزة مهمة لـ SwiftUI هي أن onAppear غير مرتبط بالظهور الفعلي على الشاشة. يُستدعى المُعدِّل عندما تُضاف View إلى التسلسل بغض النظر عما إذا كانت مرئية للمستخدم (مثلاً خارج الشاشة في ScrollView). هذا يميز SwiftUI عن UIKit، حيث viewWillAppear يُشغَّل فقط عند الظهور الفعلي.
ترتيب الاستدعاء يتبع قاعدة الأب أولاً: VStack أو NavigationView يستقبل onAppear أولاً، ثم كل عنصر ابن بالترتيب. هذا مهم لتهيئة الموارد المشتركة: إذا كانت العناصر الابن تعتمد على بيانات يُحمّلها الأب، فيجب أن تتحقق من التوفّر عبر Optional.
struct ParentView: View {
var body: some View {
VStack {
ChildView()
ChildView()
}
.onAppear {
print("Parent onAppear — first")
}
}
}
struct ChildView: View {
var body: some View {
Text("Child")
.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 | الشاشة العائدة | الشاشة المُغادَرة |
| تبديل تبويب | التبويب الجديد | التبويب القديم |
| إغلاق sheet | الشاشة الأم | الشاشة المفتوحة |
التطبيقات العملية لـ onAppear تغطي ثلاث فئات رئيسية: تحميل البيانات، تشغيل الرسوم المتحركة، وإرسال التحليلات. كل سيناريو يتطلب مراعاة ميزات دورة حياة SwiftUI لتجنب الاستدعاءات المكررة وتسريبات الذاكرة.
تحميل البيانات هو السيناريو الأكثر شيوعاً لـ onAppear. داخل الإغلاق يُنشأ Task للاستدعاء async، ويُخزَّن النتيجة في @State أو @StateObject. من المهم التحقق مما إذا كانت البيانات قد حُمِّلت بالفعل باستخدام flag 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()
}
}
}
}
الحماية من إعادة الجلب ممارسة مهمة. إذا أعاد SwiftUI إنشاء View (مثلاً عند دوران الشاشة)، سيُستدعى onAppear مرة أخرى بدون حماية. البديل هو المُعدِّل .task الذي يُلغي تلقائياً الطلب السابق.
الرسم المتحرك للدخول يستخدم onAppear لتغيير متغيرات الحالة التي تُشغِّل الرسم المتحرك عبر 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 (تغيير حالة، دوران شاشة)، قد يُستدعى onAppear مرة أخرى. الحل — إضافة flag تحميل أو استخدام .equatable() لمنع إعادة الرسم غير الضرورية. وفقاً SwiftLee (2025)، 40% من أخطاء SwiftUI في الإنتاج مرتبطة باستدعاءات onAppear المتكررة.
الخطأ 2: تسريب ذاكرة عبر مرجع قوي. إذا التقط إغلاق onAppear self بدون مرجع ضعيف، يُنشئ دورة احتجاز مع View. لا يضمن SwiftUI إلغاء الكائنات الملتقطة عند اختفاء View. استخدم capture list [weak self] لـ ViewModel أو الخدمات.
الخطأ 3: التنفيذ في خيط خلفية. يُنفَّذ onAppear في الخيط الرئيسي — هذا صحيح لعمليات UI. لكن إذا أطلقت Task داخل onAppear، تأكد من أن تحديث @State يحدث عبر MainActor.run. Swift 5.9 وما فوق يعود تلقائياً إلى MainActor، لكن من الأفضل تحديد @MainActor صراحةً.
النمط مع flag تحميل هو الطريقة الأكثر موثوقية للحماية من الازدواجية. خزّن flag في @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 لا يقبل معاملات — فقط إغلاق Void. لتمرير معاملات، استخدم إغلاقاً يلتقط متغيرات خارجية. نهج بديل هو إنشاء مُعدِّل onAppear مخصص مع معاملات عبر ViewModifier أو ما يعادل .onChange.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.