@@ -105,22 +105,50 @@ extension View {
105105private struct ImageViewFrameModifier : ViewModifier {
106106 let size : MarkdownImageSize ?
107107
108+ @State private var currentSize : CGSize = . zero
109+
108110 func body( content: Content ) -> some View {
109111 if let size {
110112 switch size. value {
111113 case . fixed( let width, let height) :
112- content. frame ( width: width, height: height)
114+ content
115+ . frame ( width: width, height: height)
113116 case . relative( let wRatio, _) :
114- if #available( iOS 17 . 0 , * ) {
115- content
116- // .containerRelativeFrame(.vertical) { height, _ in height * hRatio }
117- . containerRelativeFrame ( . horizontal) { width, _ in width * wRatio }
118- } else {
117+ ZStack ( alignment: . leading) {
118+ /// Track the full content width.
119+ GeometryReader { metrics in
120+ content
121+ . preference ( key: BoundsPreferenceKey . self, value: metrics. frame ( in: . global) . size)
122+ }
123+ . opacity ( 0.0 )
124+
125+ /// Draw the content applying relative width. Relative height is not handled.
119126 content
127+ . frame (
128+ width: currentSize. width * wRatio
129+ )
130+ }
131+ . onPreferenceChange ( BoundsPreferenceKey . self) { newValue in
132+ /// Avoid recursive loop that could happens
133+ /// https://developer.apple.com/videos/play/wwdc2022/10056/?time=1107
134+ if Int ( currentSize. width) == Int ( newValue. width) ,
135+ Int ( currentSize. height) == Int ( newValue. height) {
136+ return
137+ }
138+
139+ self . currentSize = newValue
120140 }
121141 }
122142 } else {
123143 content
124144 }
125145 }
126146}
147+
148+ private struct BoundsPreferenceKey : PreferenceKey {
149+ static var defaultValue : CGSize = . zero
150+
151+ static func reduce( value: inout Value , nextValue: ( ) -> Value ) {
152+ value = nextValue ( )
153+ }
154+ }
0 commit comments