如何根据设备的高度和宽度缩放 UIImage 以适合屏幕
How to scale a UIImage to fit screen based on height and width of device
我有一个 UIScrollView
,里面有一个 UIImage
,我从我的服务器上得到的。每个图像的 width
大于 height
或 height
大于其 width
。
我想根据 height
或 width
以适合我的设备屏幕的方式缩放图像
例如:
我从服务器获取图像并检查哪个更大,width
或 height
。
if let width = image?.size.width, let height = image?.size.height {
if width > height {
print("Width is greater than height")
}else
print("Height is greater than width")
}
}
执行此操作后,我想以这两种方式之一缩放图像。
选项 1:
如果 width
大于 height
那么我希望根据固定到设备高度和宽度的 height
来缩放图像,并且宽度应该是 scrollable
查看。
选项 2:
如果 height
大于 width
那么我希望根据固定到设备宽度和高度的 width
来缩放图像,并且高度应该是 scrollable
查看。
我已经设法让 Option 2
使用此功能。
func scaleImageWidth(sourceImage:UIImage, scaledToWidth: CGFloat) -> UIImage {
let oldWidth = sourceImage.size.width
let scaleFactor = scaledToWidth / oldWidth
let newHeight = sourceImage.size.height * scaleFactor
let newWidth = oldWidth * scaleFactor
UIGraphicsBeginImageContext(CGSize(width:newWidth, height:newHeight))
sourceImage.draw(in: CGRect(x:0, y:0, width:newWidth, height:newHeight))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
我修改了函数以适用于 Option 1
,如下所示:
func scaleImageHeight(sourceImage: UIImage, scaledToHeight: CGFloat) -> UIImage {
let oldheight: CGFloat = sourceImage.size.height
let scaleFactor: CGFloat = scaledToHeight / oldheight
let newWidth: CGFloat = sourceImage.size.width * scaleFactor
let newHeight: CGFloat = oldheight * scaleFactor
UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight))
sourceImage.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
现在这两个功能都可以正常工作,但在这两种情况下,在某些情况下缩放会导致图像缩放小于设备的 width
或 height
。
下面是缩放出错时的一些图片。
实际结果:
预期结果:
已编辑:
这里有一些图像,它们的 height
和 width
这些功能可以正常工作。
情况一:当高度大于宽度时
- 宽度:1080.0,高度:2280.0
- 宽度:1080.0,高度:2160.0
- 宽度:1083.0,高度:1920.0
- 宽度:1080.0,高度:2160.0
情况二:当宽度大于高度时
- 宽度:2715.0,高度:1920.0
- 宽度:1945.0,高度:1920.0
- 宽度:2278.0,高度:1920.0
这里有一些图像,其 height
和 width
这些函数会产生上述问题。
情况一:当高度大于宽度时
- 宽度:1300.0,高度:1920.0
- 宽度:1143.0,高度:1920.0
- 宽度:1281.0,高度:1920.0
情况二:当宽度大于高度时
我没有注意到宽度大于高度时的任何问题。在这种情况下,它总是会产生正确的结果。
将 UIImageView 约束设置为顶部、左侧、右侧、底部。
现在转到 UIIMageView 的属性检查器并将内容模式 属性 设置为缩放以填充。
在这种情况下,某些图像可能会被拉伸。
我认为问题可能在于您正在比较图像的宽度:高度,但您没有考虑 imageView 的 aspect-ratio。
这是一个 CGSize
扩展,用于根据 "source" 尺寸(图像尺寸)和 [=32] 计算 aspectFit
或 aspectFill
尺寸=] 大小(imageView 大小)。
extension CGSize {
func aspectFit(sourceSize: CGSize, targetSize: CGSize) -> CGSize {
let vWidth = targetSize.width / sourceSize.width;
let vHeight = targetSize.height / sourceSize.height;
var returnSize = targetSize
if( vHeight < vWidth ) {
returnSize.width = targetSize.height / sourceSize.height * sourceSize.width;
}
else if( vWidth < vHeight ) {
returnSize.height = targetSize.width / sourceSize.width * sourceSize.height;
}
return returnSize;
}
func aspectFill(sourceSize: CGSize, targetSize: CGSize) -> CGSize {
let vWidth = targetSize.width / sourceSize.width;
let vHeight = targetSize.height / sourceSize.height;
var returnSize = targetSize
if( vHeight > vWidth ) {
returnSize.width = targetSize.height / sourceSize.height * sourceSize.width;
}
else if( vWidth > vHeight ) {
returnSize.height = targetSize.width / sourceSize.width * sourceSize.height;
}
return returnSize;
}
}
对于您的情况,您想要 aspectFill
尺码。
这是一个简单、完整的示例,将原始计算的输出与 aspectFill
计算的输出进行比较。我使用了您添加到问题中的图像尺寸,以及 375 x 667
的 imageView 尺寸(full-screen on an iPhone 7):
extension CGSize {
func aspectFit(sourceSize: CGSize, targetSize: CGSize) -> CGSize {
let vWidth = targetSize.width / sourceSize.width;
let vHeight = targetSize.height / sourceSize.height;
var returnSize = targetSize
if( vHeight < vWidth ) {
returnSize.width = targetSize.height / sourceSize.height * sourceSize.width;
}
else if( vWidth < vHeight ) {
returnSize.height = targetSize.width / sourceSize.width * sourceSize.height;
}
return returnSize;
}
func aspectFill(sourceSize: CGSize, targetSize: CGSize) -> CGSize {
let vWidth = targetSize.width / sourceSize.width;
let vHeight = targetSize.height / sourceSize.height;
var returnSize = targetSize
if( vHeight > vWidth ) {
returnSize.width = targetSize.height / sourceSize.height * sourceSize.width;
}
else if( vWidth > vHeight ) {
returnSize.height = targetSize.width / sourceSize.width * sourceSize.height;
}
return returnSize;
}
}
class TestViewController: UIViewController {
let testSizes: [CGSize] = [
// width > height
CGSize(width: 2715.0, height: 1920.0),
CGSize(width: 1945.0, height: 1920.0),
CGSize(width: 2278.0, height: 1920.0),
// used to provide a blank line in debug output
CGSize.zero,
// height > width
CGSize(width: 1080.0, height: 2280.0),
CGSize(width: 1080.0, height: 2160.0),
CGSize(width: 1083.0, height: 1920.0),
CGSize(width: 1080.0, height: 2160.0),
// used to provide a blank line in debug output
CGSize.zero,
// height > width problems
CGSize(width: 1300.0, height: 1920.0),
CGSize(width: 1143.0, height: 1920.0),
CGSize(width: 1281.0, height: 1920.0),
]
let imageViewSize = CGSize(width: 375, height: 667)
override func viewDidLoad() {
super.viewDidLoad()
print()
testSizes.forEach { sz in
if sz == CGSize.zero {
print()
} else {
// original size calcs
if sz.height > sz.width {
let newSZ = scaleImageWidth(sourceImageSize: sz, scaledToWidth: imageViewSize.width)
print("Orig: \(newSZ)")
} else {
let newSZ = scaleImageHeight(sourceImageSize: sz, scaledToHeight: imageViewSize.height)
print("Orig: \(newSZ)")
}
// aspectFill calc
let newSZ = sz.aspectFill(sourceSize: sz, targetSize: imageViewSize)
print("Fill: \(newSZ)")
}
}
print()
}
func scaleImageWidth(sourceImageSize: CGSize, scaledToWidth: CGFloat) -> CGSize {
let oldWidth = sourceImageSize.width
let scaleFactor = scaledToWidth / oldWidth
let newHeight = sourceImageSize.height * scaleFactor
let newWidth = oldWidth * scaleFactor
return CGSize(width: newWidth, height: newHeight)
}
func scaleImageHeight(sourceImageSize: CGSize, scaledToHeight: CGFloat) -> CGSize {
let oldheight: CGFloat = sourceImageSize.height
let scaleFactor: CGFloat = scaledToHeight / oldheight
let newWidth: CGFloat = sourceImageSize.width * scaleFactor
let newHeight: CGFloat = oldheight * scaleFactor
return CGSize(width: newWidth, height: newHeight)
}
}
这是您应该在调试控制台中获得的内容:
Orig: (943.1796875, 667.0)
Fill: (943.1796875, 667.0)
Orig: (675.6848958333334, 667.0)
Fill: (675.6848958333334, 667.0)
Orig: (791.3677083333333, 667.0)
Fill: (791.3677083333333, 667.0)
Orig: (375.0, 791.6666666666666)
Fill: (375.0, 791.6666666666666)
Orig: (375.0, 750.0)
Fill: (375.0, 750.0)
Orig: (375.0, 664.819944598338)
Fill: (376.2296875, 667.0)
Orig: (375.0, 750.0)
Fill: (375.0, 750.0)
Orig: (374.99999999999994, 553.8461538461538)
Fill: (451.61458333333337, 667.0)
Orig: (375.0, 629.9212598425197)
Fill: (397.0734375, 667.0)
Orig: (375.0, 562.0608899297424)
Fill: (445.0140625, 667.0)
如您所见,您发布的 "problematic" 尺码明显不同 return 尺码。
经过大量尝试。我在阅读 @DonMag
答案后解决了这个问题。它提到问题可能是由于未考虑图像的 aspect ratio
引起的。
在我的场景中,我必须有一个 UIScrollView
并让图像根据其 width
和 height
填充 scrollView。而且它只能根据图像的纵横比 Vertically
或 Horizontally
向一个方向滚动。
为了实现这一点,我只使用了上述问题中的 1 个函数,即 OPTION 1
并且我将其修改如下。
func scaleImageHeight(sourceImage: UIImage, scaledToHeight: CGFloat) -> UIImage {
let oldheight: CGFloat = sourceImage.size.height
let scaleFactor: CGFloat = scaledToHeight / oldheight
let newWidth: CGFloat = sourceImage.size.width * scaleFactor
let newHeight: CGFloat = oldheight * scaleFactor
UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight))
sourceImage.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
//This check is added as the scaling function would scale down an image to a size which was less than the width of the screen.
if newWidth < self.view.frame.width{
let differenceWidth = self.view.frame.width - newWidth
newWidth = self.view.frame.width
newHeight = newHeight+differenceWidth
}
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
因此,为了满足客户的要求,对我有用的解决方案是始终按图像的 height
缩放图像,而不管 height
还是 width
是一个比一个大。
如果缩放后的图像宽度小于屏幕宽度,我会在缩放后将图像缩放回 width
,以便它适合屏幕,没有任何空格。此缩放将导致 height
可滚动。
注:
ImageView
应该有一个 contentMode
的 aspectFill
。
- 此解决方案可能不适用于所有人,因为此处用于缩放的图像是自定义设计的图像。
我有一个 UIScrollView
,里面有一个 UIImage
,我从我的服务器上得到的。每个图像的 width
大于 height
或 height
大于其 width
。
我想根据 height
或 width
例如:
我从服务器获取图像并检查哪个更大,width
或 height
。
if let width = image?.size.width, let height = image?.size.height {
if width > height {
print("Width is greater than height")
}else
print("Height is greater than width")
}
}
执行此操作后,我想以这两种方式之一缩放图像。
选项 1:
如果 width
大于 height
那么我希望根据固定到设备高度和宽度的 height
来缩放图像,并且宽度应该是 scrollable
查看。
选项 2:
如果 height
大于 width
那么我希望根据固定到设备宽度和高度的 width
来缩放图像,并且高度应该是 scrollable
查看。
我已经设法让 Option 2
使用此功能。
func scaleImageWidth(sourceImage:UIImage, scaledToWidth: CGFloat) -> UIImage {
let oldWidth = sourceImage.size.width
let scaleFactor = scaledToWidth / oldWidth
let newHeight = sourceImage.size.height * scaleFactor
let newWidth = oldWidth * scaleFactor
UIGraphicsBeginImageContext(CGSize(width:newWidth, height:newHeight))
sourceImage.draw(in: CGRect(x:0, y:0, width:newWidth, height:newHeight))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
我修改了函数以适用于 Option 1
,如下所示:
func scaleImageHeight(sourceImage: UIImage, scaledToHeight: CGFloat) -> UIImage {
let oldheight: CGFloat = sourceImage.size.height
let scaleFactor: CGFloat = scaledToHeight / oldheight
let newWidth: CGFloat = sourceImage.size.width * scaleFactor
let newHeight: CGFloat = oldheight * scaleFactor
UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight))
sourceImage.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
现在这两个功能都可以正常工作,但在这两种情况下,在某些情况下缩放会导致图像缩放小于设备的 width
或 height
。
下面是缩放出错时的一些图片。
实际结果:
预期结果:
已编辑:
这里有一些图像,它们的 height
和 width
这些功能可以正常工作。
情况一:当高度大于宽度时
- 宽度:1080.0,高度:2280.0
- 宽度:1080.0,高度:2160.0
- 宽度:1083.0,高度:1920.0
- 宽度:1080.0,高度:2160.0
情况二:当宽度大于高度时
- 宽度:2715.0,高度:1920.0
- 宽度:1945.0,高度:1920.0
- 宽度:2278.0,高度:1920.0
这里有一些图像,其 height
和 width
这些函数会产生上述问题。
情况一:当高度大于宽度时
- 宽度:1300.0,高度:1920.0
- 宽度:1143.0,高度:1920.0
- 宽度:1281.0,高度:1920.0
情况二:当宽度大于高度时
我没有注意到宽度大于高度时的任何问题。在这种情况下,它总是会产生正确的结果。
将 UIImageView 约束设置为顶部、左侧、右侧、底部。 现在转到 UIIMageView 的属性检查器并将内容模式 属性 设置为缩放以填充。
在这种情况下,某些图像可能会被拉伸。
我认为问题可能在于您正在比较图像的宽度:高度,但您没有考虑 imageView 的 aspect-ratio。
这是一个 CGSize
扩展,用于根据 "source" 尺寸(图像尺寸)和 [=32] 计算 aspectFit
或 aspectFill
尺寸=] 大小(imageView 大小)。
extension CGSize {
func aspectFit(sourceSize: CGSize, targetSize: CGSize) -> CGSize {
let vWidth = targetSize.width / sourceSize.width;
let vHeight = targetSize.height / sourceSize.height;
var returnSize = targetSize
if( vHeight < vWidth ) {
returnSize.width = targetSize.height / sourceSize.height * sourceSize.width;
}
else if( vWidth < vHeight ) {
returnSize.height = targetSize.width / sourceSize.width * sourceSize.height;
}
return returnSize;
}
func aspectFill(sourceSize: CGSize, targetSize: CGSize) -> CGSize {
let vWidth = targetSize.width / sourceSize.width;
let vHeight = targetSize.height / sourceSize.height;
var returnSize = targetSize
if( vHeight > vWidth ) {
returnSize.width = targetSize.height / sourceSize.height * sourceSize.width;
}
else if( vWidth > vHeight ) {
returnSize.height = targetSize.width / sourceSize.width * sourceSize.height;
}
return returnSize;
}
}
对于您的情况,您想要 aspectFill
尺码。
这是一个简单、完整的示例,将原始计算的输出与 aspectFill
计算的输出进行比较。我使用了您添加到问题中的图像尺寸,以及 375 x 667
的 imageView 尺寸(full-screen on an iPhone 7):
extension CGSize {
func aspectFit(sourceSize: CGSize, targetSize: CGSize) -> CGSize {
let vWidth = targetSize.width / sourceSize.width;
let vHeight = targetSize.height / sourceSize.height;
var returnSize = targetSize
if( vHeight < vWidth ) {
returnSize.width = targetSize.height / sourceSize.height * sourceSize.width;
}
else if( vWidth < vHeight ) {
returnSize.height = targetSize.width / sourceSize.width * sourceSize.height;
}
return returnSize;
}
func aspectFill(sourceSize: CGSize, targetSize: CGSize) -> CGSize {
let vWidth = targetSize.width / sourceSize.width;
let vHeight = targetSize.height / sourceSize.height;
var returnSize = targetSize
if( vHeight > vWidth ) {
returnSize.width = targetSize.height / sourceSize.height * sourceSize.width;
}
else if( vWidth > vHeight ) {
returnSize.height = targetSize.width / sourceSize.width * sourceSize.height;
}
return returnSize;
}
}
class TestViewController: UIViewController {
let testSizes: [CGSize] = [
// width > height
CGSize(width: 2715.0, height: 1920.0),
CGSize(width: 1945.0, height: 1920.0),
CGSize(width: 2278.0, height: 1920.0),
// used to provide a blank line in debug output
CGSize.zero,
// height > width
CGSize(width: 1080.0, height: 2280.0),
CGSize(width: 1080.0, height: 2160.0),
CGSize(width: 1083.0, height: 1920.0),
CGSize(width: 1080.0, height: 2160.0),
// used to provide a blank line in debug output
CGSize.zero,
// height > width problems
CGSize(width: 1300.0, height: 1920.0),
CGSize(width: 1143.0, height: 1920.0),
CGSize(width: 1281.0, height: 1920.0),
]
let imageViewSize = CGSize(width: 375, height: 667)
override func viewDidLoad() {
super.viewDidLoad()
print()
testSizes.forEach { sz in
if sz == CGSize.zero {
print()
} else {
// original size calcs
if sz.height > sz.width {
let newSZ = scaleImageWidth(sourceImageSize: sz, scaledToWidth: imageViewSize.width)
print("Orig: \(newSZ)")
} else {
let newSZ = scaleImageHeight(sourceImageSize: sz, scaledToHeight: imageViewSize.height)
print("Orig: \(newSZ)")
}
// aspectFill calc
let newSZ = sz.aspectFill(sourceSize: sz, targetSize: imageViewSize)
print("Fill: \(newSZ)")
}
}
print()
}
func scaleImageWidth(sourceImageSize: CGSize, scaledToWidth: CGFloat) -> CGSize {
let oldWidth = sourceImageSize.width
let scaleFactor = scaledToWidth / oldWidth
let newHeight = sourceImageSize.height * scaleFactor
let newWidth = oldWidth * scaleFactor
return CGSize(width: newWidth, height: newHeight)
}
func scaleImageHeight(sourceImageSize: CGSize, scaledToHeight: CGFloat) -> CGSize {
let oldheight: CGFloat = sourceImageSize.height
let scaleFactor: CGFloat = scaledToHeight / oldheight
let newWidth: CGFloat = sourceImageSize.width * scaleFactor
let newHeight: CGFloat = oldheight * scaleFactor
return CGSize(width: newWidth, height: newHeight)
}
}
这是您应该在调试控制台中获得的内容:
Orig: (943.1796875, 667.0)
Fill: (943.1796875, 667.0)
Orig: (675.6848958333334, 667.0)
Fill: (675.6848958333334, 667.0)
Orig: (791.3677083333333, 667.0)
Fill: (791.3677083333333, 667.0)
Orig: (375.0, 791.6666666666666)
Fill: (375.0, 791.6666666666666)
Orig: (375.0, 750.0)
Fill: (375.0, 750.0)
Orig: (375.0, 664.819944598338)
Fill: (376.2296875, 667.0)
Orig: (375.0, 750.0)
Fill: (375.0, 750.0)
Orig: (374.99999999999994, 553.8461538461538)
Fill: (451.61458333333337, 667.0)
Orig: (375.0, 629.9212598425197)
Fill: (397.0734375, 667.0)
Orig: (375.0, 562.0608899297424)
Fill: (445.0140625, 667.0)
如您所见,您发布的 "problematic" 尺码明显不同 return 尺码。
经过大量尝试。我在阅读 @DonMag
答案后解决了这个问题。它提到问题可能是由于未考虑图像的 aspect ratio
引起的。
在我的场景中,我必须有一个 UIScrollView
并让图像根据其 width
和 height
填充 scrollView。而且它只能根据图像的纵横比 Vertically
或 Horizontally
向一个方向滚动。
为了实现这一点,我只使用了上述问题中的 1 个函数,即 OPTION 1
并且我将其修改如下。
func scaleImageHeight(sourceImage: UIImage, scaledToHeight: CGFloat) -> UIImage {
let oldheight: CGFloat = sourceImage.size.height
let scaleFactor: CGFloat = scaledToHeight / oldheight
let newWidth: CGFloat = sourceImage.size.width * scaleFactor
let newHeight: CGFloat = oldheight * scaleFactor
UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight))
sourceImage.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
//This check is added as the scaling function would scale down an image to a size which was less than the width of the screen.
if newWidth < self.view.frame.width{
let differenceWidth = self.view.frame.width - newWidth
newWidth = self.view.frame.width
newHeight = newHeight+differenceWidth
}
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
因此,为了满足客户的要求,对我有用的解决方案是始终按图像的 height
缩放图像,而不管 height
还是 width
是一个比一个大。
如果缩放后的图像宽度小于屏幕宽度,我会在缩放后将图像缩放回 width
,以便它适合屏幕,没有任何空格。此缩放将导致 height
可滚动。
注:
ImageView
应该有一个contentMode
的aspectFill
。- 此解决方案可能不适用于所有人,因为此处用于缩放的图像是自定义设计的图像。