从iOS截图中移除状态栏

huangapple go评论101阅读模式
英文:

Removing statusbar from screenshot on iOS

问题

我试图通过裁剪来移除图像的顶部部分,但结果出乎意料。

使用的代码:

extension UIImage {
    class func removeStatusbarFromScreenshot(_ screenshot:UIImage) -> UIImage {
        let statusBarHeight = 44.0
        let newHeight = screenshot.size.height - statusBarHeight
        let newSize = CGSize(width: screenshot.size.width, height: newHeight)
        let newOrigin = CGPoint(x: 0, y: statusBarHeight)
        let imageRef:CGImage = screenshot.cgImage!.cropping(to: CGRect(origin: newOrigin, size: newSize))!
        let cropped:UIImage = UIImage(cgImage:imageRef)
        return cropped
    }
}

我的逻辑是需要将图像的高度减小44像素并将原点y坐标移动44像素,但最终只创建了一个远小于图像左上角的图像。

唯一使它按预期工作的方法是在newSize中将宽度乘以2,高度乘以2.5,但这也会使生成的图像大小加倍。这无论如何都不太合理。有人能帮助我让它正常工作,而不使用魔术值吗?

英文:

Im trying to remove the top part of an image by cropping, but the result is unexpected.
The code used:

extension UIImage {
    class func removeStatusbarFromScreenshot(_ screenshot:UIImage) -> UIImage {
        let statusBarHeight = 44.0
        let newHeight = screenshot.size.height - statusBarHeight
        let newSize = CGSize(width: screenshot.size.width, height: newHeight)
        let newOrigin = CGPoint(x: 0, y: statusBarHeight)
        let imageRef:CGImage = screenshot.cgImage!.cropping(to: CGRect(origin: newOrigin, size: newSize))!
        let cropped:UIImage = UIImage(cgImage:imageRef)
        return cropped
    }
}

My logic is that I need to make the image smaller in heigh by 44px and move the origin y by 44px, but it ends up only creating an image much smaller of the top left corner.

The only way that I get it to work as expected is by multiplying the width by 2 and height by 2.5 in newSize, but that also double the size of the image produced..
Which anyways doesnt make much sense.. can someone help make it work without using magic values?

答案1

得分: 2

以下是翻译好的内容:

有两个主要问题与您所做的内容相关:

  • UIImage 有一个“scale”(通常与您设备屏幕的分辨率相关),但 CGImage 没有。

  • 不同的设备具有不同的“状态栏”高度。一般来说,您要从顶部剪切的不是状态栏,而是“安全区域”的顶部。安全区域的顶部是您的内容开始的地方。

因此:

  • 您说到 44 个 px 是错误的。这里没有像素。像素是屏幕上的物理原子发光点。在代码中,有 points。点独立于缩放(而缩放是点和像素之间的 乘数)。

  • 您说到数字 44 本身 就好像它是硬编码的。您应该获取安全区域的顶部。

  • 在进入 CGImage 世界时,没有考虑缩放,因此丢失了缩放信息,因为 CGImage 对缩放一无所知。

  • 在返回 UIImage 世界时,没有考虑缩放,最终得到一个分辨率为 1 的 UIImage,这可能不是原始 UIImage 的分辨率。

最简单的解决方案不是执行您所做的任何操作。首先,获取安全区域的高度;将其命名为 h。然后,将快照图像绘制到与您的图像相同缩放的图形图像上下文中(如果您操作得当,它将自动完成),但是高度比您的图像短 h points,并在其 y 坐标上绘制为 -h,从而剪切掉安全区域。提取生成的图像,就完成了。

示例!此代码位于视图控制器中。首先,我将获取我的设备当前屏幕的截图(此视图控制器的视图):

let renderer = UIGraphicsImageRenderer(size: view.bounds.size)
let screenshot = renderer.image { context in
    view.layer.render(in: context.cgContext)
}

现在,我将从该截图的顶部剪切安全区域:

let h = view.safeAreaInsets.top
let size = screenshot.size
let r = UIGraphicsImageRenderer(
    size: .init(width: size.width, height: size.height - h)
)
let result = r.image { _ in
    screenshot.draw(at: .init(x: 0, y: -h))
}

实验将确认,这在 每个 设备上都完美运行,无论它是否有边框,无论其屏幕分辨率如何:生成的图像 result 的顶部是实际内容的顶部。

英文:

There are two main problems with what you're doing:

  • A UIImage has a scale (usually tied to resolution of your device's screen), but a CGImage does not.

  • Different devices have different "status bar" heights. In general, what you want to cut off from the top is not the status bar but the safe area. The top of the safe area is where your content starts.

Because of this:

  • You are wrong to talk about 44 px. There are no pixels here. Pixels are physical atomic illuminations on your screen. In code, there are points. Points are independent of the scale (and the scale is the multiplier between points and pixels).

  • You are wrong to talk about the number 44 itself as if it were hard-coded. You should get the top of the safe area instead.

  • By crossing into the CGImage world without taking scale into account, you lose the scale information, because CGImage knows nothing of scale.

  • By crossing back into the UIImage world without taking scale into account, you end up with a UIImage with a resolution of 1, which may not be the resolution of the original UIImage.

The simplest solution is not to do any of what you are doing. First, get the height of the safe area; call it h. Then just draw the snapshot image into a graphics image context that is the same scale as your image (which, if you play your cards right, it will be automatically), but is h points shorter than the height of your image — and draw it with its y origin at -h, thus cutting off the safe area. Extract the resulting image and you're all set.

Example! This code comes a view controller. First, I'll take a screenshot of my own device's current screen (this view controller's view) as my app runs:

let renderer = UIGraphicsImageRenderer(size: view.bounds.size)
let screenshot = renderer.image { context in
    view.layer.render(in: context.cgContext)
}

Now, I'll cut the safe area off the top of that screenshot:

let h = view.safeAreaInsets.top
let size = screenshot.size
let r = UIGraphicsImageRenderer(
    size: .init(width: size.width, height: size.height - h)
)
let result = r.image { _ in
    screenshot.draw(at: .init(x: 0, y: -h))
}

Experimentation will confirm that this works perfectly on every device, regardless of whether it has a bezel and regardless of its screen resolution: the top of the resulting image, result, is the top of your actual content.

huangapple
  • 本文由 发表于 2023年1月8日 21:39:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/75048196.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定