【问题标题】:Resizing a multi line UILabel within a UIScrollView breaks scrolling. Why?在 UIScrollView 中调整多行 UILabel 的大小会中断滚动。为什么?
【发布时间】:2019-07-29 05:24:59
【问题描述】:

我使用以下设置:

  • ViewControllers 视图包含一个 UIScrollView,其中包含 Top、Leading、Trailing 和 Bottom 约束以匹配 VC 的大小
  • ScrollView 包含两个子视图:
    • UIView 用于定义滚动视图的内容大小。它的高度与 ScrollView 相同,但宽度是其两倍。因此只能水平滚动。
    • 带有一些长文本的UILabel 带有用于设置固定大小的高度和宽度约束以及用于设置固定位置的 ScrollView 的顶部和前导约束。
  • 当 ScrollView 滚动时,Label 的宽度会发生变化。

问题:如果 Label 设置为使用多行并且手动设置 ScrollViews contenOffset 属性,则 ScrollView 停止滚动。

ViewController View
+---------------------+
|+-------------------+| 
||ScrollView         ||
||+------------------||--------------------+
|||UIView to define  || content size       |
|||                  ||                    |
|||                  ||                    |
|||  [MultiLine]     ||                    |
|||  [  Label  ]     ||                    |
|||                  ||                    |
||+------------------||--------------------+
|+-------------------+|
+---------------------+


- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    // Setting the ContentOffset will stop scrolling 
    //[self.scrollView setContentOffset:CGPointMake(0, 0)];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    // Resize Label when scrolling
    self.labelWidthConstraint.constant = MAX (50, 50 + self.scrollView.contentOffset.x);
}

如果

使用此代码调整标签大小可以正常工作
  • 标签设置为使用一行。在这种情况下,设置内容偏移量不会造成任何伤害。
  • 内容偏移量没有改变(甚至没有设置为(0, 0))。在这种情况下,将标签设置为多行不会造成任何伤害

设置内容偏移量并同时使用多行不起作用。卷轴不能再滚动了。

为什么会这样?知道是什么原因造成的以及如何解决吗?

【问题讨论】:

    标签: ios objective-c uiscrollview ios-autolayout


    【解决方案1】:

    问题似乎是,当标签约束发生更改时,它会触发 viewDidLayoutSubviews,然后将 UIScrollView 设置为不滚动,因为 set contentOffset 会被一遍又一遍地调用。如果您只想通过使用 bool 作为标志在初始布局上将 UIScrollView 设置为 CGPoint.zero ,则可以克服这个问题。显然,由于 UILabel 需要重新绘制大小更改,它会触发 viewDidLayoutSubviews。这是 Swift 中的一个示例。

    import UIKit
    
    class ViewController: UIViewController {
    
        lazy var scrollView : UIScrollView = {
            let sv = UIScrollView(frame: self.view.bounds)
            sv.translatesAutoresizingMaskIntoConstraints = false
            sv.delegate = self
            return sv
        }()
    
        lazy var contentView : UIView = {
            let v = UIView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width * 4, height: self.view.bounds.height))
            v.translatesAutoresizingMaskIntoConstraints = false
            return v
        }()
    
        lazy var label : UILabel = {
            let lbl = UILabel(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width, height: 50))
            lbl.numberOfLines = 0
            lbl.text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s"
            lbl.minimumScaleFactor = 0.5
            lbl.adjustsFontSizeToFitWidth = true
            lbl.font = UIFont.systemFont(ofSize: 22)
            lbl.translatesAutoresizingMaskIntoConstraints = false
            return lbl
        }()
    
        var widthConstraint : NSLayoutConstraint?
        var heightConstraint : NSLayoutConstraint?
        var startingHeight : CGFloat = 0
        var startingWidth : CGFloat = 0
        override func viewDidLoad() {
            super.viewDidLoad()
            //first scrollview
            self.view.addSubview(scrollView)
            pinToAllSides(target: scrollView)
    
            //now content view
            self.scrollView.addSubview(contentView)
            contentView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 2).isActive = true
            contentView.heightAnchor.constraint(equalTo: self.scrollView.heightAnchor, multiplier: 1).isActive = true
            contentView.backgroundColor = .green
            pinToAllSides(target: contentView)
            scrollView.layoutIfNeeded()
    
            //now the label
            self.scrollView.addSubview(label)
            label.leadingAnchor.constraint(equalTo: self.scrollView.leadingAnchor, constant: 20).isActive = true
            label.topAnchor.constraint(equalTo: self.scrollView.topAnchor, constant: 60).isActive = true
            label.backgroundColor = .red
            widthConstraint = NSLayoutConstraint(item: label, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: self.view.bounds.width/2)
    
            heightConstraint = NSLayoutConstraint(item: label, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 300)
            if let wc = widthConstraint,
                let hc = heightConstraint{
                startingHeight = hc.constant
                startingWidth = wc.constant
                label.addConstraint(wc)
                label.addConstraint(hc)
            }
    
        }
    
        func pinToAllSides(target:UIView){
            guard let superview = target.superview else{
                return
            }
            target.leadingAnchor.constraint(equalTo: superview.leadingAnchor).isActive = true
            target.trailingAnchor.constraint(equalTo: superview.trailingAnchor).isActive = true
            target.topAnchor.constraint(equalTo: superview.topAnchor).isActive = true
            target.bottomAnchor.constraint(equalTo: superview.bottomAnchor).isActive = true
        }
    
        var hasHappenedOnce : Bool = false
        override func viewDidLayoutSubviews() {
            super.viewDidLayoutSubviews()
            if hasHappenedOnce == false{
                hasHappenedOnce = true
                self.scrollView.contentOffset = .zero
            }
        }
    }
    
    extension ViewController : UIScrollViewDelegate{
    
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            //hopefully it is laggy due to simulator but for the label i would ditch constraints myself
            self.widthConstraint?.constant = max(startingWidth, self.scrollView.contentOffset.x * 1.1 + startingWidth)
    
            let height = startingHeight - self.scrollView.contentOffset.x
            self.heightConstraint?.constant = height
            label.updateConstraints()
        }
    }
    

    【讨论】:

      【解决方案2】:

      问题出在这里:

      - (void)viewDidLayoutSubviews {
          [super viewDidLayoutSubviews];
      
          // Setting the ContentOffset will stop scrolling 
          [self.scrollView setContentOffset:CGPointMake(0, 0)];
      }
      

      滚动滚动视图时更改self.labelWidthConstraint.constant触发器 viewDidLayoutSubviews!因此,一旦您开始滚动,您的代码就会立即将 .contentOffset 重置为 0,0

      我不知道你为什么要打电话给setContentOffset,当然不是viewDidLayoutSubviews

      在删除viewDidLayoutSubviews 代码后进行快速测试...

      我在viewDidload 结束时调用了[self.scrollView setContentOffset:CGPointMake(0, 0)];(并且也在viewDidAppear 中尝试过)...滚动(和约束常量更新)继续正常工作。

      我还添加了一个在点击时调用[self.scrollView setContentOffset:CGPointMake(0, 0)]; 的按钮...滚动(和约束常量更新)继续正常工作。

      【讨论】:

      • 领先我 6 分钟哈哈。
      • @agibson007 - 这不止一次发生在我身上:)
      • 我会离开我的,因为它很快,但我会投票,因为你打败了我
      猜你喜欢
      • 2012-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多