如何根据设备的高度和宽度缩放UIImage以适合屏幕

时间:2019-07-10 05:07:00

标签: ios swift uiscrollview uiimage image-scaling

我有一个UIScrollView,里面有一个UIImage,可以从服务器上获得。每个图像的width大于height,或者height大于width

我想根据heightwidth

缩放图像,使其适合我的设备屏幕。

例如: 我从服务器获取图像,然后检查哪个更大,widthheight

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
}

现在这两个功能都可以正常工作,但是在两种情况下,缩放都会导致图像缩放小于设备的widthheight

以下是缩放错误时的外观图像。

实际结果:

enter image description here




预期结果:

GIF Image of Expected Result

已编辑

以下是其中一些heightwidth这些功能正常工作的图像。

情况1:当“高度”大于“宽度”

  1. 宽度:1080.0,高度:2280.0
  2. 宽度:1080.0,高度:2160.0
  3. 宽度:1083.0,高度:1920.0
  4. 宽度:1080.0,高度:2160.0

情况2:当“宽度”大于“高度”

  1. 宽度:2715.0,高度:1920.0
  2. 宽度:1945.0,高度:1920.0
  3. 宽度:2278.0,高度:1920.0

以下是其中一些图像,这些函数的heightwidth会产生上述问题。

情况1:当“高度”大于“宽度”

  1. 宽度:1300.0,高度:1920.0
  2. 宽度:1143.0,高度:1920.0
  3. 宽度:1281.0,高度:1920.0

情况2:当“宽度”大于“高度”

当宽度大于高度时,我没有发现任何问题。在这种情况下,它总是会产生正确的结果。

3 个答案:

答案 0 :(得分:1)

我认为问题可能在于您正在比较图像的宽度:高度,但没有考虑imageView的纵横比。

这里有一个CGSize扩展名,可根据“源”大小(图像大小)和“目标”大小(imageView)来计算aspectFitaspectFill大小大小)。

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尺寸(在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)

如您所见,您发布的“有问题”尺寸得到的退货尺寸明显不同。

答案 1 :(得分:0)

将UIImageView约束设置为上,左,右,下。 现在转到UIIMageView的“属性”检查器,然后将“内容模式”属性设置为“缩放以填充”。

在这种情况下,某些图像可能会被拉伸。

答案 2 :(得分:0)

因此,经过大量尝试。阅读@DonMag答案后,我解决了这个问题。其提到问题的原因可能是由于未考虑图像的aspect ratio

在我的场景中,我必须有一个UIScrollView并让图像根据其widthheight填充scrollView。而且它只能根据图像的纵横比在VerticallyHorizontally的一个方向上滚动。

要实现此目的,我仅使用了上面问题中的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缩放图像,而不管heightwidth是否大于另一个。

在缩放后,如果缩放后​​的图像的宽度小于屏幕宽度,那么我会将图像缩放回width,以使其适合屏幕,而没有任何空格。这种缩放将导致height可滚动。

注意:

  1. ImageView的{​​{1}}应该是contentMode
  2. 此解决方案可能不适用于所有人,因为此处用于缩放的图像是自定义设计的图像。