调整大小时的 SwiftUI 文本剪辑

Posted

技术标签:

【中文标题】调整大小时的 SwiftUI 文本剪辑【英文标题】:SwiftUI Text clips on resize 【发布时间】: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 与问题的演示或提供更可见重现的代码? 【参考方案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()
                
            
        
    

【讨论】:

以上是关于调整大小时的 SwiftUI 文本剪辑的主要内容,如果未能解决你的问题,请参考以下文章

在 Swiftui 的 Text() 中自动调整字体大小?

如何调整 SVG 剪辑路径的大小?

显示键盘时推送另一个视图时的tableView调整大小问题

为啥我的调整剪辑图像大小的脚本不起作用?

调整影片剪辑 AS3 的大小

SwiftUI:动态设置图像的大小