Снимок экрана в IOS Swift [дубликат]

Я думал, что внес свой ответ, чтобы добавить более подробную информацию из Спецификаций.

Во-первых, В чем разница между передачей по ссылке или передачей по значению?

Передача по ссылке означает, что параметр вызываемых функций будет таким же, как переданный аргумент вызывающих (а не значение, а идентификатор - сама переменная).

Передача по значению означает, что параметр вызываемых функций будет копией переданного аргумента вызывающих.

blockquote>

Или из википедии, на тему прохода -by-reference

При оценке пошаговой ссылки (также называемой pass-by-reference), функция получает неявную ссылку на переменную, используемую как аргумент, а не копией его стоимости. Это обычно означает, что функция может изменять (т. Е. Назначать) переменную, используемую как аргумент, - то, что будет видно ее вызывающей стороне.

blockquote>

И по вопросу прохода -value

При вызове по умолчанию вычисляется выражение аргумента, и результирующее значение привязывается к соответствующей переменной в функции [...]. Если функция или процедура могут назначать значения своим параметрам, назначается только ее локальная копия [...].

blockquote>

Во-вторых, нам нужно знать, что использует Java в своих вызовах метода , Спецификация языка Java указывает

При вызове метода или конструктора (§15.12) значения фактических выражений аргументов инициализируют вновь созданные переменные параметра, каждый из которых объявленный тип перед выполнением тела метода или конструктора.

blockquote>

Таким образом, он присваивает (или связывает) значение аргумента соответствующей переменной параметра.

Каково значение аргумента?

Рассмотрим ссылочные типы, спецификация Java Virtual Machine Specification

Существует три типа ссылочных типов: типы классов, типы массивов и типы интерфейсов. Их значения - это ссылки на динамически созданные экземпляры классов, массивы или экземпляры классов или массивы, которые реализуют интерфейсы соответственно.

blockquote>

В спецификации языка Java также указано

Опорные значения (часто только ссылки) являются указателями на эти объекты и специальной нулевой ссылкой, которая ссылается на отсутствие объекта.

blockquote>

Значение аргумента ( определенного ссылочного типа) является указателем на объект. Обратите внимание, что переменная, вызов метода с типом возвращаемого типа ссылочного типа и выражение создания экземпляра (new ...) все решаются для значения ссылочного типа.

Итак

public void method (String param) {}
...
String var = new String("ref");
method(var);
method(var.toString());
method(new String("ref"));

все связывают значение ссылки с экземпляром String с вновь созданным параметром метода, param. Это именно то, что описывает определение pass-by-value. Таким образом, Java является передачей по значению.

Тот факт, что вы можете следовать ссылке для вызова метода или доступа к полю ссылочного объекта, совершенно не имеет отношения к разговору. Определение pass-by-reference было

Это обычно означает, что функция может изменять (т. Е. Присваивать) переменную, используемую как аргумент, - то, что будет видно ее вызывающей стороне.

blockquote>

В Java изменение переменной означает ее повторное назначение. В Java, если вы переназначили переменную в методе, она останется незамеченной для вызывающего. Изменение объекта, на которое ссылается переменная, является совершенно другой концепцией.


Примитивные значения также определены в Спецификации виртуальной машины Java, здесь . Значение типа представляет собой соответствующее значение интеграла или с плавающей запятой, соответствующим образом закодированное (8, 16, 32, 64 и т. Д.).

52
задан Community 23 May 2017 в 12:10
поделиться

11 ответов

view.snapshotView (afterScreenUpdates: true)

0
ответ дан Adam Smaka 16 August 2018 в 11:11
поделиться

Для удобства я добавил расширение в свой собственный файл

import UIKit

  public extension UIWindow {

    func capture() -> UIImage {

      UIGraphicsBeginImageContextWithOptions(self.frame.size, self.opaque, UIScreen.mainScreen().scale)
      self.layer.renderInContext(UIGraphicsGetCurrentContext()!)
      let image = UIGraphicsGetImageFromCurrentImageContext()
      UIGraphicsEndImageContext()

      return image
  }
}

, вызвав расширение следующим образом:

let window: UIWindow! = UIApplication.sharedApplication().keyWindow

let windowImage = window.capture()

Аналогичным образом можно было расширить UIView для захвата образ этого ....

9
ответ дан Damo 16 August 2018 в 11:11
поделиться

Вместо того, чтобы просто раздавать ответ, позвольте мне объяснить, что делает ваш текущий код и как его модифицировать, чтобы захватить весь экран.

UIGraphicsBeginImageContext(view.frame.size)

Эта строка кода создает новый контекст изображения с тем же размером, что и view. Главное, чтобы убрать здесь, это то, что новый контекст изображения имеет тот же размер, что и view . Если вы не хотите захватить версию приложения с низким разрешением (без сетчатки), скорее всего, вы должны использовать UIGraphicsBeginImageContextWithOptions. Затем вы можете передать 0.0, чтобы получить тот же масштабный коэффициент, что и основной экран устройства.

view.layer.renderInContext(UIGraphicsGetCurrentContext())

Эта строка кода отобразит слой представления в текущий графический контекст (который является только что созданным вами контекстом). Главное, чтобы отвлечься здесь, состоит в том, что в контекст изображения втягиваются только view (и его подвид).

let image = UIGraphicsGetImageFromCurrentImageContext()

Эта строка кода создает объект UIImage из того, что было втянуто в графический контекст.

UIGraphicsEndImageContext()

Эта строка кода завершает контекст изображения. Он очищается (вы создали контекст и также должны удалить его.


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

Если вы хотите нарисовать все в изображении, тогда вы должны создать изображение, которое является размером экрана, и нарисовать на нем все, что на экране. На практике вы, вероятно, просто говорите о все в «ключевом окне» вашего приложения. Поскольку UIWindow является подклассом UIView, его можно также вставить в контекст изображения.

60
ответ дан David Rönnqvist 16 August 2018 в 11:11
поделиться
  • 1
    Когда я пытаюсь сделать это, чтобы захватить изображение на экране, результатом будет изображение, которое немного пикселировано. Я не могу понять, почему. У тебя есть идея Дэвид? – Sam 31 July 2015 в 08:29
  • 2
    Обратите внимание, что я не изменил какой-либо код OPs, просто дал толчок в правильном направлении. Визуальный вид, который вы видите, исходит из низкого масштабного коэффициента контекста изображения. Если вы прочитаете документацию для UIGraphicsBeginImageContext, вы увидите, что она называет UIGraphicsBeginImageContextWithOptions с масштабным коэффициентом 1.0. Чтение его документации сообщит вам, что вы можете установить коэффициент масштабирования на 0.0 таким же, как на главном экране. – David Rönnqvist 31 July 2015 в 11:10
  • 3
    Спасибо Дэвиду. Действительно, сработал коэффициент масштабирования до 0. – Sam 14 August 2015 в 13:49
  • 4
    Я знаю, что это старый поток, но есть ли способ захватить все, что отображается на экране, включая предупреждение? – u84six 11 August 2016 в 15:47

Я использую этот метод:

func captureScreen() -> UIImage {
    var window: UIWindow? = UIApplication.sharedApplication().keyWindow
    window = UIApplication.sharedApplication().windows[0] as? UIWindow
    UIGraphicsBeginImageContextWithOptions(window!.frame.size, window!.opaque, 0.0)
    window!.layer.renderInContext(UIGraphicsGetCurrentContext())
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image;
}

Он фиксирует все, кроме строки состояния, и не запрашивает разрешения на сохранение изображений в рулоне камеры.

Hope это помогает!

2
ответ дан diegomen 16 August 2018 в 11:11
поделиться

Расширение Swift 3 для UIWindow

public extension UIWindow {

  func capture() -> UIImage? {

    UIGraphicsBeginImageContextWithOptions(self.frame.size, self.isOpaque, UIScreen.main.scale)
    self.layer.render(in: UIGraphicsGetCurrentContext()!)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return image

  }
}]
1
ответ дан Gregg 16 August 2018 в 11:11
поделиться

Swift 4

    /// Takes the screenshot of the screen and returns the corresponding image
    ///
    /// - Parameter shouldSave: Boolean flag asking if the image needs to be saved to user's photo library. Default set to 'true'
    /// - Returns: (Optional)image captured as a screenshot
    open func takeScreenshot(_ shouldSave: Bool = true) -> UIImage? {
        var screenshotImage :UIImage?
        let layer = UIApplication.shared.keyWindow!.layer
        let scale = UIScreen.main.scale
        UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, scale);
        guard let context = UIGraphicsGetCurrentContext() else {return nil}
        layer.render(in:context)
        screenshotImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        if let image = screenshotImage, shouldSave {
            UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
        }
        return screenshotImage
    }

Обновлено для Swift 2

Код, который вы предоставляете, работает, но не позволяет вы должны захватить NavigationBar и StatusBar на снимке экрана. Если вы хотите сделать снимок экрана вашего устройства, который будет включать в себя NavigationBar, вы должны использовать следующий код:

func screenShotMethod() {
    let layer = UIApplication.sharedApplication().keyWindow!.layer
    let scale = UIScreen.mainScreen().scale
    UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, scale);

    layer.renderInContext(UIGraphicsGetCurrentContext()!)
    let screenshot = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    UIImageWriteToSavedPhotosAlbum(screenshot, nil, nil, nil)
}

С помощью этого кода:

  • При первом запуске приложения и вызове этого метода устройство iOS попросит вас сохранить ваше изображение в кадре камеры.
  • Результатом этого кода будет изображение .JPG.
  • В конечном изображении StatusBar не будет отображаться.
40
ответ дан Kushal Ashok 16 August 2018 в 11:11
поделиться
  • 1
    Отличный метод! Благодаря снимку он фиксирует изображение в полном разрешении сетчатки. Большое спасибо за этот код! Yan- – Fox5150 13 September 2015 в 11:52
  • 2
    Отличное решение, работающее в iOS, но не могли бы вы перевести это решение на приложение OSX? – ixany 13 May 2016 в 09:32
  • 3
    @Imanou Petit Все, что я получаю с этим скриншотом, - это все черное изображение. Можете ли вы подумать, что я могу делать неправильно? Я выполняю точный код из функции touches ended функции SKScene – user594883 14 July 2016 в 14:56
  • 4
    к сожалению, это просто НЕ ВХОДИТ какой-либо CATransform3D, который вы применили к любым слоям на изображении. – Fattie 1 August 2016 в 18:20
  • 5
    Используя swift 3.0 под iOS 10.x вам нужно добавить ключ & lt; ключ & gt; NSPhotoLibraryUsageDescription & lt; / key & gt; & lt; string & gt; Добавить в свою библиотеку фотографий & lt; / string & gt; к вашему Info.plist – user3069232 17 January 2017 в 12:35

Пример Swift 3:

func captureScreen() -> UIImage? {
    guard let context = UIGraphicsGetCurrentContext() else { return .none }
    UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, UIScreen.main.scale)
    view.layer.render(in: context)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image
}
17
ответ дан Martin 16 August 2018 в 11:11
поделиться
  • 1
    пожалуйста, прекратите использование этих распаковщиков, добавив оператор guard, добавляет только одну строку – Martin 2 October 2017 в 15:46
  • 2
    @Martin не стесняйтесь редактировать мой ответ. – Daniel Storm 2 October 2017 в 16:03
  • 3
    Это снимает снимок экрана определенного вида, а не всего экрана. – Rodrigo Ruiz 10 October 2017 в 16:48

Рекомендуемым способом создания контекста в iOS 10 является использование UIGraphicsImageRenderer.

extension UIView {
    func capture() -> UIImage? {
        var image: UIImage?

        if #available(iOS 10.0, *) {
            let format = UIGraphicsImageRendererFormat()
            format.opaque = isOpaque
            let renderer = UIGraphicsImageRenderer(size: frame.size, format: format)
            image = renderer.image { context in
                drawHierarchy(in: frame, afterScreenUpdates: true)
            }
        } else {
            UIGraphicsBeginImageContextWithOptions(frame.size, isOpaque, UIScreen.main.scale)
            drawHierarchy(in: frame, afterScreenUpdates: true)
            image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
        }

        return image
    }
}
4
ответ дан neave 16 August 2018 в 11:11
поделиться
  • 1
    Это работало очень хорошо, но в случае прокручиваемого вида, такого как просмотр коллекции, результирующее изображение было первым представлением, а не фактическим текущим видом. – markhorrocks 19 May 2017 в 16:06
  // Full Screen Shot function. Hope this will work well in swift.
  func screenShot() -> UIImage {                                                    
    UIGraphicsBeginImageContext(CGSizeMake(frame.size.width, frame.size.height))
    var context:CGContextRef  = UIGraphicsGetCurrentContext()
    self.view?.drawViewHierarchyInRect(frame, afterScreenUpdates: true)
    var screenShot = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext();  
    return screenShot
  }
1
ответ дан Rex 16 August 2018 в 11:11
поделиться

Это похоже, надеюсь, это поможет кому-то в будущем.

self.view.image() //returns UIImage

Вот решение Swift 3

https://gist.github.com/ nitrag / b3117a4b6b8e89fdbc12b98029cf98f8

1
ответ дан Ryan G 16 August 2018 в 11:11
поделиться

Подробности

Xcode 9.3, Swift 4.1

Протестировано на iOS: 9, 10, 11

Решение

import UIKit

extension UIApplication {

    var screenShot: UIImage?  {
        return keyWindow?.layer.screenShot
    }
}

extension CALayer {

    var screenShot: UIImage?  {
        let scale = UIScreen.main.scale
        UIGraphicsBeginImageContextWithOptions(frame.size, false, scale)
        if let context = UIGraphicsGetCurrentContext() {
            render(in: context)
            let screenshot = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return screenshot
        }
        return nil
    }
}

Использование

 imageView.image = UIApplication.shared.screenShot

Подробности

Xcode 8.2.1, swift 3

Версия 1 для iOS 10x

import UIKit

extension UIApplication {

    var screenShot: UIImage?  {

        if let layer = keyWindow?.layer {
            let scale = UIScreen.main.scale

            UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, scale);
            if let context = UIGraphicsGetCurrentContext() {
                layer.render(in: context)
                let screenshot = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
                return screenshot
            }
        }
        return nil
    }
}

Версия 2 для iOS 9x, 10x

Если вы попытаетесь использовать код версии 1 в iOS 9x, у вас будет ошибка: CGImageCreateWithImageProvider: поставщик недопустимого изображения: NULL.

import UIKit

extension UIApplication {

    var screenShot: UIImage?  {

        if let rootViewController = keyWindow?.rootViewController {
            let scale = UIScreen.main.scale
            let bounds = rootViewController.view.bounds
            UIGraphicsBeginImageContextWithOptions(bounds.size, false, scale);
            if let _ = UIGraphicsGetCurrentContext() {
                rootViewController.view.drawHierarchy(in: bounds, afterScreenUpdates: true)
                let screenshot = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
                return screenshot
            }
        }
        return nil
    }
}

Использование

let screenShot = UIApplication.shared.screenShot!
16
ответ дан Vasily Bodnarchuk 16 August 2018 в 11:11
поделиться
  • 1
    работает как прелести – Vaibhav Jhaveri 26 December 2016 в 06:56
  • 2
    Что делать, если я хочу захватить пользовательский внешний вид AVPlayerViewController вместе с UIView? Любые предложения pls! – Raghuram 8 February 2017 в 12:51
  • 3
    Вы пытались использовать код примеров? – Vasily Bodnarchuk 8 February 2017 в 12:54
Другие вопросы по тегам:

Похожие вопросы: