[英]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:基本上你需要做几件事:
UIImageView
frame size along with the frame of the UIMage
being displayed in scaledAspectFit
.计算您的UIImageView帧大小以及在UIMage
中显示的UIImageView
的scaledAspectFit
。 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.