如何移动uibutton来为小型门腾出空间?

发布于 2025-02-06 08:05:58 字数 103 浏览 4 评论 0原文

我有一个snackbar,有时会出现在我的视图控制器上。但是当出现时,它会阻止已经存在的Uibutton。如何移动按钮以适应Snackbar,而无需硬编码移动按钮的位置?

谢谢!

I have a snackbar that appears at times on my view controller. But when it appears it blocks the UIButton that already exists. How do I shift the button to accommodate for the snackbar without hardcoding moving the position of the button?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

じ违心 2025-02-13 08:05:58

您可以通过将按钮的底部约束取决于Snackbar容器的顶部约束来做到这一点(如果Snackbar从上方降下)。当什么都没发生时,Snackbar容器高度为零,并且在显示Snackbar时会展开。

但是,我会提出另一种方法。 Snackbars通常不会取代UI,我想用户“错过”按钮会感到沮丧,因为该应用显示Snackbar。另请参见材料设计指南这些事情如何工作。 (它们不是iOS的本地,因此据我所知,苹果没有任何文件)。

You could probably do this by having the button's bottom constraint depend on the snackbar container's top constraint (or vice versa, if the snackbar comes down from above). The snackbar container height is zero when nothing happens, and expands when a snackbar is displayed.

I would, however, suggest a different approach. Snackbars don't usually displace UI, and I guess it would be frustrating for users to "miss" the button because the app displays a snackbar. See also the Material Design Guidelines for how these things might work. (They aren't native to iOS, so Apple has no documentation as far as I know).

静谧 2025-02-13 08:05:58

这可以通过为按钮的底部约束声明两个不同的nslayoutconstraints,一个基于安全区域(或最初相对于您的按钮定位),而另一个基于Snackbar的位置。然后,通过设置约束属性的属性 false 和true之间,实际使用哪个约束。

我已经在此处确认这有效:

import UIKit

class ViewController: UIViewController {
    // Connect an outlet to the button to allow adding the new constraint
    // to it programatically
    @IBOutlet weak var movingButton: UIButton!
    
    // This is the bottom constraint of the UIButton which will change based on whether or not
    // the snackbar is open.  It was set up in Main.storyboard with parameters as follows:
    // -- First Item:   Button.Bottom
    // -- Relation:     Equal
    // -- Second Item:  Safe Area.Bottom
    // -- Constant:     0
    // -- Priority:     1000
    // -- Multiplier:   1
    @IBOutlet var buttonBottomConstraint: NSLayoutConstraint!
    // Note:  do not use 'weak var' for this constraint, as this would cause a nil exception as the
    // constraint would get deleted as soon as its .isActive property is set to false inside toggleSnackbar
    
    // Will be determined by the snackbar at its initialization
    var newButtonBottomConstraint: NSLayoutConstraint?
    
    // Will be set inside viewDidLayoutSubviews
    var screenWidth: Double?
    var screenHeight: Double?
    
    var snackbar: UILabel?
    var snackbarIsOpen = false
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        if screenWidth == nil {
            // These will depend on the device (iPhone, iPad etc.)
            screenWidth = view.safeAreaLayoutGuide.layoutFrame.width;
            screenHeight = view.safeAreaLayoutGuide.layoutFrame.height;
        }
        // Only instantiate snackbar once, not every time
        // viewDidLayoutSubviews is called.
        if snackbar == nil {
            snackbar = UILabel(frame: CGRect(x: 0.1*screenWidth!, y: 0.95*screenHeight!, width: 0.8*screenWidth!, height: 0.1*screenHeight!))
            snackbar?.backgroundColor = .systemGray
            snackbar?.textColor = .white
            snackbar?.numberOfLines = 2
            snackbar?.textAlignment = .center
            snackbar?.font = UIFont.systemFont(ofSize: 18)
            snackbar?.text = "When this appears, the button will move upwards."
            
            // This constraint will replace movingButton's IBOutlet constraint when the snackbar is open
            newButtonBottomConstraint = NSLayoutConstraint(item: movingButton!, attribute: .bottom, relatedBy: .equal, toItem: snackbar!, attribute: .top, multiplier: 1, constant: -10)
        }
    }

    @IBAction func toggleSnackbar(_ sender: UIButton) {
        // Close the snackbar if it's already open
        if snackbarIsOpen {
            snackbar?.removeFromSuperview()
            sender.setTitle("Press For Snackbar", for: .normal)
            snackbarIsOpen = false
            
            movingButton.setTitle("Will Move", for: .normal)
            // Swap which constraint is active:  back to original storyboard value
            newButtonBottomConstraint?.isActive = false
            buttonBottomConstraint.isActive = true
        } else {
            // Open the snackbar if it isn't already open
            view.addSubview(snackbar!)
            sender.setTitle("Dismiss Snackbar", for: .normal)
            snackbarIsOpen = true
            
            movingButton.setTitle("Did Move", for: .normal)
            // Swap which constraint is active:  use new constraint based on snackbar's position
            buttonBottomConstraint.isActive = false
            newButtonBottomConstraint?.isActive = true
        }
    }
}

附加详细信息:按钮设置在Main.Storyboard中,除了相对于Snackbar的限制外,所有约束也是如此。我在代码示例的注释中包括了与示例相关的接口构建器值(按钮的底部约束)。

This can be done by declaring two different NSLayoutConstraints for your button's bottom constraint, one based on the safe area (or whatever your button is originally positioned relative to), and another based on the position of the snackbar. Then, toggle between which constraint is actually used by setting the respective .isActive properties of the constraints to false and true.

I have confirmed that this works in the example here:

import UIKit

class ViewController: UIViewController {
    // Connect an outlet to the button to allow adding the new constraint
    // to it programatically
    @IBOutlet weak var movingButton: UIButton!
    
    // This is the bottom constraint of the UIButton which will change based on whether or not
    // the snackbar is open.  It was set up in Main.storyboard with parameters as follows:
    // -- First Item:   Button.Bottom
    // -- Relation:     Equal
    // -- Second Item:  Safe Area.Bottom
    // -- Constant:     0
    // -- Priority:     1000
    // -- Multiplier:   1
    @IBOutlet var buttonBottomConstraint: NSLayoutConstraint!
    // Note:  do not use 'weak var' for this constraint, as this would cause a nil exception as the
    // constraint would get deleted as soon as its .isActive property is set to false inside toggleSnackbar
    
    // Will be determined by the snackbar at its initialization
    var newButtonBottomConstraint: NSLayoutConstraint?
    
    // Will be set inside viewDidLayoutSubviews
    var screenWidth: Double?
    var screenHeight: Double?
    
    var snackbar: UILabel?
    var snackbarIsOpen = false
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        if screenWidth == nil {
            // These will depend on the device (iPhone, iPad etc.)
            screenWidth = view.safeAreaLayoutGuide.layoutFrame.width;
            screenHeight = view.safeAreaLayoutGuide.layoutFrame.height;
        }
        // Only instantiate snackbar once, not every time
        // viewDidLayoutSubviews is called.
        if snackbar == nil {
            snackbar = UILabel(frame: CGRect(x: 0.1*screenWidth!, y: 0.95*screenHeight!, width: 0.8*screenWidth!, height: 0.1*screenHeight!))
            snackbar?.backgroundColor = .systemGray
            snackbar?.textColor = .white
            snackbar?.numberOfLines = 2
            snackbar?.textAlignment = .center
            snackbar?.font = UIFont.systemFont(ofSize: 18)
            snackbar?.text = "When this appears, the button will move upwards."
            
            // This constraint will replace movingButton's IBOutlet constraint when the snackbar is open
            newButtonBottomConstraint = NSLayoutConstraint(item: movingButton!, attribute: .bottom, relatedBy: .equal, toItem: snackbar!, attribute: .top, multiplier: 1, constant: -10)
        }
    }

    @IBAction func toggleSnackbar(_ sender: UIButton) {
        // Close the snackbar if it's already open
        if snackbarIsOpen {
            snackbar?.removeFromSuperview()
            sender.setTitle("Press For Snackbar", for: .normal)
            snackbarIsOpen = false
            
            movingButton.setTitle("Will Move", for: .normal)
            // Swap which constraint is active:  back to original storyboard value
            newButtonBottomConstraint?.isActive = false
            buttonBottomConstraint.isActive = true
        } else {
            // Open the snackbar if it isn't already open
            view.addSubview(snackbar!)
            sender.setTitle("Dismiss Snackbar", for: .normal)
            snackbarIsOpen = true
            
            movingButton.setTitle("Did Move", for: .normal)
            // Swap which constraint is active:  use new constraint based on snackbar's position
            buttonBottomConstraint.isActive = false
            newButtonBottomConstraint?.isActive = true
        }
    }
}

Additional details: The buttons were set up in Main.storyboard, as were all constraints except for the one which is relative to the snackbar. I included the Interface Builder values used for the one relevant to the example (button's bottom constraint) in the comments in the code sample.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文