将按钮限制为 UIImageView 内的图像

Posted

技术标签:

【中文标题】将按钮限制为 UIImageView 内的图像【英文标题】:Constrain button to image inside UIImageView 【发布时间】:2020-04-12 18:12:41 【问题描述】:

我有一个UIImageView,用户可以设置图像,以便width/height 变化。我想在右上角添加一个“x”-button,但不是来自UIImageView,而是来自实际的image

这是它现在的样子。整张图是UIImageView,左边可以看到image,右上角可以看到button

这是我目前的约束方式:

theStackView.addArrangedSubview(self.imageView)
imageContainerView.addSubview(wishImageView)
imageContainerView.addSubview(deleteImageButton)

imageContainerView.heightAnchor.constraint(equalToConstant: 60).isActive = true
imageContainerView.isHidden = true

wishImageView.leadingAnchor.constraint(equalTo: imageContainerView.leadingAnchor, constant: 20).isActive = true
wishImageView.topAnchor.constraint(equalTo: imageContainerView.topAnchor, constant: 3).isActive = true
wishImageView.bottomAnchor.constraint(equalTo: imageContainerView.bottomAnchor, constant: 3).isActive = true
wishImageView.trailingAnchor.constraint(equalTo: imageContainerView.trailingAnchor, constant: -20).isActive = true

deleteImageButton.widthAnchor.constraint(equalToConstant: 10).isActive = true
deleteImageButton.heightAnchor.constraint(equalToConstant: 10).isActive = true
deleteImageButton.topAnchor.constraint(equalTo: wishImageView.topAnchor, constant: 5).isActive = true
deleteImageButton.trailingAnchor.constraint(equalTo: wishImageView.trailingAnchor, constant: -5).isActive = true

这显然是错误的,但有没有办法将其限制为实际的image

【问题讨论】:

这个 UIImageView 的大小是固定的吗? @DilanAnuruddha 是的 @DilanAnuruddha 但里面的图片可以改变 你的 UIImageView contentMode 是什么? @DilanAnuruddha 离开 【参考方案1】:

我整理了一个示例项目,repo 是here。

基本上你需要做几件事:

计算两者您的UIImageView 帧大小以及UIMage 的帧在scaledAspectFit 中显示。 创建两个命名约束,并在获得按钮框架后动态重新定位按钮。

首先,您需要记住,直到viewDidLayoutSubviews 才能真正设置框架。我创建了一个UIImageView 扩展,可以轻松计算UIImage 框架的真正位置。 (这是旧的但可以工作的代码。我相信它可以改进。)

extension UIImageView 
    public var scaleFactor:CGFloat 
        guard let image = self.image, self.frame != CGRect.zero  else 
            return 0.0
        

        let frame = self.frame
        let extent = image.size
        let heightFactor = frame.height/extent.height
        let widthFactor = frame.width/extent.width

        if extent.height > frame.height || extent.width > frame.width 
            if heightFactor < 1 && widthFactor < 1 
                if heightFactor > widthFactor 
                    return widthFactor
                 else 
                    return heightFactor
                
             else if extent.height > frame.height 
                return heightFactor
             else 
                return widthFactor
            
         else if extent.height < frame.height && extent.width < frame.width 
            if heightFactor < widthFactor 
                return heightFactor
             else 
                return widthFactor
            
         else 
            return 1
        
    

    public var imageSize:CGSize 
        if self.image == nil 
            return CGSize.zero
         else 
            return CGSize(width: (self.image?.size.width)!, height: (self.image?.size.height)!)
        
    

    public var scaledSize:CGSize 
        guard let image = self.image, self.frame != CGRect.zero  else 
            return CGSize.zero
        
        let factor = self.scaleFactor
        return CGSize(width: image.size.width * factor, height: image.size.height * factor)
    

对于第二个项目符号,您需要创建两个 NSConstraint 类型的变量。为此,我改编了两年前的my answer:

var btnTop:NSLayoutConstraint!
var btnTrailing:NSLayoutConstraint!

在`viewDidLoad:

button.heightAnchor.constraint(equalToConstant: 20).isActive = true
button.widthAnchor.constraint(equalToConstant: 20).isActive = true

btnTop = button.topAnchor.constraint(equalTo: imageView.topAnchor, constant: 10)
btnTop.isActive = true
btnTrailing = button.trailingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: -10)
btnTrailing.isActive = true

请注意,您需要为每个约束编写两行代码!我从来不知道为什么,但是如果你尝试添加带有实际约束的 isActive 属性,编译器将不知道变量的正确类型。

现在,您将所有这些都放在viewDidLayoutSubviews 中:

let scaledSize = imageView.scaledSize
var imageFrame = CGRect(origin: CGPoint.zero, size: scaledSize)
if scaledSize.width == imageView.frame.width 
    // image fills view along width, calculate Y constant
    imageFrame.origin.y = (imageView.frame.height - scaledSize.height) / 2
 else 
    // image fills view along height, calculate X constant
    imageFrame.origin.x = (imageView.frame.width - scaledSize.width) / 2

//btnTop.constant = imageFrame.width - 30
btnTop.constant = imageFrame.origin.y + 10
btnTrailing.constant = ((imageView.frame.width - imageFrame.width - imageFrame.origin.x) * -1) - 10

将按钮放在左上方要简单得多 - 我花了整整 20 分钟才得到正确的计算结果,而不是让它位于右上方!

在我的测试项目中,我将此代码封装在repositionCloseButton() 中,应用程序显示新图像时都会调用该代码。这应该适用于纵向和横向,以及纵向和横向图像 - 将 20x20 关闭按钮放置在距离图像右上角 10 个点的位置。

【讨论】:

【参考方案2】:

使用您的图像宽度来定位按钮。粗略构思并平滑 UI

 let imgWidth = iv.image?.size.width

    NSLayoutConstraint.activate([
        iv.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        iv.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        iv.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
        iv.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),

        btn.widthAnchor.constraint(equalToConstant: 20),
        btn.heightAnchor.constraint(equalToConstant: 20),
        btn.leadingAnchor.constraint(equalTo: iv.leadingAnchor , constant: (imgWidth ?? 0) > iv.frame.width ? iv.frame.width-20 : imgWidth ?? 0),
 //The issue happen when Image width is larger than the your imageview.Validate it for better result.
        btn.topAnchor.constraint(equalTo: iv.topAnchor,constant: 10)

    ])

【讨论】:

感谢您的回答,可能也是正确的,但我同意@dfd 的想法

以上是关于将按钮限制为 UIImageView 内的图像的主要内容,如果未能解决你的问题,请参考以下文章

UIButton 内的 UIImageView 具有默认填充 - 如何删除?

使用叠加按钮为 UIImageView 设置动画

在 Swift 中使用 AlamofireImage 将 GIF 图像转换为 UIImageView

在 UITableView 内的 UIImageView 中从 Web 加载图像

如何将 UIImageView.image 设置为 UIBarButtonItem

Swift:IBAction 在单击按钮后将图像放入 UIImageView