如何相对于图像在背景图像上定位 UI 元素

Posted

技术标签:

【中文标题】如何相对于图像在背景图像上定位 UI 元素【英文标题】:How to position UI elements on background image relative to the image 【发布时间】:2018-08-13 10:03:50 【问题描述】:

我之前曾在这里提问How to make full screen background image inside ScrollView and keep aspect ratio 并从“ekscrypto”得到很好的回答,再次感谢您。

我在图像上有带有文本的按钮(1、2、3、4、5 等)以前我使用硬编码的 X 和 Y 坐标来定位背景图像上的 UI 元素(按钮),这个解决方案适用于所有iPhone 设备,而不是 iPad。 我根据从“ekscrypto”收到的解决方案更改了我的代码,现在这个解决方案当然不适用于任何设备。

图片上有一条路,我需要在这条路上安排这些按钮。无论设备和图像比例如何,我如何才能相对于图像正确定位此按钮?

附: Ekscrypto 也提供了 UI 元素定位的解决方案,但我不明白它是如何工作的。

这是我目前尝试创建按钮的方式:

let imageOne = UIImage(named: "level1") as UIImage?
let levelOne = UIButton(type: UIButtonType.system) 
levelOne.frame = CGRect.init(x: 10, y: 10, width: 100, height: 45)
levelOne.setImage(imageOne, for: .normal) 
scrollView.addSubview(levelOne)

但 iPhone 和 iPad 的按钮位置和大小应该不同。我怎样才能让它们相对于图像正确放置?

非常感谢 ekscrypto,很抱歉延迟回答。您的代码正在运行,并且解决了我的问题,但是有一个小问题。

    必须将这一行 let button = UIButton(type: .system) 更改为 .custom,或者将背景图像改为蓝色填充按钮。

    带有背景图片的按钮太大,特别是在 iPhone 5 上,将 let backgroundDesignHeight: CGFloat = 330.0: CGFloat = 330.0 改为 730 以使其更小

    iPhone 和 iPad 上的所有按钮都在同一个位置,除了«plus devices»,底部(向下)按钮的偏移量应该稍高

    在某些设备上,按钮上的背景图像有点模糊,这是在我将 backgroundDesignHeight 更改为 730 后发生的

【问题讨论】:

没有可以在这里提供帮助的 ios 人员? 由于您的 UI 是在代码中创建的,您能否展示您当前尝试创建按钮的方式? ekscrypto 感谢您的帮助。代码如下: let imageOne = UIImage(named: "level1") as UIImage? let levelOne = UIButton(type: UIButtonType.system) levelOne.frame = CGRect.init(x: 10, y: 10, width: 100, height: 45) levelOne.setImage(imageOne, for: .normal) scrollView.addSubview(一级) 我在下面添加了我的答案。让我知道是否需要进一步澄清或是否给您带来麻烦 如果您想避免图像模糊,您需要创建适合各种设备大小的自定义按钮图像。使用单个图像并在代码中调整它的大小有时看起来不太好。如果您的按钮只有一组图像,请将其放大,以便在缩放时可以使用更多数据。 【参考方案1】:

您可以通过以下几种方式解决此问题:

根据当前屏幕尺寸和“设计”尺寸手动计算最终 x/y 位置 手动创建基于纵横比的约束,让 iOS 为您计算最终位置

假设您的设备/应用程序方向是恒定的(始终为横向或始终纵向)并且始终为全屏,手动计算可能更容易。

首先,您需要一个辅助函数来调整图像大小:

private func scaledImage(named imageName: String, scale: CGFloat) -> UIImage? 
    guard let image = UIImage(named: imageName) else  return nil 
    let targetSize = CGSize(width: image.size.width * scale, height: image.size.height * scale)
    return resizeImage(image: image, targetSize: targetSize)


// Adapted from https://***.com/questions/31314412/how-to-resize-image-in-swift
private func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage? 
    let size = image.size

    let widthRatio  = targetSize.width  / size.width
    let heightRatio = targetSize.height / size.height

    // Figure out what our orientation is, and use that to form the rectangle
    var newSize: CGSize
    if(widthRatio > heightRatio) 
        newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)
     else 
        newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio)
    


    // This is the rect that we've calculated out and this is what is actually used below
    let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)

    // Actually do the resizing to the rect using the ImageContext stuff
    UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
    image.draw(in: rect)
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return newImage

接下来,我们将创建一个函数来计算按钮的位置,以及基于图像大小的按钮大小:

private func positionScaledButton(x designX: CGFloat, y designY: CGFloat, imageName: String) -> UIButton 

    // First, compute our "designScale"
    let screenHeight = UIScreen.main.bounds.size.height
    let backgroundDesignHeight: CGFloat = 330.0 // ** see below
    let designScale = screenHeight / backgroundDesignHeight


    // Create button
    let button = UIButton(type: .custom)

    // Get image to use, and scale it as required
    guard let image = scaledImage(named: imageName, scale: designScale) else  return button 

    button.frame = CGRect(x: designX * designScale, y: designY * designScale, width: image.size.width, height: image.size.height)
    button.setImage(image, for: .normal)
    scrollView.addSubview(button)
    return button

** 上面代码中的“330.0”值是指滚动条中背景图片的高度,从这里测量按钮的x/y坐标。

假设按钮的左上角应该在 x: 10, y: 10,对于图像“levelOne”:

let levelOneButton = positionScaledButton(x: 10, y: 10, imageName: "imageOne")
// To do: addTarget event handler

【讨论】:

我尝试摆脱模糊图像,为不同的设备创建不同的图像,还尝试了非常大的图像甚至矢量图像,不走运,图像仍然模糊。作为解决方法,我只是在创建按钮后更改了图像。 @dakath 如果您为每个设备使用自定义图像,您将希望摆脱 scaledImage 和 resizeImage 函数并在创建按钮时直接使用您的图像。 我无法正确设置 backgroundDesignHeight。我的背景图片高度为 1242 像素,并且 positionScaledButton 函数返回非常小的按钮(图片),特别是在 iPhone 5 上。你有什么解决方法的想法吗? 鉴于您用于背景的设计,我敢说是的,您的按钮在 iPhone 5 上会非常小。您可能必须为这些小型设备提供不同的背景图像,尽管我不再为 iPhone 5 设计;我认为 Apple 积极销售和支持的最小设备是 iPhone 6s。 不幸的是,在 iphone 6 和 iPad 上的按钮也太小了。什么是最简单的解决方案?也许最好只是让背景图像高度更小,对于 exp。小两倍?【参考方案2】:

使用相对点,而不是绝对点。

因此,不要说按钮 1 位于 (30, 150) 处,而是使用屏幕尺寸的分数,使其位于 (0.0369, 0.4) - 然后使用这些分数来创建您的自动布局约束。

【讨论】:

我不明白。能否请您更详细地解释一下?假设我们有一台屏幕尺寸为 414x736 点的 iPhone 6 plus。在 iPone 6 plus 上需要的地方有 30、150 个坐标点。 1. 如何获得相对积分? 2. 如何使用这个相对点来创建自动布局的约束? 简单除法 - 给定坐标的相对位置为 (30/414, 150/736) = (0.0725, 0.2038) 好的,如何使用相对坐标定位按钮? button1.centerXAnchor.constraint(equalTo: "等于什么?").isActive = true button1.centerYAnchor.constraint(equalTo: "等于什么?").isActive = true 提前致谢。 @dakath 不需要设置约束,设置位置即可

以上是关于如何相对于图像在背景图像上定位 UI 元素的主要内容,如果未能解决你的问题,请参考以下文章

当图像大小改变时相对于图像定位文本

web的定位,背景图

无法使用适用于 iOS 混合应用程序的 appium 检查作为背景图像存在的元素

React Native ScrollView 与相对于屏幕大小的绝对定位的子项

相对于其容器定位元素

在 Unity UI Builder 中动态更改视觉元素的背景图像