SwiftUI: @Binding değişkenleriyle özel bir init nasıl uygulanır?


95

Bir para giriş ekranı initüzerinde çalışıyorum ve başlatılan miktara göre bir durum değişkeni ayarlamak için bir özel uygulamaya ihtiyacım var .

Bunun işe yarayacağını düşündüm, ancak şu derleyici hatası alıyorum:

Cannot assign value of type 'Binding<Double>' to type 'Double'

struct AmountView : View {
    @Binding var amount: Double

    @State var includeDecimal = false

    init(amount: Binding<Double>) {
        self.amount = amount
        self.includeDecimal = round(amount)-amount > 0
    }
    ...
}

Yanıtlar:


152

Argh! Çok yakındın. Bunu nasıl yapıyorsun. Bir dolar işaretini (beta 3) veya alt çizgiyi (beta 4) ve miktar mülkünüzün önünde kendinizi veya miktar parametresinden sonraki .value'yu kaçırdınız. Tüm bu seçenekler çalışır:

@StateİncludeDecimal 'i kaldırdığımı göreceksiniz , sondaki açıklamayı kontrol edin.

Bu mülkü kullanmaktır (önüne kendini koyun):

struct AmountView : View {
    @Binding var amount: Double

    private var includeDecimal = false

    init(amount: Binding<Double>) {

        // self.$amount = amount // beta 3
        self._amount = amount // beta 4

        self.includeDecimal = round(self.amount)-self.amount > 0
    }
}

veya sonra .value kullanarak (ancak self olmadan, çünkü yapının özelliğini değil, iletilen parametreyi kullanıyorsunuz):

struct AmountView : View {
    @Binding var amount: Double

    private var includeDecimal = false

    init(amount: Binding<Double>) {
        // self.$amount = amount // beta 3
        self._amount = amount // beta 4

        self.includeDecimal = round(amount.value)-amount.value > 0
    }
}

Bu aynıdır, ancak parametre (withAmount) ve özellik (miktar) için farklı isimler kullanıyoruz, böylece her birini ne zaman kullandığınızı açıkça görebilirsiniz.

struct AmountView : View {
    @Binding var amount: Double

    private var includeDecimal = false

    init(withAmount: Binding<Double>) {
        // self.$amount = withAmount // beta 3
        self._amount = withAmount // beta 4

        self.includeDecimal = round(self.amount)-self.amount > 0
    }
}
struct AmountView : View {
    @Binding var amount: Double

    private var includeDecimal = false

    init(withAmount: Binding<Double>) {
        // self.$amount = withAmount // beta 3
        self._amount = withAmount // beta 4

        self.includeDecimal = round(withAmount.value)-withAmount.value > 0
    }
}

.Value değerini gereksiz kılan erişimcileri oluşturan özellik sarmalayıcı (@Binding) sayesinde, özellikte .value gerekli değildir. Ancak, parametre ile böyle bir şey yoktur ve bunu açıkça yapmanız gerekir. Mülk sarmalayıcılar hakkında daha fazla bilgi edinmek isterseniz WWDC oturumu 415 - Modern Swift API Tasarımı'na bakın ve 23: 12'ye atlayın.

Keşfettiğin gibi, başlatıcıdan @State değişkenini değiştirmek aşağıdaki hatayı oluşturacak: Thread 1: Önemli hata: View.body dışında State'e erişiliyor . Bundan kaçınmak için, @State'i kaldırmalısınız. Bu mantıklıdır çünkü includeDecimal bir gerçek kaynağı değildir. Değeri miktardan türetilir. Ancak @ State kaldırıldığında, includeDecimalmiktar değişirse güncellenmez. Bunu başarmak için en iyi seçenek, includeDecimal'inizi hesaplanmış bir özellik olarak tanımlamaktır, böylece değeri doğruluk kaynağından (miktar) türetilir. Bu şekilde, miktar değiştiğinde, includeDecimal değeriniz de değişir. Görünümünüz includeDecimal'e bağlıysa değiştiğinde güncellenmelidir:

struct AmountView : View {
    @Binding var amount: Double

    private var includeDecimal: Bool {
        return round(amount)-amount > 0
    }

    init(withAmount: Binding<Double>) {
        self.$amount = withAmount
    }

    var body: some View { ... }
}

Rob mayoff tarafından belirtildiği gibi, bir Durum değişkenini başlatmak için $$varName(beta 3) veya _varName(beta4) da kullanabilirsiniz :

// Beta 3:
$$includeDecimal = State(initialValue: (round(amount.value) - amount.value) != 0)

// Beta 4:
_includeDecimal = State(initialValue: (round(amount.value) - amount.value) != 0)

Teşekkürler! Bu çok yardımcı oldu! Ben bir çalışma zamanı hatası alıyorum self.includeDecimal = round(self.amount)-self.amount > 0aitThread 1: Fatal error: Accessing State<Bool> outside View.body
keegan3d

Pekala, mantıklı. @Statedeğişkenler bir gerçeğin kaynağını temsil etmelidir. Ancak sizin durumunuzda bu gerçeği kopyalıyorsunuz, çünkü includeDecimal değeri, miktar olan gerçek doğruluk kaynağınızdan türetilebilir. İki seçeneğiniz var: 1. includeDecimal'i özel bir değişken (@State yok) veya daha iyisi yaparsınız. 2. Onu, değerini türeten hesaplanmış bir özellik yaparsınız amount. Bu şekilde, miktar değişirse includeDecimalde değişir . Bunu şu şekilde beyan etmelisiniz: private var includeDecimal: Bool { return round(amount)-amount > 0 }veself.includeDecimal = ...
kontiki

Hmm, değiştirebilmem includeDecimalgerekiyor, bu yüzden görünümde bir @ Durum değişkeni olarak buna ihtiyacım var. Gerçekten onu bir başlangıç ​​değeriyle başlatmak istiyorum
keegan3d

1
@ Let's_Create Onları yalnızca bir kez izledim, ama ileri düğmesi için Tanrıya şükür ;-)
kontiki

1
Gerçekten güzel bir açıklama, teşekkürler. Sanırım şimdi .valueile değiştirildi .wrappedValue, cevabı güncellemek ve beta seçeneklerini kaldırmak güzel olurdu.
user1046037

11

(Bir yorumda) "Değiştirebilmem gerekiyor" dedin includeDecimal. Değişmek ne demektir includeDecimal? Görünüşe göre onu amount(başlatma zamanında) bir tam sayı olup olmadığına göre başlatmak istiyorsunuz . Tamam. Yani ne olur includeDecimalise falseve daha sonra daha sonra bunu değiştirmek true? amountO zaman bir şekilde tamsayı olmamaya mı zorlayacaksınız ?

Her neyse, includeDecimaliçinde değişiklik yapamazsınız init. Ama bunu şu şekilde başlatabilirsiniz init:

struct ContentView : View {
    @Binding var amount: Double

    init(amount: Binding<Double>) {
        $amount = amount
        $$includeDecimal = State(initialValue: (round(amount.value) - amount.value) != 0)
    }

    @State private var includeDecimal: Bool

(Bu Not noktada$$includeDecimal sözdizimi olarak değiştirilecek _includeDecimal.)


Harika, bu bölüm için ihtiyacım olan şey çifte $$ oldu!
keegan3d

2

2020'nin ortası olduğundan özetleyelim:

Benzer @Binding amount

  1. _amountyalnızca başlatma sırasında kullanılması önerilir. Ve self.$amount = xxxbaşlatma sırasında asla bu şekilde atama

  2. amount.wrappedValueve amount.projectedValuesık kullanılmaz, ancak aşağıdaki gibi durumları görebilirsiniz

@Environment(\.presentationMode) var presentationMode

self.presentationMode.wrappedValue.dismiss()
  1. @Binding'in yaygın kullanım durumu şudur:
@Binding var showFavorited: Bool

Toggle(isOn: $showFavorited) {
    Text("Change filter")
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.