An empty snapshotView on iPhone 7/7plus - ios

My first question here:) Recently I update my Xcode to 8, and the resizableSnapshotView method doesn't work properly on some simulators. The snapshotView works well on all testing devices with iOS9/10 and simulators under iPhone6s, but it is empty on iPhone7/7p simulators. I think 7/7p may need some authorities for accessing snapshot, but I have no idea what they are.
let cell = self.tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as! CalendarCell
var topFrame = cell.frame
topFrame.origin.y = tableView.contentOffset.y
topFrame.size.height -= tableView.contentOffset.y
topSnapshotView = tableView.resizableSnapshotView(from: topFrame, afterScreenUpdates: false, withCapInsets: UIEdgeInsets.zero)

Use the following UIView extension to create a snapshot using CoreGraphics.
I can confirm this works on iPhone 7 simulator.
public extension UIView {
public func snapshotImage() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
drawHierarchy(in: bounds, afterScreenUpdates: false)
let snapshotImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return snapshotImage
}
public func snapshotView() -> UIView? {
if let snapshotImage = snapshotImage() {
return UIImageView(image: snapshotImage)
} else {
return nil
}
}
}

Works in:
Version 8.1 (8B62)
Simulator Version 10.0 (SimulatorApp-700.14
Swift 3
import Foundation
import UIKit
extension UIView {
func snapshotView() -> UIView? {
guard let image = snapshotImage() else { return nil }
return UIImageView(image: image)
}
func snapshotImage() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, contentScaleFactor)
defer { UIGraphicsEndImageContext() }
drawHierarchy(in: bounds, afterScreenUpdates: false)
return UIGraphicsGetImageFromCurrentImageContext()
}
}

Related

Get visible part of UIImageView as UIImage in Swift

In my app, I'm trying to implement a crop feature. So now I need to convert UIImageView's visible part to UIImage.
Here's a screenshot from the UI debugger.
I've tried to achieve it with this function, but it doesn't work.
func imageFromImageView(imageView: UIImageView) -> UIImage? {
UIGraphicsBeginImageContext(imageView.frame.size)
let context = UIGraphicsGetCurrentContext()!
context.rotate(by: 2 * .pi)
imageView.layer.render(in: context)
let image = UIGraphicsGetImageFromCurrentImageContext()
return image
}
You can use bellow extension:
private var rendererKey: UInt8 = 0
extension UIView {
var renderer: UIGraphicsImageRenderer! {
get {
guard let rendererInstance = objc_getAssociatedObject(self, &rendererKey) as? UIGraphicsImageRenderer else {
self.renderer = UIGraphicsImageRenderer(bounds: bounds)
return self.renderer
}
return rendererInstance
}
set(newValue) {
objc_setAssociatedObject(self, &rendererKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
}
func snapImageView() -> UIImageView {
let img:UIImage = renderer.image { ctx in
DispatchQueue.main.async {
layer.render(in: ctx.cgContext)
}
}
let imageView:UIImageView = UIImageView(image: img)
imageView.frame = renderer.format.bounds
imageView.clipsToBounds = true
return imageView
}
}
// Generate image and image view of any view instance
let anImageView = yourView.snapImageView()

UIView to UIImage without subviews and hidden

I've got a UIView that I draw on just like a fingerpainting app, but, it's invisible sometimes. And I want to be able to take a screenshot of it when it's invisible. Also, I want a screenshot of it when it is visible however I dont want any of the subviews. I only want the UIView itself. Here's somethings I've tried:
func shapshot() -> UIImage? {
UIGraphicsBeginImageContext(self.frame.size)
guard let context = UIGraphicsGetCurrentContext() else {
return nil
}
self.layer.render(in: context)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
if image == nil {
return nil
}
// if !SettingsManager.shared.isColorInverted {
//return image!.invertedImage()
// }
return image
}
func snapshot() -> UIImage {
UIGraphicsBeginImageContextWithOptions(bounds.size, self.isOpaque, UIScreen.main.scale)
layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
Both aren't working.
try this,
func snapshot() -> UIImage {
let bounds = UIScreen.main.bounds
UIGraphicsBeginImageContextWithOptions(bounds.size, true, 0.0)
self.view.drawHierarchy(in: bounds, afterScreenUpdates: true)
self.btnShare.isHidden = true
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}

Take snapshot from UIScreen

I want to take a snapshot from view including the status bar.
the code below works in the simulator but doesn't on a physical device.
extension UIScreen {
class func screenshot() -> UIImage {
let view = main.snapshotView(afterScreenUpdates: false)
UIGraphicsBeginImageContext(view.bounds.size)
view.drawHierarchy(in: view, afterScreenUpdates: true)
let screenshot = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return screenshot!
}
}
to fix your problem you can use this function:
func takeScreenshot() -> 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()
return screenshotImage
}
I just tested this and it works on a physical device.
Create a extension like:
extension UIApplication {
func getViewScreenShot(vc: UIViewController) -> UIImage? {
let scale = UIScreen.main.scale
let bounds = vc.view.bounds
UIGraphicsBeginImageContextWithOptions(bounds.size, false, scale)
if UIGraphicsGetCurrentContext() != nil {
vc.view.drawHierarchy(in: bounds, afterScreenUpdates: true)
let screenshot = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return screenshot!
}
return nil
}
}
And call it like:
weak var weakSelf = self
let screenShotImage: UIImage = UIApplication.shared.getViewScreenShot(vc: weakSelf!)

iOS 11, completion of transitionCoordinator.animate() never called

I have wrote a FadeSegue class that override perform function from its super, UIStoryboardSegueclass.
import Foundation
import UIKit
class FadeSegue :UIStoryboardSegue{
override func perform() {
let screenShotView = UIImageView()
screenShotView.image = self.source.view.screenShot
screenShotView.frame = CGRect(x: 0, y: 0, width: self.destination.view.frame.width, height: self.destination.view.frame.height)
self.destination.view.addSubview(screenShotView)
super.perform()
self.source.transitionCoordinator?.animate(alongsideTransition: nil, completion: { (c) in
UIView.animate(withDuration: 0.3, animations: {
screenShotView.alpha = 0
}) { (status) in
screenShotView.removeFromSuperview()
}
})
}
}
extension UIView {
var screenShot: UIImage? {
UIGraphicsBeginImageContextWithOptions(bounds.size, false, 1.0);
if let _ = UIGraphicsGetCurrentContext() {
drawHierarchy(in: bounds, afterScreenUpdates: true)
let screenshot = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return screenshot
}
return nil
}
}
When Animates checkbox be unchecked in Attribute Inspector, This code will work fine in iOS 10. But in iOS 11, completion block of transitionCoordinator.animate will never called and fade animation will freeze in middle of its progress.
I cannot find out what is wrong in my code for iOS 11.

Take 20 screenshots a second Swift?

I want to take 20 screenshots a second for 3 seconds, all those added to a UIImage array that gets converted into a video.
Here is the code im using to take a screenshot:
func screenshot() {
var imageSize = CGSize.zero
let orientation = UIApplication.shared.statusBarOrientation
if UIInterfaceOrientationIsPortrait(orientation) {
imageSize = UIScreen.main.nativeBounds.size
} else {
imageSize = CGSize(width: UIScreen.main.nativeBounds.size.height, height: UIScreen.main.nativeBounds.size.width)
}
UIGraphicsBeginImageContextWithOptions(imageSize, false, 0)
for window in UIApplication.shared.windows {
window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)
}
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
images.append(image!)
}
I've read the following question, though it is quiet old and i'm sure that at 20FPS for 3 seconds it can be done, but i am not sure of which would be the best way to achieve it.
Edit: Use UIScreen.main.nativeBounds.size rather than UIScreen.main.bounds.size or you will get the 'wrong' image size , and it can be quiet a pain. This is true if Display Zoom is set to Zoomed rather than standard Credit: # Christopher Pickslay
This worked:
var limit = 80
func capture() {
if limit > 0 {
delay(0.03) {
self.screenshot()
self.capture()
} else {
convertToVideo()
}
func screenshot() {
var imageSize = CGSize.zero
let orientation = UIApplication.shared.statusBarOrientation
if UIInterfaceOrientationIsPortrait(orientation) {
imageSize = UIScreen.main.nativeBounds.size
} else {
imageSize = CGSize(width: UIScreen.main.nativeBounds.size.height, height: UIScreen.main.nativeBounds.size.width)
}
UIGraphicsBeginImageContextWithOptions(imageSize, false, 0)
for window in UIApplication.shared.windows {
window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)
}
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
images.append(image!)
limit = limit - 1
}
Delay is :
func delay(_ delay:Double, closure:#escaping ()->()) {
DispatchQueue.main.asyncAfter(
deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
}

Resources