You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift5纯代码UIViewController中UITextView无法滚动问题求助

UITextView无法滚动的原因分析与解决建议

问题背景

我正在为应用添加烟花特效,需要通过代码生成UIView与UIViewController,部分场景无法使用Storyboard的Segue。目前遇到核心问题:在Storyboard创建的UIViewController内,代码实现的包含UILabel及关联@objc函数UIButton的UITextView可正常滚动;但在纯代码生成的GameViewController(已设置代理)中,相同逻辑的UITextView完全无法滚动。技术栈为Swift 5、UIKit、Xcode 15.0。

原因分析

1. UITextView布局方式冲突

GameViewController中同时为gameStatusTextview设置了frame和translatesAutoresizingMaskIntoConstraints = false,导致手动frame布局与AutoLayout规则冲突,系统无法正确计算textView的实际可滚动区域。

2. Child ViewController实例复用错误

在ViewController的aCreateController方法中,连续创建了3个独立的GameViewController实例:

  • 第一个实例设置了frame但未被使用
  • 第二个实例修改了frame.origin.x也未被使用
  • 第三个实例被添加为child controller,但未设置任何布局属性
    所有frame设置均为无效操作,GameViewController的view尺寸未被正确定义,进而影响内部textView的滚动逻辑。

3. ScrollView的ContentSize与Child View约束不匹配

scrollView.contentSize仅通过view.frame.size.width累加,未与GameViewController的view实际尺寸绑定,导致scrollView的可滚动范围计算错误;同时GameViewController的view未通过约束或正确frame绑定到scrollView,布局混乱。

4. UITextView内容尺寸未正确计算

由于是直接向UITextView添加子控件(UILabel、UIButton)而非使用文本内容,UITextView无法自动计算contentSize,默认contentSize与textView自身尺寸一致,自然无法触发滚动。

解决建议

1. 统一UITextView布局方式

选择AutoLayout或手动frame其中一种方式,避免混用。推荐使用AutoLayout:

// 替换GameViewController中gameStatusTextview的frame设置代码
gameStatusTextview.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(gameStatusTextview)
NSLayoutConstraint.activate([
    gameStatusTextview.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 5),
    gameStatusTextview.topAnchor.constraint(equalTo: screenLabel.bottomAnchor, constant: 50),
    gameStatusTextview.widthAnchor.constraint(equalToConstant: 380),
    gameStatusTextview.heightAnchor.constraint(equalToConstant: 412)
])

2. 修复Child ViewController的创建与布局

在ViewController的aCreateController中,复用同一个GameViewController实例,并通过约束绑定到scrollView:

func aCreateController() {
    let gameVC = GameViewController()
    gameVC.view.translatesAutoresizingMaskIntoConstraints = false
    add(child: gameVC, in: scrollView)
    
    // 约束gameVC的view尺寸与屏幕一致,位置在现有contentSize的右侧
    NSLayoutConstraint.activate([
        gameVC.view.topAnchor.constraint(equalTo: scrollView.topAnchor),
        gameVC.view.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
        gameVC.view.widthAnchor.constraint(equalTo: view.widthAnchor),
        gameVC.view.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: scrollView.contentSize.width)
    ])
    
    // 更新scrollView的可滚动宽度
    scrollView.contentSize.width += view.bounds.width
}

3. 手动计算并设置UITextView的ContentSize

在添加完所有子控件到gameStatusTextview后,遍历子控件计算最大Y值,手动设置contentSize:

// 在GameViewController的aRefreshView方法中,添加完所有label和button后
var maxContentY: CGFloat = 0
for subview in gameStatusTextview.subviews {
    if subview.frame.maxY > maxContentY {
        maxContentY = subview.frame.maxY
    }
}
// 增加底部间距避免内容被截断
gameStatusTextview.contentSize = CGSize(width: gameStatusTextview.bounds.width, height: maxContentY + 10)

4. 修复ViewController的aCreateView方法

原方法中view = UIView(frame: UIScreen.main.bounds)会替换Storyboard创建的原始view,导致原有控件丢失,应直接向原view添加scrollView:

func aCreateView() {
    view.addSubview(scrollView)
    NSLayoutConstraint.pin(view: scrollView, to: view)
}

内容的提问来源于stack exchange,提问作者BobA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 03:34:51