使用 UIViewControllerRepresentable 时 UIViewController 子视图的大小/布局不正确(没有情节提要)

Posted

技术标签:

【中文标题】使用 UIViewControllerRepresentable 时 UIViewController 子视图的大小/布局不正确(没有情节提要)【英文标题】:UIViewController subview incorrect size/layout when using UIViewControllerRepresentable (without storyboard) 【发布时间】:2021-09-06 22:22:39 【问题描述】:

我正在尝试使用 tapRecognizer 和 UITextView 构建并集成到自定义 UIViewController。

我面临的问题是 UITextView 将部分文本显示在屏幕外,我不知道是什么原因造成的!我期待一些简单的东西,或者可能包括更多的课程,但我还没有找到任何指向我正确方向的东西。

UITextView 的大小/位置在使用 UIViewRepresentable 和 UITextView 时运行良好,但由于某些原因不能在其中使用 UIViewController / UIViewControllerRepresentable 和 UITextView(参见下面的示例以进行比较)。

这是我拥有的测试应用程序的代码:

import SwiftUI
import UIKit

struct TestingView: View 
    var body: some View 
        VStack 
            WrappedUIViewController().padding()
            Spacer()
            WrappedUITextView(myText: "This is a long text to see if the word wrap work in this case better I hope so but I don't know. Is it? I hope it is, ! Do you? I do! Hope you do too, do you?").padding()
            Spacer()
        
    


struct WrappedUITextView: UIViewRepresentable 
    let myText: String
    
    func makeUIView(context: Context) -> UITextViewPlus 
        let view = UITextViewPlus()
        view.isScrollEnabled = true
        view.isEditable = false
        view.isUserInteractionEnabled = true
        view.font = UIFont(name: "Time New Roman", size: 20)
        
        view.isOpaque = false
        view.text = "WrappedUITextView \(myText)"
        return view
    
    
    func updateUIView(_ uiView: UITextViewPlus, context: Context) 


struct WrappedUIViewController: UIViewControllerRepresentable 
    typealias UIViewControllerType = CustomUIViewController
    
    func makeUIViewController(context: Context) -> CustomUIViewController 
        return CustomUIViewController()
    
    
    func updateUIViewController(_ uiViewController: CustomUIViewController, context: Context) 


class CustomUIViewController: UIViewController 
    var textView: UITextView = UITextView()
    var tapGesture: UITapGestureRecognizer!
    
    override func viewDidLoad() 
        super.viewDidLoad()
        tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(recognizer:)))
        self.textView.addGestureRecognizer(tapGesture)
        self.textView.isEditable = false
        self.textView.isSelectable = false
        self.textView.clipsToBounds = true
        
        self.textView.text = """
            This is some text that will need to be displayed on multiple lines as it's longer than the screen size, will it wrapp correctly?
            This is a great testing app
                        
            This is the end of this textView content
            Here is a Potato
            """
        
        self.textView.backgroundColor = UIColor.green
        self.textView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        self.textView.textContainer.lineBreakMode = .byWordWrapping
        view.addSubview(self.textView)
        
    
    
    @objc func handleTap(recognizer: UITapGestureRecognizer) 
        let location: CGPoint = recognizer.location(in: textView)
        let position: CGPoint = CGPoint(x: location.x, y: location.y)
        let tapPosition: UITextPosition = textView.closestPosition(to: position)!
        guard let textRange: UITextRange = textView.tokenizer.rangeEnclosingPosition(tapPosition, with: UITextGranularity.word, inDirection: UITextDirection(rawValue: 1)) else  return 
        
        let tappedWord: String = textView.text(in: textRange) ?? ""
        print("tapped word -> \(tappedWord)")
    
    
    override func viewWillLayoutSubviews() 
        textView.sizeThatFits(self.view.bounds.size)
        textView.sizeToFit()
    

由于某些原因,绿色 textView 没有正确包裹文本或尺寸超出屏幕:

我是 swift 和 ios 的新手,所以如果我做错了什么,我不会感到惊讶,欢迎任何帮助!

编辑:如果其他文件的代码有助于解决这个问题(我对此表示怀疑):

import SwiftUI

@main
struct testAppApp: App 
    var body: some Scene 
        WindowGroup 
            Text("Test App")
            TestingView()
        
    

【问题讨论】:

这快把我逼疯了!除了给我的 UITextView 提供一个固定框架以确保它不会超出屏幕之外,没有其他方法吗? 我认为这是因为在 UIViewController 中您需要添加约束以正确对齐您的 UITextView,无论是编程方式还是在情节提要上 你给了我一些有趣的东西,可悲的是,添加约束似乎会导致更多问题。我与现有约束发生冲突。我用这篇文章添加了约束:***.com/questions/33072969/… 我已对您的代码进行了更改并将其发布为答案,希望这能解决您的问题。 【参考方案1】:

我已对您的 CustomViewController 类进行了更改,并为您的 textview 添加了约束以使其正确对齐。我添加的方法和代码行已注释

你必须告诉自动布局你希望你的视图如何对齐和定位,否则一切都会混乱。

class CustomUIViewController: UIViewController 
    var textView: UITextView = UITextView()
    var tapGesture: UITapGestureRecognizer!
    
    override func viewDidLoad() 
        super.viewDidLoad()
        tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(recognizer:)))
        self.textView.addGestureRecognizer(tapGesture)
        self.textView.isEditable = false
        self.textView.isSelectable = false
        self.textView.clipsToBounds = true
        
        self.textView.text = """
            This is some text that will need to be displayed on multiple lines as it's longer than the screen size, will it wrapp correctly?
            This is a great testing app
                        
            This is the end of this textView content
            Here is a Potato
            """
        
        self.textView.backgroundColor = UIColor.green
        self.textView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        self.textView.textContainer.lineBreakMode = .byWordWrapping
        self.textView.translatesAutoresizingMaskIntoConstraints = false //ADDED: allows view to obey Auto Layout constraints.
        view.addSubview(self.textView)
        
        addConstraints() //ADDED: method for invoking constraints
        
    
    
    func addConstraints()
        let margins = view.layoutMarginsGuide //safe area layout - esp for devices with notch
        NSLayoutConstraint.activate([
            textView.topAnchor.constraint(equalTo: margins.topAnchor), //textview's top anchor = that of parent view
            textView.leadingAnchor.constraint(equalTo: margins.leadingAnchor),
            textView.heightAnchor.constraint(equalToConstant: 200), //gave textview a constant height
            textView.trailingAnchor.constraint(equalTo: margins.trailingAnchor)
        ])
    
    
    @objc func handleTap(recognizer: UITapGestureRecognizer) 
        let location: CGPoint = recognizer.location(in: textView)
        let position: CGPoint = CGPoint(x: location.x, y: location.y)
        let tapPosition: UITextPosition = textView.closestPosition(to: position)!
        guard let textRange: UITextRange = textView.tokenizer.rangeEnclosingPosition(tapPosition, with: UITextGranularity.word, inDirection: UITextDirection(rawValue: 1)) else  return 
        
        let tappedWord: String = textView.text(in: textRange) ?? ""
        print("tapped word -> \(tappedWord)")
    
    
    override func viewWillLayoutSubviews() 
        textView.sizeThatFits(self.view.bounds.size)
        textView.sizeToFit()
    

【讨论】:

感谢您提供此代码。来自 swiftUI 我发现很难将我的头脑围绕在“旧的做事方式”上。我会玩弄你给我的东西,看看我是否可以让视图只包裹文本,仅此而已! 很高兴我可以帮助@Slamit,如果以上解决了您的问题,请考虑将其标记为答案 我没有设法让视图环绕文本,同时保持在屏幕上的约束:(。我会继续玩跳跃我可能会管理它:-)【参考方案2】:

经过多次迭代,我设法得到了一些可行的方法,实际上有 2 个选项可以解决这个问题:

    在 viewDidLoad 中将“view.addSubview(self.textView)”更改为“view.= self.textView”。我不确定这个变化是什么,但有了这个我有 UITextview 由系统调整。
override func viewDidLoad() 
        super.viewDidLoad()
        ...
        view = self.textView
    
    第二个也是最有可能“更好”的解决方案:在 viewDidLoad 中为我的 UITextView 设置一个框架。当“textView.sizeToFit()”时,高度会被添加,而宽度会保持在我想要的范围内:
    override func viewDidLoad() 
        super.viewDidLoad()
        ...
        self.textView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 10000)
        ...
        view.addSubview(self.textView)
    

【讨论】:

以上是关于使用 UIViewControllerRepresentable 时 UIViewController 子视图的大小/布局不正确(没有情节提要)的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)