约束布局更改后不触发 UIButton 操作

Posted

技术标签:

【中文标题】约束布局更改后不触发 UIButton 操作【英文标题】:UIButton action is not triggered after constraint layouts changed 【发布时间】:2018-08-24 11:03:33 【问题描述】:

我在故事板 ViewController 上有一个 UIButton。当我将数据加载到表单中并且布局发生显着变化时,按钮无法识别触摸动作。

我发现当按钮在滚动视图上可见时,如果填充了数据,则触摸操作有效。

如果数据太长,按钮一开始是不可见的,只是当它滚动到显示中时,触摸动作不起作用。

我正在检查按钮上方是否有东西,但什么也没有。我试过改变按钮的zPosition,没有解决问题。

可能是什么问题?

我已经从 UIScrollView 和 UIButton 制作了自定义类来检查触摸事件是如何触发的。它显示出相同的行为,这是显而易见的。如果按钮在开始时可见,则触发 UIButton 的 touchesBegan 事件。如果按钮向下移动并且在开始时不可见,则永远不会触发它,而是调用滚动视图的 touchesBegan。

根据我加载到页面中的数据大小,有时按钮在开始时是可见的,但表单仍然可以滚动一点。在这种情况下,按钮仍然可以工作,因此这种行为似乎不取决于滚动视图之前是否滚动,仅取决于按钮的初始可见性。

是否应该调用任何布局或显示刷新函数来设置按钮的行为?

如果填充的数据需要更大的空间,则确保内容视图调整大小以进行滚动的代码部分。

func fillFormWithData() 
    dispDescription.text = jSonData[0]["advdescription"]
    dispLongDescription.text = jSonData[0]["advlongdesc"]
    priceandcurrency.text = jSonData[0]["advprice"]! + " " + jSonData[0]["advpricecur"]!
    validitydate.text = jSonData[0]["advdate"]!
    contentview.layoutIfNeeded()
    let contentRect = CGRect(x: 0, y: 0, width: scrollview.frame.width, height: uzenetbutton.frame.origin.y+uzenetbutton.frame.height+50)

    contentview.frame.size.height = contentRect.size.height
    scrollview.contentSize = contentview.bounds.size

好的,所以另一个更新。我已将 contentview 背景着色为蓝色,并将滚动视图背景着色为白色。当我加载数据并调整布局约束的大小时,内容视图正在按预期调整大小,但是现在滚动视图正在底部。在我滚动视图后,它正在调整为适合屏幕的原始大小。现在,只有当我触摸后面的蓝色区域时,才能识别该按钮。在白色背景下,它不再被识别,所以滚动视图似乎隐藏了按钮。

【问题讨论】:

向我们展示将视图放在一起的代码和视图层次结构 【参考方案1】:

让我弄清楚,故事板中添加了按钮,它是一个 spritekit 项目?如果您使用的是 zPosition??为什么不通过助手编辑器将 UIButton 连接为 IBAction,然后该操作始终与按钮相关联。

你也可以用不同的方式来做

创建一个 SKLabelNode 并将其放在屏幕上您想要按钮的位置,然后为其命名为 myButton

override func touchesBegan(_ touches: Set<UITouch>, with event:
UIEvent?) 
 if let touch = touches.first 
    let location = touch.location(in: self)
    let tappedNodes = nodes(at: location)
      for node in tappedNodes 
        if node.name == "myButton" 
           // call your action here
        
    
  

编辑 1: 如果您通过应用程序或以编程方式添加任何视图,您也可以尝试自动调整 scrollView.content 的大小

    private func resizeScrollView()
    print("RESIZING THE SCROLLVIEW from \(scrollView.contentSize)")
    for view in scrollView.subviews 
        contentRect = contentRect.union(view.frame)
    
    scrollView.contentSize = CGSize(width: contentRect.size.width, height: contentRect.size.height + 150)
    print("THE CONTENT SIZE AFTER RESIZING IS: \(scrollView.contentSize)")

编辑 2:我想我发现您的项目存在问题。您需要在对象检查器中将 MessageButton(UzenetButton) 移动到 DispDescription 标签上方,这样它将始终位于消息 textView 上方。 目前 UzeneButton 在您的视图层次结构中非常靠后,因此如果您的 textView 在编辑时调整大小,它会覆盖按钮,这就是您无法单击它的原因。

【讨论】:

这是一个简单的 UI 项目,而不是 spritekit 项目,但我猜测问题可能与 spritekit 项目相同。 IBAction 已创建,但也未触发。我想当我使用 layoutIfNeeded() 函数重新计算滚动视图的内容视图大小时会发生一些事情。如果内容视图大小小于或等于原始大小,则按钮获取调用。如果比滚动视图的 touchesBegan 更大,则调用初始 contentsize,并且不会触发 IBAction。 我只是想了解本机解决问题背后的逻辑,但我无法找出导致问题的原因。当然,通过类似的解决方案,我可以将控制权交给按钮,因为我已经尝试过了,但我想要一个防弹解决方案。按钮应按设计工作。所以需要一个没有变通方法的明确解决方案。 您是否将按钮放入滚动视图,因为滚动视图可能位于按钮上方,这可能是问题 是的,我把它放在故事板编辑器的滚动视图上。我的猜测是一样的,但是如何将 UIButton 放回滚动视图上方。我试图在 UIButton 中使用 bringSubview(toFront: ) 函数,但它没有改变任何东西。 我添加了我的故事板的屏幕截图和调整内容视图大小的数据填充代码。【参考方案2】:

见@Endre Olah,

    为了让情况更清楚,再做一件事,将 contentviewclipToBound 属性设置为 true。 您会注意到,在加载数据后,您的按钮不完全可见,这意味着它正在将其 parentView (ContentView) 移出界限 这就是为什么按钮没有被您触摸的原因。但是,如果您仔细触摸按钮的上部,它仍然可以正常工作。因为上半部分还在ContentViewbound 解决方案: 加载数据后,您必须确保增加 ContentView 的高度,以便 button 永远不会 超出其 的界限父视图(内容视图)

举例

@IBOutlet var heightConstraintOfContentView : NSLayoutConstraint!

加载数据后

let contentRect = CGRect(x: 0, y: 0, width: scrollview.frame.width, height: uzenetbutton.frame.origin.y+uzenetbutton.frame.height+50)
heightConstraintOfContentView.constant = contentRect.size.height
contentView.layoutIfNeeded()

【讨论】:

这是我的情况,我在上面解释过。由于滚动将内容视图大小设置回初始大小,因此内容大小调整还不够。 所以,我认为你不应该直接改变 contentView 的高度。但是,您应该创建一个 ContentView 高度约束的 IBOutlet,然后在加载数据后相应地更改其常量属性。我正在用一些示例代码编辑我的答案。 是的,这就是我在回答中所写的解决方案。只需阅读标记的答案。【参考方案3】:

当我需要使用带有动态内容的滚动视图时,我使用以下步骤:

1) 首先添加一个滚动视图,其 top、bottom、trailingleading0 到超级视图。

2) 将 view 添加到 scrollView 并将 view 的尾随、前导底部和顶部空间添加到 scrollView 可以设置为 0(或者您可以选择添加边距)。

3) 现在,您应该添加 UI 元素,例如按钮、带有适当顶部、底部、尾随和前导边距的标签。

4) 最后,用Safe Areaview添加等高和等宽约束:

view的等高优先级改为250

UIScrollView应该可以解决你的问题。

【讨论】:

这是为了解决问题,使其始终与保险箱尺寸相同,但我的问题不同。我不得不动态调整内容视图的大小。但是一旦我滚动了视图,它就会恢复到原始大小,即使内容大小要大得多。如果我不需要与内容进行交互,那将不是问题。在这种情况下是一个按钮。【参考方案4】:

最后,我在另一个链中找到了解决方案,一旦明确滚动视图的内容视图正在将滚动事件调整为原始大小。 (不清楚为什么会这样,但事实就是如此。)

所以我不得不在情节提要的内容视图中添加一个高度约束,并为其创建一个出口,并在内容大小发生变化时调整此约束,如下所示:

@IBOutlet weak var ContentViewHeight: NSLayoutConstraint!

func fillFormWithData() 
    dispDescription.text = jSonData[0]["advdescription"]
    dispLongDescription.text = jSonData[0]["advlongdesc"]
    priceandcurrency.text = jSonData[0]["advprice"]! + " " + jSonData[0]["advpricecur"]!
    validitydate.text = jSonData[0]["advdate"]!
    contentview.layoutIfNeeded()
    let contentRect = CGRect(x: 0, y: 0, width: scrollview.frame.width, height: uzenetbutton.frame.origin.y+uzenetbutton.frame.height+50)

    contentview.bounds = contentRect
    scrollview.contentSize = contentRect.size

    ----------- This is the key line to the success ----------
    ContentViewHeight.constant = contentRect.size.height
    ----------------------------------------------------------

这个添加之后就完美了。

【讨论】:

我想我发现了你的项目的问题。您需要在对象检查器中将 MessageButton(UzenetButton) 移动到 DispDescription 标签上方,这样它将始终位于您的消息 textView 上方

以上是关于约束布局更改后不触发 UIButton 操作的主要内容,如果未能解决你的问题,请参考以下文章

旋转后不采用 Swift 约束

使用自动布局约束在 UIScrollView 内为 UIView 设置动画

iOS:如何在其附件视图更改时触发 tableViewCell 的约束/自动布局刷新?

同一行中的 UIButton 的自动布局约束

UICollectionView 上的动画自动布局约束更改

UITableViewController 表头添加一个带有自动布局约束的 UIButton