[英]Resizing a multi line UILabel within a UIScrollView breaks scrolling. Why?
I use the following setup: 我使用以下设置:
UIScrollView
with Top, Leading, Trailing and Bottom constraints to match the VCs size UIScrollView
具有顶部,前导,尾随和底部约束以匹配VC的大小 UIView
to define the content size of the scroll view. UIView
用于定义滚动视图的内容大小。 It has the same height as the ScrollView but twice its width. UILabel
with some long text with a Height and Width constraint to set a fixed size and a Top and Leading constraint to the ScrollView to set a fixed position. UILabel
带有一些带有Height和Width约束的长文本,用于设置固定大小,以及对ScrollView设置Top和Leading约束以设置固定位置。 Problem: If the Label is set to use more than one line AND the ScrollViews contenOffset
property is set manually the ScrollView stops scrolling. 问题:如果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);
}
Resizing the label using this code works fine if 如果以下情况,使用此代码调整标签大小可以正常工作
(0, 0)
). (0, 0)
)。 In this case setting the label to multi line does NOT do any harm Setting the content offset AND using multi line at the same time DOES NOT work. 设置内容偏移并采用多线同时不起作用 。 The scroll cannot be scrolled any more.
滚动无法再滚动。
Why is this? 为什么是这样? Any idea what might cause this and how to solve it?
任何想法可能导致此问题以及如何解决?
The problem is here: 问题在这里:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
// Setting the ContentOffset will stop scrolling
[self.scrollView setContentOffset:CGPointMake(0, 0)];
}
Changing self.labelWidthConstraint.constant
when scrolling the scroll view triggers viewDidLayoutSubviews
! 滚动滚动视图时更改
self.labelWidthConstraint.constant
会触发 viewDidLayoutSubviews
! So, as soon as you start scrolling, your code immediately resets .contentOffset
to 0,0
. 因此,一旦开始滚动,您的代码就会立即将
.contentOffset
重置为0,0
。
I don't know why you would want to be calling setContentOffset
anyway, certainly not in viewDidLayoutSubviews
. 我不知道为什么您无论如何都要调用
setContentOffset
,当然不可以在viewDidLayoutSubviews
调用。
Doing a quick test, after removing the viewDidLayoutSubviews
code... 删除
viewDidLayoutSubviews
代码后,进行快速测试...
I called [self.scrollView setContentOffset:CGPointMake(0, 0)];
我叫
[self.scrollView setContentOffset:CGPointMake(0, 0)];
at the end of viewDidload
(and also tried in viewDidAppear
)... the scrolling (and constraint constant updating) continues to work fine. 在
viewDidload
的末尾(并在viewDidAppear
也尝试过)...滚动(和约束常量更新)继续正常进行。
I also added a button that calls [self.scrollView setContentOffset:CGPointMake(0, 0)];
我还添加了一个调用
[self.scrollView setContentOffset:CGPointMake(0, 0)];
的按钮[self.scrollView setContentOffset:CGPointMake(0, 0)];
when tapped... the scrolling (and constraint constant updating) continues to work fine. 点击时...滚动(和约束常量更新)继续正常工作。
The issue appears to be that when the label constraints are changed it triggers viewDidLayoutSubviews which then sets the UIScrollView to not scroll since set contentOffset is then called over and over. 问题似乎是,当更改标签约束时,它会触发viewDidLayoutSubviews,然后将UIScrollView设置为不滚动,因为随后会反复调用set contentOffset。 You could overcome this if you are only wanting to set the UIScrollView to CGPoint.zero on the initial layout by using a bool as a flag.
如果您只想通过使用bool作为标志在初始布局上将UIScrollView设置为CGPoint.zero,则可以克服此问题。 Apparently since UILabel needs a redraw on size changes it triggers viewDidLayoutSubviews.
显然,由于UILabel需要重新绘制尺寸更改,因此会触发viewDidLayoutSubviews。 Here is an example in Swift.
这是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()
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.