Arrivée d'un mur de texte
Il est préférable de ne pas considérer que les modificateurs modifient les MapView
. Pensez plutôt à MapView().edgesIgnoringSafeArea(.top)
comme renvoyant un SafeAreaIgnoringView
dont body
est le MapView
et qui dispose son corps différemment selon que son propre bord supérieur se trouve ou non au bord supérieur de la zone de sécurité. C'est ainsi qu'il faut l'envisager, car c'est ce qu'il fait en réalité.
Comment pouvez-vous être sûr que je dis la vérité ? Insérez ce code dans votre application(_:didFinishLaunchingWithOptions:)
méthode :
let mapView = MapView()
let safeAreaIgnoringView = mapView.edgesIgnoringSafeArea(.top)
let framedView = safeAreaIgnoringView.frame(height: 300)
print("framedView = \(framedView)")
Cliquez maintenant sur l'option mapView
pour voir son type déduit, qui est simple MapView
.
Ensuite, cliquez sur l'option safeAreaIgnoringView
pour voir son type déduit. Son type est _ModifiedContent<MapView, _SafeAreaIgnoringLayout>
. _ModifiedContent
est un détail d'implémentation de SwiftUI et est conforme à View
lorsque son premier paramètre générique (nommé Content
) est conforme à la norme View
. Dans ce cas, son Content
es MapView
, de sorte que cette _ModifiedContent
est également un View
.
Ensuite, cliquez sur l'option framedView
pour voir son type déduit. Son type est _ModifiedContent<_ModifiedContent<MapView, _SafeAreaIgnoringLayout>, _FrameLayout>
.
Vous pouvez donc voir cela au niveau du type, framedView
est une vue dont le contenu a le type safeAreaIgnoringView
y safeAreaIgnoringView
est une vue dont le contenu a le type mapView
.
Mais il ne s'agit que de types, et la structure imbriquée des types peut ne pas être représentée au moment de l'exécution dans les données réelles, n'est-ce pas ? Exécutez l'application (sur un simulateur ou un appareil) et regardez la sortie de l'instruction print :
framedView =
_ModifiedContent<
_ModifiedContent<
MapView,
_SafeAreaIgnoringLayout
>,
_FrameLayout
>(
content:
SwiftUI._ModifiedContent<
Landmarks.MapView,
SwiftUI._SafeAreaIgnoringLayout
>(
content: Landmarks.MapView(),
modifier: SwiftUI._SafeAreaIgnoringLayout(
edges: SwiftUI.Edge.Set(rawValue: 1)
)
),
modifier:
SwiftUI._FrameLayout(
width: nil,
height: Optional(300.0),
alignment: SwiftUI.Alignment(
horizontal: SwiftUI.HorizontalAlignment(
key: SwiftUI.AlignmentKey(bits: 4484726064)
),
vertical: SwiftUI.VerticalAlignment(
key: SwiftUI.AlignmentKey(bits: 4484726041)
)
)
)
)
J'ai reformaté la sortie car Swift l'imprime sur une seule ligne, ce qui la rend très difficile à comprendre.
Quoi qu'il en soit, nous pouvons constater qu'en fait framedView
a apparemment un content
dont la valeur est le type de safeAreaIgnoringView
et cet objet a son propre content
dont la valeur est un MapView
.
Ainsi, lorsque vous appliquez un "modificateur" à un View
vous ne modifiez pas vraiment la vue. Vous créez un nouveau View
dont body
/ content
est l'original View
.
Maintenant que nous comprenons ce que font les modificateurs (ils construisent des enveloppes View
), nous pouvons raisonnablement deviner comment ces deux modificateurs ( edgesIgnoringSafeAreas
y frame
) affectent la mise en page.
À un moment donné, SwiftUI parcourt l'arbre pour calculer le cadre de chaque vue. Elle commence par la zone de sécurité de l'écran, qui est le cadre de notre vue de haut niveau ContentView
. Il visite ensuite le ContentView
qui est (dans le premier tutoriel) une VStack
. Pour un VStack
SwiftUI divise le cadre de l'article en deux. VStack
parmi les enfants de la pile, qui sont au nombre de trois _ModifiedContent
suivi d'un Spacer
. SwiftUI passe en revue les enfants pour déterminer l'espace qui leur est alloué. Le premier _ModifiedChild
(qui contient en fin de compte le MapView
) a un _FrameLayout
dont le modificateur height
est de 300 points, ce qui correspond à la part de la VStack
est assignée à la première _ModifiedChild
.
SwiftUI finit par comprendre quelle partie de l'élément VStack
à attribuer à chacun des enfants. Il visite ensuite chacun des enfants pour leur assigner leur cadre et disposer les enfants des enfants. Il visite donc ce _ModifiedContent
avec le _FrameLayout
en fixant son cadre à un rectangle qui rejoint le bord supérieur de la zone de sécurité et qui a une hauteur de 300 points.
Puisque la vue est un _ModifiedContent
avec un _FrameLayout
dont le modificateur height
est 300, SwiftUI vérifie que la hauteur attribuée est acceptable pour le modificateur. C'est le cas, et SwiftUI n'a donc pas besoin de modifier davantage le cadre.
Il visite ensuite l'enfant de ce _ModifiedContent
, arrivant à la _ModifiedContent
dont le modificateur est `_SafeAreaIgnoringLayout. Il définit le cadre de la vue d'alignement sur la zone de sécurité comme étant le même que celui de la vue parentale (qui définit le cadre).
Ensuite, SwiftUI doit calculer le cadre de l'enfant de la vue qui s'aligne sur la zone de sécurité (l'élément MapView
). Par défaut, l'enfant reçoit le même cadre que le parent. Mais comme ce parent est un _ModifiedContent
dont le modificateur est _SafeAreaIgnoringLayout
SwiftUI sait qu'il peut être nécessaire d'ajuster le cadre de l'enfant. Étant donné que la fonction edges
est fixé à .top
SwiftUI compare le bord supérieur du cadre du parent au bord supérieur de la zone de sécurité. Dans ce cas, ils coïncident, donc Swift élargit le cadre de l'enfant pour couvrir la partie de l'écran située au-dessus de la partie supérieure de la zone de sécurité. Le cadre de l'enfant s'étend donc en dehors du cadre du parent.
Ensuite, SwiftUI visite le MapView
et lui attribue le cadre calculé ci-dessus, qui s'étend au-delà de la zone de sécurité jusqu'au bord de l'écran. Ainsi, l'image MapView
La hauteur de la zone de sécurité est de 300 plus l'étendue au-delà du bord supérieur de la zone de sécurité.
Vérifions-le en traçant une bordure rouge autour de la vue d'alignement de la zone de sécurité et une bordure bleue autour de la vue de définition du cadre :
MapView()
.edgesIgnoringSafeArea(.top)
.border(Color.red, width: 2)
.frame(height: 300)
.border(Color.blue, width: 1)
La capture d'écran révèle qu'en effet, les cadres des deux _ModifiedContent
coïncident et ne sortent pas de la zone de sécurité. (Il se peut que vous deviez zoomer sur le contenu pour voir les deux bordures).
C'est ainsi que SwiftUI fonctionne avec le code du projet tutoriel. Maintenant, que se passe-t-il si nous intervertissons les modificateurs sur les objets MapView
comme vous l'avez proposé ?
Lorsque SwiftUI visite la page VStack
enfant de la ContentView
Elle doit répartir les VStack
parmi les enfants de la pile, comme dans l'exemple précédent.
Cette fois-ci, le premier _ModifiedContent
est celui avec le _SafeAreaIgnoringLayout
modificateur. SwiftUI constate qu'il n'y a pas de hauteur spécifique, et se tourne donc vers le modificateur _ModifiedContent
qui est aujourd'hui l'enfant de la _ModifiedContent
avec le _FrameLayout
modificateur. Cette vue a une hauteur fixe de 300 points, de sorte que SwiftUI sait maintenant que le modificateur safe-area-ignoring _ModifiedContent
devrait être de 300 points. SwiftUI accorde donc les 300 premiers points de la liste des VStack
au premier enfant de la pile (l'aliénation de la zone de sécurité _ModifiedContent
).
Plus tard, SwiftUI visite ce premier enfant pour lui assigner son cadre actuel et disposer ses enfants. SwiftUI définit donc la valeur de la zone de sécurité (safe-area-ignoring) _ModifiedContent
à exactement les 300 premiers points de la zone de sécurité.
Ensuite, SwiftUI doit calculer le cadre de l'aliénation de la zone de sécurité. _ModifiedContent
qui est l'enfant du cadre _ModifiedContent
. Normalement, l'enfant reçoit le même cadre que le parent. Mais comme le parent est un _ModifiedContent
avec un modificateur de _SafeAreaIgnoringLayout
dont edges
es .top
SwiftUI compare le bord supérieur du cadre du parent au bord supérieur de la zone de sécurité. Dans cet exemple, ils coïncident, et SwiftUI étend donc le cadre de l'enfant jusqu'au bord supérieur de l'écran. Le cadre est donc à 300 points plus l'étendue au-dessus du haut de la zone de sécurité.
Lorsque SwiftUI cherche à définir le cadre de l'enfant, il constate que l'enfant est un _ModifiedContent
avec un modificateur de _FrameLayout
dont height
est de 300. Comme le cadre est plus haut que 300 points, il n'est pas compatible avec le modificateur, et SwiftUI est donc obligé d'ajuster le cadre. Elle ramène la hauteur du cadre à 300, mais il ne se retrouve pas avec le même cadre que le parent . L'étendue supplémentaire (en dehors de la zone de sécurité) a été ajoutée au sommet du cadre, mais la modification de la hauteur du cadre modifie le bord inférieur du cadre.
L'effet final est donc que le cadre est déplacé plutôt que de l'étendre, en fonction de l'étendue au-dessus de la zone de sécurité. Le cadre _ModifiedContent
obtient un cadre qui couvre les 300 premiers points de l'écran, plutôt que les 300 premiers points de la zone de sécurité.
SwiftUI visite ensuite l'enfant de la vue qui définit le cadre, qui est la vue MapView
et lui donne le même cadre.
Nous pouvons le vérifier en utilisant la même technique de traçage des frontières :
if false {
// Original tutorial modifier order
MapView()
.edgesIgnoringSafeArea(.top)
.border(Color.red, width: 2)
.frame(height: 300)
.border(Color.blue, width: 1)
} else {
// LinusGeffarth's reversed modifier order
MapView()
.frame(height: 300)
.border(Color.red, width: 2)
.edgesIgnoringSafeArea(.top)
.border(Color.blue, width: 1)
}
Ici, nous pouvons voir que l'ignorant de la zone de sécurité _ModifiedContent
(avec la bordure bleue cette fois) a le même cadre que dans le code original : il commence en haut de la zone de sécurité. Mais nous pouvons également voir que maintenant le cadre du frame-setting _ModifiedContent
(avec la bordure rouge cette fois) commence au bord supérieur de l'écran, et non au bord supérieur de la zone de sécurité, et le bord inférieur du cadre a également été décalé vers le haut dans la même mesure.