Comportamento SwiftUI di .frame (altezza: zero)

Oct 26 2020

Il problema

Ricevo un problema di thread che dice "Dimensione frame non valida (negativa o non finita)".

Ecco il mio codice:

struct CellStyle: ViewModifier {
    func body (content: Content) -> some View {
        soddisfare
            .frame (larghezza: .infinity, altezza: 56, allineamento: .center)
            .padding (.horizontal, 16)
    }
}

Il codice funziona correttamente e sembra che faccia quello che dovrebbe, quindi sono confuso sul motivo per cui mi sta dando problemi di thread. Perché il codice verrà eseguito senza una larghezza / altezza finita, in contraddizione con i documenti di Apple? (vedere la sezione "Alcune letture" di seguito)

La correzione

Questo codice risolve il problema perfettamente, ma vorrei comunque capire perché il codice precedente non si arresta in modo anomalo o crea un errore.

struct CellStyle: ViewModifier {
    func body (content: Content) -> some View {
        soddisfare
            .frame (altezza: 56, allineamento: .center)
            .frame (maxWidth: .infinity)
            .padding (.horizontal, 16)
    }
}

Per quanto ne so, mi dà quel problema di thread perché dico .frame, che voglio una larghezza che non è finita e che vuole una larghezza finita.

Altre domande sullo stack overflow

Ho trovato due domande che chiedono la stessa cosa:
iOS 14 Dimensione frame non valida (negativa o non finita)

SwiftUI iOS14 GeometryReader Dimensione del frame non valida

Nella prima domanda, il problema del thread ha senso perché dato il codice .frame(width: p.size.width - padding)non c'è alcuna garanzia che p.size.widthsarà inferiore a padding. Per quanto ne so, è un problema in cui il valore dato può essere negativo.
Questo problema che ho a che fare con il valore dato non è finito, quindi le domande non sono correlate.
La seconda domanda non ha ancora avuto risposta ed è un po 'vaga, quindi non è di grande aiuto per me in questo momento. Forse in seguito qualcuno risponderà con una risposta utile, ma per ora non è utile.

Qualche lettura

Guardando i documenti Apple trovo questo:

Utilizzare questo metodo per specificare una dimensione fissa per la larghezza, l'altezza o entrambe le viste. Se si specifica solo una delle dimensioni, la vista risultante assume il comportamento di ridimensionamento di questa vista nell'altra dimensione.

Allora perché .frame(width: .infinity, height: 56, alignment: .center)funziona anche solo?
Cosa intende Apple con "la vista risultante assume il comportamento di dimensionamento di questa vista nell'altra dimensione"? Presumerà sempre che la vista sia una vista "push out", dando così lo stesso risultato di .infinity?

L'ho provato e ho eseguito questo codice:

struct CellStyle: ViewModifier {
    func body (content: Content) -> some View {
        soddisfare
            .frame (altezza: 56, allineamento: .center)
            .padding (.horizontal, 16)
    }
} 

Sembra dare lo stesso risultato, quindi ora sono curioso, c'è mai stato un caso in cui "assumendo" rapidamente la larghezza si tradurrà in un comportamento diverso da .infinity?

Domanda finale

Qual è la differenza di comportamento tra .frame(width: .infinity)vs .frame(width: nil)(l'equivalente di emettere larghezza o altezza).

func frame (width: CGFloat? = nil, height: CGFloat? = nil, alignment: Alignment = .center) -> some View

Documenti Apple

Grazie per l'aiuto!

Risposte

1 Asperi Oct 26 2020 at 19:43

Perché è un contratto di interfaccia documentato:

/// - Parameters:
///   - width: A fixed width for the resulting view. If `width` is `nil`,
///     the resulting view assumes this view's sizing behavior.
///   - height: A fixed height for the resulting view. If `height` is `nil`,
///     the resulting view assumes this view's sizing behavior.
///   - alignment: The alignment of this view inside the resulting view.
///     `alignment` applies if this view is smaller than the size given by
///     the resulting frame.
///
/// - Returns: A view with fixed dimensions of `width` and `height`, for the
///   parameters that are non-`nil`.
@inlinable public func frame(width: CGFloat? = nil, height: CGFloat? = nil, alignment: Alignment = .center) -> some View

mentre il tipo di modificatore con argomenti min / max consente vincoli flessibili (non copiati qui - leggi nel modulo SwiftUI generato).

MikaelWeiss Oct 26 2020 at 22:32

Cosa fa .frame (larghezza: zero)

Fondamentalmente .frame prenderà i valori dati e restituirà un'altra vista che viene aggiornata con i valori inseriti.
Se si omette uno dei parametri o lo si imposta a zero, verrà utilizzato il rispettivo valore di larghezza / altezza della vista precedente.

Codice di prova:

struct ContentView: View {
    @State var keyValue = ""
    var body: some View {
        VStack(spacing: 10) {
            Text("Something")
                .background(Color.blue)
            
            Text("Something")
                .frame(width: 300, height: nil, alignment: .center)
                .background(Color.blue)
            
            Text("Something")
                .frame(width: nil, height: 200, alignment: .center)
                .background(Color.blue)
            
            Text("Something")
                .frame(width: nil, height: nil, alignment: .center)
                .background(Color.blue)
        }
    }
}

Perché .frame (larghezza: .infinity) funziona ancora

.infinity( Documento Apple su .infinity ) è di tipo Float, quindi non c'è modo di .framenon escluderlo , ma può comunque creare problemi. (Così il problema del thread)

Grazie a Mark Moeykens per l'aiuto nel farmi arrivare a questa risposta!