Как масштабировать UIImage по размеру экрана в зависимости от высоты и ширины устройства - PullRequest
0 голосов
/ 10 июля 2019

У меня есть 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 устройства.

Вот некоторые изображения того, как это выглядит, когда масштабирование происходит неправильно.

Фактический результат:

enter image description here




Ожидаемый результат:

GIF Image of Expected Result

РЕДАКТИРОВАНИЕ:

Вот несколько изображений, для которых height и width эти функции работают правильно.

Случай 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

ВотНесколько изображений, для которых height и width, эта функция создает вышеупомянутую проблему.

Случай 1: Когда высота больше ширины

  1. Ширина: 1300,0, высота: 1920,0
  2. ширина: 1143,0, высота: 1920,0
  3. Ширина: 1281,0, Высота: 1920,0

Случай 2: Когда ширина больше высоты

Я не заметил каких-либо проблем, когда ширина большечем высота.В этом случае он всегда дает правильные результаты.

Ответы [ 3 ]

1 голос
/ 10 июля 2019

Я думаю, что проблема может заключаться в том, что вы сравниваете ширину изображения с высотой, но не учитываете соотношение сторон для imageView.

Это расширение CGSize для расчета размера 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. Я использовал размеры изображений, которые вы добавили в свой вопрос, и размер изображения imageView 375 x 667 (в полноэкранном режиме на 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)

Как вы можете видеть, "проблемные" размеры, которые вы разместили, имеют отчетливо разные размеры возврата.

0 голосов
/ 15 июля 2019

Итак, после долгих попыток. Я решил эту проблему после того, как прочитал @DonMag ответ. Где упомянуто, что проблема могла быть из-за неучтения aspect ratio изображения.

В моем сценарии мне нужно было иметь UIScrollView, и изображение должно заполнить scrollView в соответствии с width & height. И он должен был прокручивать только в одном направлении: 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.

Примечание:

  1. ImageView должен иметь contentMode из aspectFill.
  2. Это решение может работать не для всех, так как изображения, используемые для масштабирования, являются изображениями, разработанными на заказ.
0 голосов
/ 10 июля 2019

Установить ограничения UIImageView сверху, слева, справа, снизу. Теперь перейдите в инспектор атрибутов UIIMageView и установите для свойства режима содержимого значение Scale To Fill.

В этом случае некоторые изображения могут быть растянуты.

...