@Binding — چیست، چگونه کار می‌کند و مثال‌هایی در SwiftUI

نویسنده: IT Sectr منتشر شده: 2026-06-19 زمان مطالعه: 7 دقیقه

@Binding یک Property Wrapper در SwiftUI است که ارجاعی به داده‌های متعلق به مؤلفه دیگر ایجاد می‌کند. Binding مقدار را به طور مستقل ذخیره نمی‌کند — فقط از طریق پروجکشن $ به منبع حقیقت موجود دسترسی فراهم می‌کند. طبق Apple Developer Documentation (2025)، Binding یک ارتباط دوطرفه واکنشی بین نمای والد و فرزند بدون مالکیت مستقیم داده‌ها فراهم می‌کند. @Binding مکانیسم کلیدی برای انتقال حالت تغییرپذیر به پایین سلسله‌مراتب است.

نکات اصلی

  • @Binding — Property Wrapper برای ایجاد ارجاع به حالت نمای والد
  • بدون مالکیت — Binding داده ذخیره نمی‌کند، فقط به منبع حقیقت دسترسی می‌دهد
  • پروژکشن $ — $stateValue از @State یا @StateObject Binding می‌سازد
  • ارتباط دوطرفه — تغییرات در نمای فرزند بلافاصله در والد منعکس می‌شود
  • Binding.constant — مقدار ثابت برای نمونه‌سازی اولیه بدون بازخورد

@Binding در SwiftUI چیست؟

@Binding یک Property Wrapper است که یک ارتباط دوطرفه بین ویژگی ذخیره‌شده در نمای والد و مؤلفه فرزند ایجاد می‌کند. تفاوت اصلی Binding با @State: Binding صاحب داده نیست. آن فقط مقدار را از طریق منبع واقعی — @State، @StateObject یا Binding دیگر در والد — می‌خواند و می‌نویسد. بدون Binding، نماهای فرزند نمی‌توانستند حالت اجداد را بدون callback یا delegate تغییر دهند.

Binding به عنوان یک ساختار با دو ویژگی پیاده‌سازی شده است: wrappedValue (مقدار فعلی) و projectedValue (خود Binding که از طریق $ قابل دسترسی است). وقتی نمای فرزند wrappedValue را از طریق Binding تغییر می‌دهد، SwiftUI تغییر را به منبع داده منتقل کرده و همه نماهای وابسته را دوباره ترسیم می‌کند. این به صورت همزمان در چرخه به‌روزرسانی فعلی رخ می‌دهد.

ویژگی مهم: @Binding به انتقال تک‌سطحی محدود نمی‌شود. Binding را می‌توان از طریق چندین سطح سلسله‌مراتب منتقل کرد — هر مؤلفه فرزند به یک منبع داده یکسان ارجاع دریافت می‌کند. تغییر در هر سطحی باعث به‌روزرسانی واحد همه نماهای مرتبط می‌شود.

ارتباط دوطرفه Binding چگونه کار می‌کند

مکانیسم ارتباط دوطرفه از طریق @Binding بر روی پروجکشن Property Wrapperها ساخته شده است. وقتی والد @State var value: T را اعلان می‌کند، SwiftUI به طور خودکار پروجکشن $value از نوع Binding را تولید می‌کند. با انتقال $value به مؤلفه فرزند با @Binding var value: T، هر دو نما را به یک خانه حافظه متصل می‌کنید. هر نوشتن از طریق Binding در نمای فرزند باعث ترسیم دوباره هر دو مؤلفه می‌شود.

swift
struct SliderContainer: View {
    @State private var value: Double = 0.5

    var body: some View {
        VStack {
            Text("مقدار: \(value)")
            SliderView(value: $value)
        }
    }
}

struct SliderView: View {
    @Binding var value: Double

    var body: some View {
        Slider(value: $value, in: 0...1)
    }
}

در مثال SliderContainer صاحب @State value است و SliderView Binding را از طریق $value دریافت می‌کند. Slider داخل SliderView به این Binding متصل است. هنگام کشیدن لغزنده، Slider مقدار را از طریق Binding تغییر می‌دهد که به طور خودکار @State را در SliderContainer به‌روزرسانی کرده و هر دو نما عدد فعلی را نمایش می‌دهند. کل زنجیره بدون هیچ callback یا اعلانی کار می‌کند.

برای ایجاد Binding از @StateObject یا @ObservedObject از همان پروجکشن استفاده می‌شود: $object.property یک Binding می‌دهد. این امکان انتقال ویژگی‌های جداگانه ObservableObject به نماهای فرزند بدون انتقال کل شیء را فراهم می‌کند. چنین رویکردی اتصال محدودتری داده و از ترسیم‌های مجدد غیرضروری جلوگیری می‌کند.

@Binding در مقابل callbackها: کدام را انتخاب کنیم

قبل از SwiftUI روش استاندارد انتقال تغییرات به بالای سلسله‌مراتب callbackها و delegateها بود: والد یک closure ارسال می‌کرد، مؤلفه فرزند آن را هنگام تغییر فراخوانی می‌کرد. @Binding جایگزینی با کد کمتر و نحو اعلانی‌تر ارائه می‌دهد. به جای ارسال closure تکمیلی، کافی است $stateValue را ارسال کنید.

معیار@BindingCallbackها
کدیک حاشیه‌نویس + $Closure + فراخوانی
چندسطحیخودکارزنجیره closureها
تستBinding(value:constant)Mock-closureها
خواناییبالامتوسط
انعطاف‌پذیریفقط دادههر منطقی

زمانی از @Binding استفاده کنید که نمای فرزند فقط نیاز به خواندن و تغییر مقدار دارد. اگر انجام اقدامات جانبی در زمان تغییر (اعتبارسنجی، ثبت رویداد، درخواست شبکه) مورد نیاز است، Binding را با callback ترکیب کنید: Binding را برای داده و closure را برای رویدادها ارسال کنید. برای مثال، TextField می‌تواند به Binding متصل شود و onChange اعتبارسنجی را اجرا کند.

الگوهای استفاده از @Binding در پروژه‌ها

@Binding در چند سناریوی معمولی استفاده می‌شود. اول — کنترل‌های سفارشی: سوئیچ‌ها، لغزنده‌ها، انتخابگرهای رنگ و سایر عناصر تعاملی برای همگام‌سازی دوطرفه Binding دریافت می‌کنند. دوم — پنجره‌های مودال: پرچم نمایش sheet به عنوان Binding منتقل می‌شود و به نمای فرزند اجازه می‌دهد خود را از طریق presentationMode یا تنظیم مستقیم ببندد.

الگوی سوم — فرم‌های تقسیم‌شده. اگر فرم از فیلدهای متعددی تشکیل شده باشد، هر فیلد را می‌توان به یک مؤلفه جداگانه منتقل کرد که Binding را برای مقدار خود دریافت می‌کند. این کار تست و استفاده مجدد فیلدها را بین فرم‌های مختلف ساده می‌کند. مؤلفه والد تنها صاحب کل مدل فرم باقی می‌ماند.

swift
struct FormField: View {
    let title: String
    @Binding var text: String

    var body: some View {
        VStack(alignment: .leading) {
            Text(title).font(.caption)
            TextField("Enter \(title.lowercased())", text: $text)
                .textFieldStyle(.roundedBorder)
        }
    }
}

مؤلفه FormField عنوان و Binding به یک رشته را دریافت می‌کند. آن برچسب و TextField متصل به Binding منتقل‌شده را نمایش می‌دهد. هر فرمی می‌تواند با ارسال $property برای هر فیلد، چندین بار از FormField استفاده کند. این کار تکرار نشانه‌گذاری را کاهش داده و استایل‌دهی فیلدهای متنی را متمرکز می‌کند.

ایجاد Binding سفارشی

SwiftUI امکان ایجاد دستی Binding را از طریق مقداردهنده Binding(get:set:) فراهم می‌کند. این زمانی مفید است که نیاز به افزودن منطق هنگام خواندن یا نوشتن مقدار دارید. برای مثال، می‌توان Bindingای ایجاد کرد که عدد را قبل از ذخیره فرمت می‌کند، یا Bindingای که مقدار را با هر تغییر با سرور راه دور همگام می‌کند.

swift
struct ValidatedField: View {
    @State private var email: String = ""

    var emailBinding: Binding<String> {
        .init(
            get: { email },
            set: { email = $0.lowercased().trimmingCharacters(in: .whitespaces) }
        )
    }

    var body: some View {
        TextField("Email", text: emailBinding)
    }
}

در لیستینگ، emailBinding سفارشی به طور خودکار متن را به حروف کوچک تبدیل کرده و فاصله‌ها را در هر تغییر حذف می‌کند. TextField به جای اتصال مستقیم به $email از این Binding استفاده می‌کند. چنین رویکردی امکان متمرکز کردن اعتبارسنجی و تبدیل داده‌ها در داخل Binding را بدون آلوده کردن کد با کنترل‌کننده‌های onChange فراهم می‌کند.

خطاها و ضدالگوهای @Binding

اولین و رایج‌ترین خطا — انتقال مقدار به جای Binding. اگر مؤلفه فرزند @Binding var text: String را اعلان کند و والد text (بدون $) را ارسال کند، کامپایلر خطا می‌دهد: Cannot convert value of type 'String' to expected argument type 'Binding'. راه‌حل — همیشه در زمان ارسال از پیشوند $ استفاده کنید: $text.

دومین خطا — Binding روی داده‌های فقط خواندنی. اگر نمای فرزند فقط نیاز به خواندن مقدار دارد، از @Binding استفاده نکنید — let ساده یا @State از والد کافی است. Binding امکان نوشتن را فرض می‌کند و حقوق اضافی تغییر، اشکال‌زدایی را پیچیده کرده و اصل حداقل دسترسی را نقض می‌کند.

سومین مشکل — Binding.constant در تولید. Binding.constant(value) یک اتصال ساختگی بدون بازخورد ایجاد می‌کند — تغییرات نادیده گرفته می‌شوند. constant را فقط برای نمونه‌سازی اولیه و پیش‌نمایش‌ها (Xcode Previews) استفاده کنید، اما هرگز در کد واقعی. برای تست‌ها از Binding(get:set:) با رفتار کنترل‌شده استفاده کنید.

سوالات متداول

تفاوت بین @Binding و @State چیست؟

@State مالک داده‌هاست و ذخیره‌سازی آن‌ها را در heap مدیریت می‌کند. @Binding فقط به حالت موجود بدون مالکیت ارجاع می‌دهد. @State همیشه private است، @Binding — پارامتر ورودی نمای فرزند.

آیا می‌توان بدون @State Binding ایجاد کرد؟

بله، از طریق مقداردهنده Binding(get:set:) یا Binding.constant(value). همچنین Binding را می‌توان از @StateObject از طریق پروجکشن $object.$property و از Publisher از طریق Binding(get:set:) داخل Subscribe دریافت کرد.

چگونه Binding را از طریق چند سطح تو در تو منتقل کنیم؟

@Binding در طول زنجیره منتقل می‌شود: هر مؤلفه میانی @Binding را اعلان کرده و آن را از طریق $ به پایین منتقل می‌کند. همه سطوح به یک منبع داده در نمای ریشه ارجاع می‌دهند.

چرا Binding.constant رابط را به‌روز نمی‌کند؟

Binding.constant یک پوشش بی‌صدا ایجاد می‌کند — setter مقادیر جدید را نادیده می‌گیرد. این فقط برای نمونه‌سازی اولیه و SwiftUI Previews طراحی شده است، جایی که بازخورد از مؤلفه فرزند مورد نیاز نیست.

آیا @Binding می‌تواند اختیاری باشد؟

بله، Binding<T?> پشتیبانی می‌شود. اگر Binding<String?> را ارسال کنید، نمای فرزند می‌تواند nil تنظیم کند. این برای فیلدهای اختیاری فرم یا حالت‌های با امکان بازنشانی مفید است.

خلاصه

  • @Binding — Property Wrapper برای ارتباط دوطرفه با داده‌های نمای والد
  • مالک نیست — فقط به منبع حقیقت دسترسی می‌دهد
  • پروژکشن $ @State و @StateObject را برای انتقال به نماهای فرزند به Binding تبدیل می‌کند
  • Binding سفارشی از طریق Binding(get:set:) با منطق اضافی ایجاد می‌شود
  • Binding.constant — فقط برای پیش‌نمایش‌ها و نمونه‌های اولیه
  • انتقال چندسطحی — Binding از هر عمق تو در تویی عبور می‌کند
  • جایگزین callbackها — روش اعلانی تغییر حالت از مؤلفه‌های فرزند

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید