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 并得到newWidthnewHeight

区域的 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 上的可点击区域的主要内容,如果未能解决你的问题,请参考以下文章

Swift(iOS)将UIImage数组插入子视图?

Swift iOS 为 UIImage 添加隐形边框

swift ios数据持久化UIImage

iOS Swift - 在 UIImage 上画线以保存到文件

Swift 3 ios:当图像出现时,UILabel 需要出现在 UIImage 之上

UIImage 不使用 Swift 显示