简体   繁体   English

将按钮约束到 UIImageView 内部的图像

[英]Constrain button to image inside UIImageView

I have a UIImageView and the user can set the image so the width/height varies.我有一个UIImageView ,用户可以设置图像,使width/height发生变化。 I would like add a "x"- button to the upper right corner but NOT from the UIImageView but from the actual image .我想在右上角添加一个“x” button ,但不是来自UIImageView ,而是来自实际image

Here is how it looks right now.这是它现在的样子。 The whole picture is the UIImageView , on the left you can see the image and on the right top corner you can see the button .整个图片是UIImageView ,左边可以看到image ,右上角可以看到button

在此处输入图像描述

This is how I constrain it at the moment:这就是我目前限制它的方式:

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

This is obviously wrong but is there a way to constrain it to the actual image ?这显然是错误的,但有没有办法将其限制在实际image中?

I put together a sample project that the repo is here .我整理了一个示例项目,repo 在这里

Basically you need to do a few things:基本上你需要做几件事:

  • Calculate both your UIImageView frame size along with the frame of the UIMage being displayed in scaledAspectFit .计算您的UIImageView帧大小以及在UIMage中显示的UIImageViewscaledAspectFit
  • Create two named constraints and dynamically reposition your button once you have its frame.创建两个命名约束并在您拥有其框架后动态地重新定位您的按钮。

For the first, you need to remember that the frames may not really be set until viewDidLayoutSubviews .首先,您需要记住,直到viewDidLayoutSubviews才真正设置框架。 I create a UIImageView extension that easily computes where the UIImage frame really is.我创建了一个UIImageView扩展,它可以轻松计算UIImage框架的真正位置。 (It's old but working code. I'm sure it can be improved.) (这是旧的但可以工作的代码。我相信它可以改进。)

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)
    }
}

For the second bullet you need to create two variables of type NSConstraint .对于第二个项目符号,您需要创建两个NSConstraint类型的变量。 I adapted my answer from two years ago for this:为此,我改编了两年前的答案

var btnTop:NSLayoutConstraint!
var btnTrailing:NSLayoutConstraint!

And in `viewDidLoad:在`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

Note that you need to code two lines for each constraint, I never figured out why, but if you try to add the isActive property with the actual constraint the compiler doesn't know the correct type of the variable.请注意,您需要为每个约束编写两行代码,我从来没有弄清楚为什么,但是如果您尝试将isActive属性与实际约束一起添加,编译器将不知道变量的正确类型。

Now, you tie all this together in viewDidLayoutSubviews :现在,您在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

Placing the button in the top left is much simpler - it took me a good 20 minutes to get the correct calculation to make it top right instead!将按钮放在左上角简单得多 - 我花了 20 分钟才得到正确的计算结果,而不是把它放在右上角!

In my test project I encapsulated this code in repositionCloseButton() , which would be called anytime the app displays a new image.在我的测试项目中,我将此代码封装在repositionCloseButton()中,应用程序显示新图像时都会调用该代码。 This should work in both portrait and landscape orientation, and both portrait and landscape images - positioning a 20x20 close button 10 points away from the top right of an image.这应该适用于纵向和横向,以及纵向和横向图像 - 将 20x20 关闭按钮放置在距离图像右上角 10 点的位置。

Use your image width to position the button.使用您的图像宽度为 position 按钮。 Get rough idea and smooth the UI获得粗略的想法并平滑 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)

    ])

结果

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM