Как разместить UIView поверх UIImage на всех устройствах iPhone и симуляторах - PullRequest
0 голосов
/ 27 декабря 2018

Я пытаюсь получить вид на верхнюю часть изображения.У меня есть приложение xCode с изображением iPhone в UIImageView и UIView, которое позиционирует себя поверх изображения для отображения экранов iPhone.Это работало в более ранних выпусках, потому что изображение было статического размера, но теперь мне нужно масштабировать изображение, чтобы заполнить экран для новых устройств, таких как iPhone X. Думаю, Aspect Fit является частью ответа.Мне нужно, чтобы рассматриваемый вид располагался правильно на всех устройствах iPhone и симуляторах.У меня есть некоторый код, который, кажется, работает для устройств, но тогда этот же код работает по-другому в симуляторах.Мне нужно, чтобы код работал в симуляторах и устройствах одинаково, поскольку у меня нет всех устройств для тестирования.

Как я могу, используя раскадровку или код, расположить прямоугольник поверх изображениячто масштабируется системой?Нужно универсальное решение, которое работает на всех устройствах и в симуляторе.

Я включаю грубые примеры кода, с которыми я экспериментировал.

  • Файл WeedplatesViewController.swift содержит код для устройств, который, кажется, правильно позиционируется для устройств, а затем аналогичный код, который я скопировал и настраивал для тестирования симуляторов, которые неправильно позиционируются.Имейте расширение UIImage, чтобы создать изображение представления и затем некоторый код, чтобы искать черный прямоугольник в изображении.Использование кода сравнения пикселей, найденного здесь, при переполнении стека.

На раскадровке находится контроллер представления Weedpaper с заголовком Weedpaper, текстом «для Apple iPhone», изображением iPhone, UIView, который я хочучтобы правильно расположить верхнюю часть изображения iPhone, текст «количество установленных Weedpapers» и ряд кнопок автоматического изменения размера в нижней части.

  1. Сначала получил сложный урок, используя ограничения раскадровки для позиционирования прямоугольника.и, кажется, может заставить его работать в раскадровке, но не работает на устройствах или симуляторах.
  2. Попробовал жестко запрограммировать позицию, которая, казалось, работала на устройствах, но не работала на симуляторе, и наоборот, и занимаетдлинный тест и явно не правильный способ сделать это.
  3. Затем изменил файл png, поместив черный (255,255,255) прямоугольник в файл изображения iPhone, затем в коде скопировал это представление в UIImageи попытался найти пиксели черного прямоугольника, которые представляют кадр.Как только пиксели будут найдены, вы сможете позиционировать вид, используя эти координаты.Но я получаю разные растровые изображения скриншотов от устройств против симуляторов.
  4. Также пробовал использовать AVMakeRect (aspectRatio: CGSize (width: w, height: h), insideRect: self.uiImageView.bounds) безрезультатно.

Требуется UIView для размещения в верхней части изображения, которое масштабируется системой на устройствах и симуляторе.

override func viewDidAppear(_ animated: Bool) {

        if (UIDevice.modelName.contains("Simulator"))
        {
            deviceName = ProcessInfo.init().environment["SIMULATOR_DEVICE_NAME"] ?? "NoN"
            bSimulator = true
        }
        else
        {
            deviceName = UIDevice.modelName
            bSimulator = false
        }

        print("deviceName:", deviceName)
        print("simulator:", bSimulator)

        var frame:CGRect!

        if bSimulator{
            frame = self.getImageRectForSimulators()
        }
        else
        {
            frame = self.getImageRectForDevices()
        }

        self.uiViewPhotos.frame = frame
        self.uiViewPhotos.isHidden = false
    }

    func getImageRectForDevices() -> CGRect
    {

        // Write the view to an image so we can get the positioning rectangle
        // Positioning Rectangle is a black rectangle in the image
        // it has the only true black pixels in the image

        let imgView:UIImageView = self.uiImageView
        let img:UIImage = self.uiImageView.asImage()


        // Write to the photo album for testing
        //UIImageWriteToSavedPhotosAlbum(img, nil, nil, nil)

        var pixelData = img.cgImage!.dataProvider!.data
        var data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

        let maxX = img.size.width
        let maxY = img.size.height
        let halfwayX = maxX / 2
        let halfwayY = maxY / 2

        let screenScale = UIScreen.main.scale
        let scaleFactorX = img.scale
        let scaleFactorY = img.scale * screenScale
        let screenFactor = UIScreen.main.bounds.width/UIScreen.main.bounds.height
        let imgViewFactor = self.uiImageView.frame.width/self.uiImageView.frame.height

        var pnt:CGPoint = CGPoint(x: -1, y: -1)

        var pixelInfo: Int = -1
        var r:CGFloat!, g:CGFloat!
        var b:CGFloat!, a:CGFloat!
        var uiColor:UIColor!

        var v1:CGFloat!, v2:CGFloat!
        var v3:CGFloat!, v4:CGFloat!

        var newRect:CGRect!

        // Find this color in the image to locate the black pixel frame
        // use that to size the view accordingly
        // Seems to change among devices so round use black color

        let uiColor_phoneFramePixelColor = UIColor(red:0.0, green:0.0, blue:0.0, alpha:1.0)

        // Device code
        for i in stride(from: halfwayX*scaleFactorX, to: 0, by: -1)
        {
            pnt.x = i
            pnt.y = halfwayY*scaleFactorY

            pixelInfo = ((Int(img.size.width) * Int(pnt.y)) + Int(pnt.x)) * 4
            r = CGFloat(data[pixelInfo])/CGFloat(255.0)
            g = CGFloat(data[pixelInfo+1])/CGFloat(255.0)
            b = CGFloat(data[pixelInfo+2])/CGFloat(255.0)
            a = CGFloat(data[pixelInfo+3])/CGFloat(255.0)
            uiColor = UIColor(red: r, green: g, blue: b, alpha: a)
            print("searching for i black pixel at i, y:", i, pnt.y, 255.0*r, 255.0*g, 255.0*b, a)
            if (uiColor == uiColor_phoneFramePixelColor)
            {
                v1 = i
                print("found i pixel at:", i)
                break
            }
        }
        print(" ")

        // find top y pixel
        // Device code
        for j in stride(from: halfwayY*scaleFactorY, to: 0, by: -1)
        {
            pnt.x = halfwayX*scaleFactorX
            pnt.y = j

            pixelInfo = ((Int(img.size.width) * Int(pnt.y)) + Int(pnt.x)) * 4
            r = CGFloat(data[pixelInfo])/CGFloat(255.0)
            g = CGFloat(data[pixelInfo+1])/CGFloat(255.0)
            b = CGFloat(data[pixelInfo+2])/CGFloat(255.0)
            a = CGFloat(data[pixelInfo+3])/CGFloat(255.0)
            uiColor = UIColor(red: r, green: g, blue: b, alpha: a)
            print("searching for j black pixel at j, x:", j, pnt.x, 255.0*r, 255.0*g, 255.0*b, a)
            if (uiColor == uiColor_phoneFramePixelColor)
            {
                v2 = j
                print("found j pixel at:", j)
                break
            }
        }
        print(" ")

        // Find bottom x pixel
        // Device code
        for k in stride(from: halfwayX*scaleFactorX, to: maxX*scaleFactorX, by: 1)
        {
            pnt.x = k
            pnt.y = halfwayY

            pixelInfo = ((Int(img.size.width) * Int(pnt.y)) + Int(pnt.x)) * 4

            r = CGFloat(data[pixelInfo])/CGFloat(255.0)
            g = CGFloat(data[pixelInfo+1])/CGFloat(255.0)
            b = CGFloat(data[pixelInfo+2])/CGFloat(255.0)
            a = CGFloat(data[pixelInfo+3])/CGFloat(255.0)
            uiColor = UIColor(red: r, green: g, blue: b, alpha: a)
            print("searching for k black pixel at k, y:", k, pnt.y, 255.0*r, 255.0*g, 255.0*b, a)
            if (uiColor == uiColor_phoneFramePixelColor)
            {
                v3 = k
                print("found bottom k pixel at:", k)
                break
            }
        }
        print(" ")

        // Find bottom y pixel
        // Device code
        for l in stride(from: halfwayY*scaleFactorY, to: maxY*scaleFactorY, by: 1)
        {
            pnt.x = halfwayX
            pnt.y = l

            pixelInfo = ((Int(img.size.width) * Int(pnt.y)) + Int(pnt.x)) * 4

            r = CGFloat(data[pixelInfo])/CGFloat(255.0)
            g = CGFloat(data[pixelInfo+1])/CGFloat(255.0)
            b = CGFloat(data[pixelInfo+2])/CGFloat(255.0)
            a = CGFloat(data[pixelInfo+3])/CGFloat(255.0)

            uiColor = UIColor(red: r, green: g, blue: b, alpha: a)
            print("searching for l black pixel at l, x:", l, pnt.x, 255.0*r, 255.0*g, 255.0*b, a)

            if (uiColor == uiColor_phoneFramePixelColor)
            {
                v4 = l
                print("found bottom l pixel at:", l)
                break
            }
        }
        print(" ")

        // this is the Black Rectangle from the bitmap of the orginal image
        let w = (v3 - v1)
        let h = (v4 - v2)
        newRect = CGRect(x: v1/scaleFactorX, y: v2/scaleFactorY, width: w/scaleFactorX, height: h/scaleFactorY)

        print("calculated rectangle:", newRect)

        return newRect
    }

extension UIView {
    func asImage()-> UIImage
    {
        // Get an image of the view. Apple discourages using UIGraphicsBeginImageContext
        // Starting with IOS 10 UIGraphicsBeginImageContext is sRBG only and 32 bit only.
        // Use UIGraphicsImageRenderer

        if #available(iOS 10.0, *) {
            let renderer = UIGraphicsImageRenderer(bounds: bounds)
            let renderFormat = UIGraphicsImageRendererFormat.default()
            renderFormat.opaque = false
            let renderedImage = renderer.image {
                rendererContext in
                layer.render(in: rendererContext.cgContext)
            }
            return renderedImage
        }
        else{
            UIGraphicsBeginImageContext(self.frame.size)
            self.layer.render(in: UIGraphicsGetCurrentContext()!)
            let image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return UIImage(cgImage: image!.cgImage!)
        }
     }
}

Ответы [ 2 ]

0 голосов
/ 04 января 2019

Наконец-то все заработало.Кажется, работает на симуляторах и устройствах, которые у меня есть: код сравнения пикселей, который я использовал, не работал должным образом, вот ссылка, которая помогла мне разобраться: Почему я получаю неправильный цвет пикселя со следующимкод .Вот что сработало для меня.Изображение iPhone, которое не показано, является изображением iPhone, имеющим черный прямоугольник, в котором вы хотите, чтобы экран находился, и для которого настроено форматное соотношение.

private func getiPhoneScreenRect() -> (rect: CGRect, bOk: Bool){

// Write the view to an image so we can get the positioning rectangle
// Positioning Rectangle is a black rectangle in the image
// it has the only true black pixels in the image
// but the pixels may not be true black when we look at them so loosen
// equality criteria

let imgView:UIImageView = self.uiImageView
let img:UIImage = self.uiImageView.screenShotViaRenderImage()

let pixelData = img.cgImage!.dataProvider!.data
let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
let bytesPerPixel = (img.cgImage?.bitsPerPixel)!
let bytesPerRow = img.cgImage!.bytesPerRow

let maxX = img.size.width
let maxY = img.size.height
let halfwayX = maxX / 2
let halfwayY = maxY / 2

let imgScale = img.scale

var pnt:CGPoint = CGPoint(x: -1, y: -1)

var pixelInfo: Int = -1
var r:CGFloat!, g:CGFloat!
var b:CGFloat!, a:CGFloat!

var v1:CGFloat = 0.0, v2:CGFloat = 0.0
var v3:CGFloat = 0.0, v4:CGFloat = 0.0

var newRect:CGRect!

// Find the black border in the image byfinding the black pixel frame
// use that to size the view accordingly
// Seems to change among devices so dont check for pure black

// From center towards left edge find black pixel

for i in stride(from: halfwayX*imgScale, to: 0, by: -1)
{
    pnt.x = i
    pnt.y = halfwayY*imgScale

    pixelInfo = Int(pnt.y) * bytesPerRow + Int(pnt.x) * 4
    r = CGFloat(data[pixelInfo])/CGFloat(255.0)
    g = CGFloat(data[pixelInfo+1])/CGFloat(255.0)
    b = CGFloat(data[pixelInfo+2])/CGFloat(255.0)
    a = CGFloat(data[pixelInfo+3])/CGFloat(255.0)

    // No true black in image so get close
    if (r*255.0 <= 3.0 && g*255.0 <= 3.0)
    {
        v1 = i
        break
    }
}

// From center towards top find top y pixel

for j in stride(from: halfwayY*imgScale, to: 0, by: -1)
{
    pnt.x = halfwayX*imgScale
    pnt.y = j

    pixelInfo = Int(pnt.y) * bytesPerRow + Int(pnt.x) * 4
    r = CGFloat(data[pixelInfo])/CGFloat(255.0)
    g = CGFloat(data[pixelInfo+1])/CGFloat(255.0)
    b = CGFloat(data[pixelInfo+2])/CGFloat(255.0)
    a = CGFloat(data[pixelInfo+3])/CGFloat(255.0)

    if (r*255.0 <= 3.0 && g*255.0 <= 3.0)
    {
        v2 = j
        break
    }
}

// From center towards bottom Find bottom x pixel

for k in stride(from:halfwayX*imgScale, to: maxX*imgScale-1, by: 1)
{
    pnt.x = k
    pnt.y = halfwayY*imgScale

    pixelInfo = Int(pnt.y) * bytesPerRow + Int(pnt.x) * 4
    r = CGFloat(data[pixelInfo])/CGFloat(255.0)
    g = CGFloat(data[pixelInfo+1])/CGFloat(255.0)
    b = CGFloat(data[pixelInfo+2])/CGFloat(255.0)
    a = CGFloat(data[pixelInfo+3])/CGFloat(255.0)

    if (r*255.0 <= 3.0 && g*255.0 <= 3.0)
    {
        v3 = k
        break
    }
}

// Find center towards right edge find bottom y pixel

for l in stride(from: halfwayY*imgScale, to: (maxY*imgScale)-1, by: 1)
{
    pnt.x = halfwayX*imgScale
    pnt.y = l

    pixelInfo = Int(pnt.y) * bytesPerRow + Int(pnt.x) * 4
    r = CGFloat(data[pixelInfo])/CGFloat(255.0)
    g = CGFloat(data[pixelInfo+1])/CGFloat(255.0)
    b = CGFloat(data[pixelInfo+2])/CGFloat(255.0)
    a = CGFloat(data[pixelInfo+3])/CGFloat(255.0)

    if (r*255.0 <= 3.0 && g*255.0 <= 3.0)
    {
        v4 = l
        break
    }
}

// If did not find rectangle return bOk false

if (v1 <= 0.0 || v2 <= 0.0 || v3 <= 0.0 || v4 <= 0.0)
    || v3 >= (maxX*imgScale)-1 || v4 >= (maxY*imgScale)-1
{
    return (newRect, false)
}

let w = (v3 - v1)
let h = (v4 - v2)

// this is the Black Rectangle from the bitmap of screenshot of the view
// inset the frame by 1 pixel

newRect = CGRect(x: (v1+2)/imgScale, y: (v2+2)/imgScale, width: (w-2)/imgScale, height: (h-2)/imgScale)

return (newRect, true)

}

Получите снимок экранакак это

extension UIView {
func screenShotViaRenderImage()-> UIImage
{
    // Get an image of the view. Apple discourages using UIGraphicsBeginImageContext
    // Starting with IOS 10 UIGraphicsBeginImageContext is sRBG only and 32 bit only.
    // Use UIGraphicsImageRenderer

    if #available(iOS 10.0, *) {
        let rendererFormat = UIGraphicsImageRendererFormat.default()
        rendererFormat.opaque = false
        let renderer = UIGraphicsImageRenderer(bounds: bounds, format: rendererFormat)
        let screenShot = renderer.image {
            rendererContext in
            layer.render(in: rendererContext.cgContext)
        }
        return screenShot
    }
    else{
        UIGraphicsBeginImageContextWithOptions(bounds.size, false, 0.0)
        self.layer.render(in: UIGraphicsGetCurrentContext()!)
        let screenShot = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return UIImage(cgImage: screenShot!.cgImage!)
    }
}

}

, затем назовите это так

    override func viewWillLayoutSubviews() {

    // Different images depending on where we call it from viewDidAppear, viewWillAppear
    // This will make the screen size into the black rectangle in the phone image
    // the image looses detail so the black frame may not be totally black
    //

    let iPhoneScreenFrame = self.getiPhoneScreenRect()
    if (iPhoneScreenFrame.bOk)
    {
        self.someScreen.frame = iPhoneScreenFrame.rect
    }
}
0 голосов
/ 27 декабря 2018

Для UIImage убедитесь, что оно закреплено по краям (0).Масштабирование изображения зависит от размера и размеров вашего изображения.Посмотрите, что работает лучше всего.Scale To Fill может работать.

Для UIView вам, вероятно, придется поиграть с различными NSLayoutConstraints, которые активируются и деактивируются в зависимости от разных размеров экрана.NSLayoutConstraint имеет метод класса с именем activate(), который активирует несколько ограничений одновременно, что должно позволить Auto Layout обновлять весь макет одновременно.Например:

NSLayoutConstraint.activate([
vw.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20),
vw.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20),
vw.heightAnchor.constraint(equalToConstant: 100),
vw.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.

Имейте в виду, что эти ограничения также могут быть деактивированы:

NSLayoutConstraint.deactivate([
vw.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20),
vw.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20),
vw.heightAnchor.constraint(equalToConstant: 100),
vw.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor)

])

...