【问题标题】:SwiftUI Text clips on resize调整大小时的 SwiftUI 文本剪辑
【发布时间】:2020-02-16 22:59:36
【问题描述】:

在以下示例中,点击“展开”按钮会导致文本“39”在调整大小以转换为“40”时进行剪辑。这种使用的真正上下文是一个文本标签,它反映了动画图中选择器的值。我希望文本只占用它需要的空间,但在值之间进行动画处理时不进行剪辑。

禁用标签动画不是一个选项,因为标签在我的图表中的位置也会动画。

复制:

import SwiftUI

struct TextClipping: View {
    enum ExpansionState {
        case expanded
        case contracted

        mutating func toggle() {
            switch self {
            case .expanded:
                self = .contracted
            case .contracted:
                self = .expanded
            }
        }
    }

    @State var expansion: ExpansionState = .contracted

    var text: String {
        switch expansion {
        case .expanded:
            return "40"
        case .contracted:
            return "39"
        }
    }

    var body: some View {
        VStack(spacing: 16) {
            Text(text)
                .font(.system(.title, design: .rounded))
                .fontWeight(.bold)
                .foregroundColor(.black)

            Button(self.expansion == .contracted ? "Expand" : "Contract") {
                withAnimation {
                    self.expansion.toggle()
                }
            }
        }
    }
}

struct TextClipping_Previews: PreviewProvider {
    static var previews: some View {
        TextClipping()
    }
}

有什么想法可以让标签的文本流畅地变化而不被剪裁吗?

【问题讨论】:

  • 无法在 Xcode 13 / iOS 15 中重现。您能否附上 gif 与问题的演示或提供更可见重现的代码?

标签: animation text swiftui clipping


【解决方案1】:

所以,我认为您的问题并不完全清楚,但我认为您所指的“剪辑”是 39 和 40 在您之间制作动画时左右移动轻微他们。在父视图中,这可能会剪辑,但我在这里看不到它。 首先,您不需要制作自己的变异结构来修改视图中的变量,这就是@State 变量的全部意义,而这整个问题可以用一个简单的布尔值来解决。所有这些代码基本相同,除了一个关键行:.id 行。这告诉 SwiftUI 视图是不一样的。在添加之前,它认为它们是相同的视图,但是文本正在更改,导致偏移量看起来很奇怪。如果这仍然不能回答您的问题,请告诉我,但我无法想象您还指的是什么,因为您没有包含视觉示例,并且我无法重现您所指的“剪辑”效果.

struct TextClipping: View {
    @State private var expanded: Bool = false
    var body: some View {
        let text: String = expanded ? "40" : "39"
        VStack(spacing: 16) {
             Text(text)
                .font(.system(.title, design: .rounded))
                .fontWeight(.bold)
                .foregroundColor(.black)
                // Opacity is the default animation, but can be replaced by another if you feel like it
                //.transition(.opacity)
                .id("label\(text)")

            Button(expanded ? "Contract" : "Expand") {
                withAnimation {
                    expanded.toggle()
                }
            }
        }
    }
}

struct TextClipping_Previews: PreviewProvider {
    static var previews: some View {
        TextClipping()
    }
}

【讨论】:

  • 这应该是公认的答案,只需将 id 添加到 Text 即可解决问题。谢谢!
【解决方案2】:

通过申请解决

.minimumScaleFactor(0.1)

到正文。

【讨论】:

  • 这不起作用(至少在带有自定义字体的 iOS 15 上)
【解决方案3】:

您可以尝试将此修饰符添加到文本中

.fixedSize()

【讨论】:

    【解决方案4】:

    要在不禁用偏移动画的情况下禁用大小动画,我使用了嵌入另一个 UIViewControllerUIHostingController:这允许我在不使用动画的情况下更新大小,从而允许 SwiftUI 为偏移设置动画。

    附言从技术上讲,您可以将此代码转换为修饰符,但我认为将视图嵌入容器中应该很清楚,因为在某些情况下它会显着影响布局。

    struct DisableSizeAnimationContainer<Content: View>: View {
        let content: Content
        
        init(@ViewBuilder _ content: () -> Content) {
            self.content = content()
        }
        
        var body: some View {
            ContainerRepreseentable {
                content
            }.fixedSize()
        }
    }
    
    private struct ContainerRepreseentable<Content: View>: UIViewControllerRepresentable {
        let content: Content
        
        init(@ViewBuilder _ content: () -> Content) {
            self.content = content()
        }
        
        func makeUIViewController(context: Context) -> UIHostingControllerContainer<Content> {
            UIHostingControllerContainer(rootView: content)
        }
        
        func updateUIViewController(_ uiViewController: UIHostingControllerContainer<Content>, context: Context) {
            uiViewController.rootView = content
        }
    }
    
    private class UIHostingControllerContainer<RootView: View>: UIViewController {
        private let hostingController: UIHostingController<RootView>
        
        init(rootView: RootView) {
            hostingController = UIHostingController(rootView: rootView)
            super.init(nibName: nil, bundle: nil)
        }
        
        var rootView: RootView {
            get { hostingController.rootView }
            set {
                hostingController.rootView = newValue
                preferredContentSize = hostingController.view.intrinsicContentSize
                
                view.frame = .init(origin: .zero, size: preferredContentSize)
                hostingController.view.frame = view.frame
            }
        }
        
        required dynamic init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        override func viewDidLoad() {
            super.viewDidLoad()
    
            addChild(hostingController)
            view.addSubview(hostingController.view)
        }
    }
    

    用法:

    Button(action: {
        withAnimation {
            expansion.toggle()
        }
    }) {
        DisableSizeAnimationContainer {
            Text(expansion ? "Small text" : "Significantly larger text")
        }
    }.offset(x: expansion ? 100 : 0)
    

    结果:

    【讨论】:

      【解决方案5】:

      为了防止剪裁,您可以使用这样的叠加层:

      struct TextClipping: View {
          @State private var expanded: Bool = false
          var body: some View {
              let text: String = expanded ? "40" : "39"
              VStack(spacing: 16) {
                  Text("40")
                      .fontWeight(.bold)
                      .font(.system(.title, design: .rounded))
                      .hidden()
                      .overlay(
                          Text(text).fontWeight(.bold).font(.system(.title, design: .rounded))
                      ).id("label\(text)")
                  
                  Button(expanded ? "Contract" : "Expand") {
                      withAnimation {
                          expanded.toggle()
                      }
                  }
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-05-17
        • 2016-03-08
        • 2023-03-30
        • 1970-01-01
        相关资源
        最近更新 更多