iOS - Swift - UIImage 上的可点击区域
Posted
技术标签:
【中文标题】iOS - Swift - UIImage 上的可点击区域【英文标题】:iOS - Swift - Clickable Regions on a UIImage 【发布时间】:2018-11-03 19:38:37 【问题描述】:我正在做一个项目,我必须在代表人体的图像上的特定区域绘制点。在Interface Builder中,我设置了一个容器UIView,它占据了主视图的大部分垂直中心。在那个容器视图中,我放置了一个 UIImageView 并在 IB 中设置了图形。图形比 UIImageView 和容器 UIView 都大得多,更具体地说,它更高。 UIImageView 的 ContentMode 设置为 AspectFit,因为我希望图像不显示为大于容器。
代码创建了几个 CGRect 实例,这些实例是用户点击有意义的区域。当用户点击容器视图时,代码用于确定该点是否在某个区域内,如果是,则在该区域的中心绘制一个点。
问题是当我在某些模拟器上运行应用程序时,区域矩形不在图像上的正确位置。例如,当我在 iPhone X 上运行应用程序时,头部的矩形区域看起来不错。当我在 iPhone XR 上运行应用程序时,矩形区域位于头部左侧。
我使用坐标来定义区域矩形,这些矩形基于图像中的位置,例如人头。我觉得这不是正确的方法,因为图像的 ContentMode 的 AspectFit 很可能导致图像被缩放以保持纵横比。
底线是,无论图像如何缩放,我都希望矩形位于正确的位置和大小。不知道我的做法是否有意义,所以希望有一些建议可以提供更好的方法。
更新 1:UIImageView 固定在周围的 UIView 上,因此它的宽度和高度与容器一样大。由于图像比 UIImageView 更瘦,因此图像在其中居中显示。在附加的图像中,紫色背景是 UIImageView 显示最上面的 UIView 的背景颜色。
更新 2:我检查了宽度和高度的比例,发现它们不同。宽度比例因子为 1.36565656565,高度比例因子为 2.104。我尝试了使用 Sweeper 给出的两个比例因子给出的公式,但没有运气。
【问题讨论】:
【参考方案1】:你只需要做一些数学运算。
在原始图像上,确定用户可以点击的区域。记下它相对于图像的 x、y、w、h。
计算出图像在图像视图中缩小了多少。由于您说图像比图像视图高,因此图像的比例因子为imageViewHeight / imageHeight
。我们现在将其称为scaleFactor
。
该区域的 Y 坐标肯定也下降了scaleFactor
,因此您将regionY
乘以scaleFactor
得到newY
。
区域的宽度和高度将做同样的事情,因此将它们乘以scaleFactor
并得到newWidth
和newHeight
。
区域的 X 坐标,相对于图像视图,有点棘手。您需要通过缩小图像来考虑图像视图创建的空白空间量。此emptySpace
由(imageViewWidth - newWidth) / 2
计算得出。然后要计算新区域相对于图像视图的 X 坐标,请执行 emptySpace + X * scaleFactor
。
现在 rect (newX, newY, newWidth, newHeight)
是相对于用户可以点击的图像视图的区域!
【讨论】:
您提供的公式似乎适用于除 x 坐标以外的所有内容。我正在修改我的问题以反映 UIImageView 的设置方式。 @Moebius 你能用我的公式显示你创建的区域吗?此外,仅使用高度比例因子。您计算的宽度比例因子是错误的,因为图像视图的宽度与图像的缩放宽度不同。仅使用高度比例因子。 我确实使用了高度比例因子,但它太大了。与此同时,我已经切换到锁定 UIImageView 的宽度,这是导致问题的主要原因。 @Moebius 比例因子有多大并不重要。数学应该仍然有效。你能告诉我为什么它太大是个问题吗?【参考方案2】:我为每个设备上的位置完全相同的矩形制作了这段代码。 为了检查它是否是 x 手机,我在 viewDidLoad 中执行并将约束设置为我想要的并添加标签栏大小。
@IBOutlet weak var tabMenu: NSLayoutConstraint!
@IBOutlet weak var topConstraint: NSLayoutConstraint!
@IBOutlet weak var bottomConstraint: NSLayoutConstraint!
@IBOutlet weak var backgroundImg: UIImageView!
@IBOutlet weak var rectangleImg: UIImageView!
override func viewDidLoad()
super.viewDidLoad()
// Check if it's a iPhone X
if #available(ios 11, *)
let safeArea = UIApplication.shared.delegate?.window??.safeAreaInsets
// If its a x phone i use safe instead of superView
guard let safe = safeArea else return
if safe.bottom > 0
topConstraint.constant = safe.top
bottomConstraint.constant = safe.bottom + tabMenu.constant
override func viewDidAppear(_ animated: Bool)
// Here I set the rectangle to 36% of the width and height (change to what you want)
rectangleImg.frame = CGRect(x: 0, y: 0, width: backgroundImg.frame.width * 0.36,
height: backgroundImg.frame.height * 0.36)
// Last I put the rectangle in the center of background image
rectangleImg.center.x = backgroundImg.center.x
rectangleImg.center.y = backgroundImg.center.y
希望此代码可以提供帮助!
【讨论】:
【参考方案3】:我的问题的根源是我将 UIImageView 的四个侧面固定在其容器视图上。当设备的宽度发生变化时,它会正确缩放图像,但会导致图形被“拉伸”。例如,这导致头部的宽度增加。我现在通过锁定图像的宽度解决了这个问题,因此无论使用什么设备,我从原始图像中得出的坐标都保持不变。
对于那些阅读本文的人来说,这听起来可能不是一个很好的解决方案,但我必须接受这个,因为我的截止日期非常紧迫。我已经在多个设备模拟器上对其进行了测试,并且可以正常工作。我将来可能需要重新审视这个,但现在,它正在工作。
我也使用这个问题的答案来修改代码:create a clickable body diagram
【讨论】:
以上是关于iOS - Swift - UIImage 上的可点击区域的主要内容,如果未能解决你的问题,请参考以下文章
iOS Swift - 在 UIImage 上画线以保存到文件