我有一个固定的视图RectangleView,它显示来自外部来源的内容,具有任意的宽度和高度,这意味着直接调整框架不是.frame修改器的首选.当用户点击按钮旋转这个视图时,视图会按预期旋转,但我希望水平旋转的RectangleView适合屏幕(同时也保留长宽比),并且不会溢出到显示边界之外.

// 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? enter image description here

推荐答案

我假设红色矩形表示的内容可以是任意大小,并且该大小对于演示视图是未知的.您只需要将其zoom 以适合它,无论它的大小是什么.内容的纵横比也应该保持不变.

修改器.scaledToFit可以应用于任何View,并且这是使视图适合可用空间的方便方式.但是,只有在以下情况下才能起作用:

  1. 尚未对该视图应用固定的宽度或高度
  2. 转换(例如旋转)尚未应用于视图.

在这里,这两个条件都不满足,所以.scaledToFit需要一些帮助才能工作.

  • 为了解决第一个问题,可以使用ìdealWidthidealHeight来设置帧大小,而不是widthheight.

  • 要解决第二个问题,布局需要知道旋转.定制Layout可用于此目的.

下面的示例使用名为MaxRotatedContent的自定义布局.这只需要一个子视图.如果子视图已旋转,则应使用修改器.layoutValue和键RotationDegrees使布局知道旋转Angular .

自定义布局使用可在How to scale a rotated rectangle to always fit another rectangle的答案中找到的公式计算旋转子视图的大小.这适用于任何旋转Angular .但是,如果只将视图精确旋转90度的倍数,则计算可以简化.

顺便说一句,旋转Angular 不需要作为Binding提供给RectangleView,因为它是只读的.所以您只需使用let定义变量即可.

struct RotationDegrees: LayoutValueKey {
    static let defaultValue = Double.zero
}

struct RectangleView: View {
    let rotation: Double

    var body: some View {
        Rectangle()
            .fill(Color.red)
//            .frame(width: 100, height: 200) // fixed
            .frame(idealWidth: 100, idealHeight: 200)
            .rotationEffect(.degrees(rotation))
    }
}

struct MaxRotatedContent: Layout {

    public func sizeThatFits(
        proposal: ProposedViewSize,
        subviews: Subviews,
        cache: inout ()
    ) -> CGSize {
        proposal.replacingUnspecifiedDimensions()
    }

    public func placeSubviews(
        in bounds: CGRect,
        proposal: ProposedViewSize,
        subviews: Subviews,
        cache: inout ()
    ) {
        if let firstSubview = subviews.first {

            // Credit to M Oehm for the formula used here
            // https://stackoverflow.com/a/33867165/20386264
            let subviewSize = firstSubview.sizeThatFits(proposal)
            let w = subviewSize.width
            let h = subviewSize.height
            let rotationDegrees = firstSubview[RotationDegrees.self]
            let rotationRadians = rotationDegrees * .pi / 180
            let absSin = abs(sin(rotationRadians))
            let absCos = abs(cos(rotationRadians))
            let W = (w * absCos) + (h * absSin)
            let H = (w * absSin) + (h * absCos)
            let scalingFactor = min((bounds.width / W), (bounds.height / H))
            let scaledWidth = scalingFactor * w
            let scaledHeight = scalingFactor * h
            let x = bounds.minX + (bounds.width - scaledWidth) / 2
            let y = bounds.minY + (bounds.height - scaledHeight) / 2
            firstSubview.place(
                at: CGPoint(x: x, y: y),
                proposal: .init(width: scaledWidth, height: scaledHeight)
            )
        }
    }
}

struct ContentView: View {
    @State public var rotation: Double = 0

    var body: some View {
        VStack {

            MaxRotatedContent {
                RectangleView(rotation: rotation)
                    .scaledToFit()
                    .layoutValue(key: RotationDegrees.self, value: rotation)
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .border(.black)

            Slider(value: $rotation, in: 0...360)
                .padding()
        }
    }
}

Animation

Ios相关问答推荐

IFrame内容拒绝仅在iOS浏览器上加载-即使发布了内容-安全-策略(CSP)框架-祖先指令

SWIFT AVFoundation不能在查看更新的同时发布更新

快速相机放大手势不能正常工作

如何在SwiftUI中将图像整合到文本标签中间?

如何在iOS 17 SwiftUI中获取用户名

Xcode 15阻止我的应用程序运行:由于实时模式录制的高比率,丢失了1条日志(log)/路标消息?

objc[25442]:Swift类的扩展和类别不允许有+load方法

执行devicectl:ProcessException时出错:进程异常退出:错误:命令超时超过5.0秒

在 Android 和 iOS 应用程序上下文中阻止后台线程有哪些缺点?

无法使用 NavigationView 和 NavigationLink 在 SwiftUI 中弹出当前屏幕

为什么@FetchRequest 在删除时不更新和重绘视图?

SwiftUI Disclosure Group 文本对齐

SwiftUI DatePicker 格式在每个设备上都不一样,如何让它总是显示相同的 Select 版本?

如何让我的 iOS 应用程序与 api 连接?

UIButton在iOS7中没有显示突出显示

警告:iPad:Icon-72.png:图标尺寸(0 x 0)

针对 Xcode 中的 iPad 选项

使用 swift 从应用程序委托打开视图控制器

如何更改uitableview删除按钮文本

使用 xib 创建可重用的 UIView(并从情节提要中加载)