1
votes

Cadre et décalage du contenu SwiftUI ScrollView

Quelqu'un peut-il expliquer le comportement de ce code de test ScrollView - pourquoi je ne peux pas faire défiler vers les bords gauche et supérieur, et pourquoi je peux faire défiler au-delà des bords droit et inférieur? Et comment y remédier. (Notez que si nous supprimons VStack le comportement ne change pas.)

    var body: some View {

        ScrollView.init([.vertical,.horizontal]) {

            VStack {
                Text("AAA")
                    .padding(8)
                    .frame(width: 1024, height: 1024)
                    .background(Color.orange)
                    .border(Color.red)
            }

        }
        .border(Color.blue)

    }

Dans cette image est aussi loin vers la gauche et vers le haut que je peux faire défiler - il ne défile pas jusqu'aux bords: entrez la description de l'image ici

Mais ici, je peux faire défiler au-delà des bords inférieur et droit: entrez la description de l'image ici entrez la description de l'image ici


6 commentaires

par défaut, le contenu n'est pas aligné dans le scrollview bidimensionnel - comment voulez-vous l'aligner?


Il est aligné, la vue texte apparaît au centre de l'écran. Le problème est que vous ne pouvez pas faire défiler vers les bords supérieur et gauche du contenu, et vous pouvez faire défiler au-delà des bords droit et inférieur du contenu.


Vous trouverez peut-être ce sujet utile.


C'est une solution de contournement compliquée à un problème mal compris identique au mien. Donc, ce que vous dites, c'est que ScrollView a actuellement un bogue, et c'est un contournement pour cela.


andrewz, veuillez réduire la taille des images (ou achetez-moi un écran plus grand)


@ user3441734 .. a choisi l'option la moins chère


3 Réponses :


1
votes

J'ai adapté la réponse de @ Asperi à une autre question ( SwiftUI Comment aligner une vue plus grande que la largeur de l'écran ), et cela fonctionne.

    @State private var offset : CGPoint = .zero

    var body: some View {

        ScrollView.init([.vertical,.horizontal]) {

            VStack {
                Text("AAA")
                    .padding(8)
                    .frame(width: 1024, height: 1024)
                    .background(Color.orange)
                    .border(Color.red)
            }
            .background(rectReader())
            .offset(x: self.offset.x, y: self.offset.y)

        }
        .border(Color.blue)

    }

    func rectReader() -> some View {
        return GeometryReader { (geometry) -> AnyView in
            let offset : CGPoint = CGPoint.init(
                x: -geometry.frame(in: .global).minX,
                y: -geometry.frame(in: .global).minY
            )
            if self.offset == .zero {
                DispatchQueue.main.async {
                    self.offset = offset
                }
            }
            return AnyView(Rectangle().fill(Color.clear))
        }
    }


0 commentaires

1
votes

Ou vous pouvez essayer d'utiliser un ScrollView pour chaque direction, essayez ceci

import SwiftUI

struct ContentView: View {
    var body: some View {

        HStack {
            Text("Hello").padding().background(Color.yellow).offset(x: 20, y: 40).border(Color.red)
            Text("World").padding().background(Color.pink).offset(x: -10, y: -10).border(Color.yellow)
            }.padding().border(Color.gray)

    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

UPDATE

pour voir, ce qui se passe en utilisant le modificateur .offset, essayez Imaginez le résultat de cet extrait de code

import SwiftUI

struct ContentView: View {
    var body: some View {

        ScrollView(.horizontal, showsIndicators: true) {
            ScrollView(.vertical, showsIndicators: true) {
                Color.yellow.frame(width: 1024, height: 1024)
                    .overlay(Text("A").font(.largeTitle), alignment: .topLeading)
                    .overlay(Text("B").font(.largeTitle), alignment: .topTrailing)
                    .overlay(Text("C").font(.largeTitle), alignment: .bottomTrailing)
                    .overlay(Text("D").font(.largeTitle), alignment: .bottomLeading)
            }
        }

    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


6 commentaires

J'aime cette solution, mais elle ne reproduit pas exactement le comportement - ici, vous faites défiler horizontalement ou verticalement, mais pas les deux en même temps. Pourtant, c'est une alternative raisonnable sans ajouter de code compliqué.


@andrewz la motivation est différente, veuillez consulter stackoverflow.com/questions/60407125/... le modificateur .offset NE CHANGE PAS la disposition ou la position. La même chose est vraie pour tout modificateur de vue qui modifie l'arrière-plan ou la superposition, etc.


Les rectangles de texte sont décalés de l'alignement calculé par HStack. Je m'attendais à ce que les bordures soient alignées avec les rectangles de texte, mais ce n'est pas le cas. Hm, on dirait que l'alignement HStack est calculé sur la vue finale, c'est-à-dire la bordure, puis le calcul se propage vers les vues de texte, frappant le décalage en cours de route. C'est donc comme un parcours inversé de la chaîne de modificateurs de vue.


@andrewz la position et les limites ne sont pas affectées! et enfin, ajoutez ..clipped () sur HStack! Et c'est exactement la même chose avec la solution d'Asperi. Cela pourrait fonctionner, mais pas nécessaire dans toutes les situations.


Il est clair que les limites ne sont pas affectées. Le modificateur .offset () modifie le décalage sur sa vue - le décalage est une translation de position supplémentaire qui peut être appliquée à cette vue. Je pense que ce qui est intéressant, c'est que HStack aligne en fait le produit du modificateur .border (), et .offset () affecte la vue produite par le modificateur .background ().


@andrewz pour moi, cela semble logique. chaque modificateur renvoie une nouvelle vue et la vue finale est affectée par le système de disposition SwiftUI.



0
votes

Je suis tombé sur cette question et ces réponses alors que je cherchais aujourd'hui une solution à ce "problème de placement incorrect de la vue du contenu" pendant de nombreuses heures alors que j'essaie de faire défiler une image (agrandie) dans n'importe quelle direction.

Aucune des solutions proposées ici n'est «sympa» ou applicable dans mon cas. Mais après avoir trouvé une solution pour moi-même, je veux documenter ici comment je résoudrais votre problème.

Le problème principal est que le centre de votre vue de contenu (= VStack avec élément Text ) est aligné avec le centre de ScrollView . C'est toujours le cas: que la taille du contenu soit inférieure ou supérieure à la taille de ScrollView . (N'oubliez pas: la taille de ScrollView correspond à la taille de l'écran, moins les encarts de bord de la zone de sécurité) Pour aligner le bord d'attaque supérieur de vos vues de contenu sur le bord d'attaque supérieur des vues de défilement, vous devrez compenser votre contenu de la quantité qu'il chevauche actuellement sur le côté gauche.

La bonne chose: il peut être calculé en utilisant directement GeometryReader et la taille de contenu connue.

    var body: some View {
        GeometryReader { proxy -> AnyView in

            let insets = proxy.safeAreaInsets
            let sw = proxy.size.width + insets.leading + insets.trailing
            let sh = proxy.size.height + insets.top + insets.bottom

            let contentSize : CGFloat = 1024
            let dx: CGFloat = (contentSize > sw ? (contentSize - sw)/2 : 0.0)
            let dy: CGFloat = (contentSize > sh ? (contentSize - sh)/2 : 0.0)

            return AnyView(
                ScrollView([.vertical,.horizontal]) {
                    VStack {
                        Text("AAA")
                            .padding(8)
                            .frame(width: 1024, height: 1024)
                            .background(Color.orange)
                            .border(Color.red)
                    }
                    .offset(x: dx, y: dy)
                }
                .border(Color.blue)
            )
        }
    }


0 commentaires