I had a fixed View RectangleView
that displays content from external sources with arbitrary width and height which means that direct adjustments of frame is not preferred with the .frame
modifier. When user clicks on a button to rotate this View, the view rotates as expected but I wanted the horizontally rotated RectangleView
to fit into the screen and not overflow outside of the bounds of display.
// Playground
import SwiftUI
import PlaygroundSupport
struct RectangleView: View {
@Binding var rotation: Double
var body: some View {
Rectangle()
.fill(Color.red)
.frame(width: 100, height: 200) // fixed
.rotationEffect(.degrees(rotation))
}
}
struct ContentView: View {
@State public var rotation: Double = 0
var body: some View {
VStack {
RectangleView(rotation: $rotation)
Button(action: {
withAnimation {
rotation += 90
}
}) {
Text("Rotate")
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
}
}
}
PlaygroundPage.current.setLiveView(ContentView())
I had tried using .aspectRatio(contentMode: .fit)
, or a GeometryReader
to make the rectangle fit the screen on 90 or 270 degrees but nothing really works. How to solve this issue?
2
Answers
Problem is that you are rotating and modifiying the shape frame but you need to modify the View frame instead, you need to control when your view is in horizontal or vertical state and adjust maxHeight accordingly
Result
I am assuming that the content represented by the red rectangle might be any size and that this size is not known to the presenting view. You just want it to be scaled to fit, whatever size it is. The aspect ratio of the content should also be maintained.
The modifier
.scaledToFit
can be applied to anyView
and this is a convenient way to make a view fit into the available space. However, it only works if:In the case here, neither of the conditions are satisfied, so
.scaledToFit
needs some help to work.To resolve the first issue, the frame size can be set using
ìdealWidth
andidealHeight
, instead ofwidth
andheight
.To resolve the second issue, the layout needs to be aware of the rotation. A custom
Layout
can be used for this purpose.The example below uses a custom layout called
MaxRotatedContent
. This is only expecting one subview. If the subview has been rotated then the angle of rotation should be made known to the layout using the modifier.layoutValue
and keyRotationDegrees
.The custom layout computes the size of the rotated subview using the formula that can be found in the answer to How to scale a rotated rectangle to always fit another rectangle. This works for any angle of rotation. However, if you are only going to be rotating the view by exact multiples of 90 degrees then the calculation could be simplified.
BTW, the angle of rotation doesn’t need to be supplied to
RectangleView
as aBinding
, because it is read-only. So you can just define the variable usinglet
instead.