Center footer UIImage in grouped UITableView - Swift - ios

I've centered my Image and my label in the footer of my tableView like this :
override func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
switch section {
case 2:
let view = UIView()
let version = UILabel(frame: CGRectMake(0, 40, tableView.frame.width, 30))
version.font = version.font.fontWithSize(14)
version.text = Constants.Version
version.textColor = UIColor.lightGrayColor()
version.textAlignment = .Center;
let logo = UIImageView(frame: CGRectMake(0, 15, tableView.frame.width, 30))
logo.contentMode = .ScaleAspectFit
logo.image = UIImage(named: "LogoVector")
let context = CIContext(options: nil)
if let currentFilter = CIFilter(name: "CISepiaTone") {
let beginImage = CIImage(image: logo.image!)
currentFilter.setValue(beginImage, forKey: kCIInputImageKey)
currentFilter.setValue(0.5, forKey: kCIInputIntensityKey)
if let output = currentFilter.outputImage {
let cgimg = context.createCGImage(output, fromRect: output.extent)
let processedImage = UIImage(CGImage: cgimg)
logo.image = processedImage
}
}
view.addSubview(version)
view.addSubview(logo)
return view
default:
return nil
}
}
But when I'm going on the view, I have something like a freeze while the image is loading.. Do you have any idea?
regards.

You are paused while you are doing the filtering. Since you've hardcoded an image there, I'd suggest you add the sepia tone to the image directly with an image editor and save that. Then you're not doing that processing at run time every time.
If that's not an option, you could just create/position the toplevel UIImageView in this method, and then do a dispatch_async() to create the image and apply the filter, and then when it's done update the image view

Related

Swift 5: Better way/approach to add image border on photo editing app?

In case the title doesn't make sense, i'm trying to make a photo editing app where user can add border to their photo. For now, i'm testing a white border.
here is a gif sample of the app. (see how slow the slider is. It's meant to be smooth like any other slider.)
Gif sample
My approach was, to render the white background to the image's size, and then render the image n% smaller to shrink it hence the border.
But i have come to a problem where when i'm testing on my device (iphone 7 plus) the slider was so laggy and slow as if it's taking so much time to compute the function.
Here are the codes for the function. This function serves as blend the background with the foreground. Background being plain white colour.
blendImages is a function located on my adjustmentEngine class.
func blendImages(backgroundImg: UIImage,foregroundImg: UIImage) -> Data? {
// size variable
let contentSizeH = foregroundImg.size.height
let contentSizeW = foregroundImg.size.width
// the magic. how the image will scale in the view.
let topImageH = foregroundImg.size.height - (foregroundImg.size.height * imgSizeMultiplier)
let topImageW = foregroundImg.size.width - (foregroundImg.size.width * imgSizeMultiplier)
let bottomImage = backgroundImg
let topImage = foregroundImg
let imgView = UIImageView(frame: CGRect(x: 0, y: 0, width : contentSizeW, height: contentSizeH))
let imgView2 = UIImageView(frame: CGRect(x: 0, y: 0, width: topImageW, height: topImageH))
// - Set Content mode to what you desire
imgView.contentMode = .scaleAspectFill
imgView2.contentMode = .scaleAspectFit
// - Set Images
imgView.image = bottomImage
imgView2.image = topImage
imgView2.center = imgView.center
// - Create UIView
let contentView = UIView(frame: CGRect(x: 0, y: 0, width: contentSizeW, height: contentSizeH))
contentView.addSubview(imgView)
contentView.addSubview(imgView2)
// - Set Size
let size = CGSize(width: contentSizeW, height: contentSizeH)
UIGraphicsBeginImageContextWithOptions(size, true, 0)
contentView.drawHierarchy(in: contentView.bounds, afterScreenUpdates: true)
guard let i = UIGraphicsGetImageFromCurrentImageContext(),
let data = i.jpegData(compressionQuality: 1.0)
else {return nil}
UIGraphicsEndImageContext()
return data
}
Below are the function i called to render it into uiImageView
guard let image = image else { return }
let borderColor = UIColor.white.image()
self.adjustmentEngine.borderColor = borderColor
self.adjustmentEngine.image = image
guard let combinedImageData: Data = self.adjustmentEngine.blendImages(backgroundImg: borderColor, foregroundImg: image) else {return}
let combinedImage = UIImage(data: combinedImageData)
self.imageView.image = combinedImage
This function will get the image and blend it with a new background colour for the border.
And finally, below are the codes for the slider's didChange function.
#IBAction func sliderDidChange(_ sender: UISlider) {
print(sender.value)
let borderColor = adjustmentEngine.borderColor
let image = adjustmentEngine.image
adjustmentEngine.imgSizeMultiplier = CGFloat(sender.value)
guard let combinedImageData: Data = self.adjustmentEngine.blendImages(backgroundImg: borderColor, foregroundImg: image) else {return}
let combinedImage = UIImage(data: combinedImageData)
self.imageView.image = combinedImage
}
So the question is, Is there a better way or optimised way to do this? Or a better approach?

Image is not showing according to size

I just want to show image according to image size if the image height is grater then width then image will show horizentally or if the width is grater then height then it will show in centre.
I have already tried and I am showing my code. What I am trying ?
override func viewDidLoad() {
super.viewDidLoad()
var profileImagesData:Array<UIImage> = []
for i in 0..<profileImages.count {
// for venueImageTemp in profileImages {
var statusDict = profileImages[i] as! Dictionary<String,Any>
var imageUrl = String(format:"%#",statusDict["imageId"] as! CVarArg)
print(imageUrl)
if imageUrl.contains("cloudinary.com") {
let imgNameArr = imageUrl.components(separatedBy: "upload/")
print(imgNameArr)
let subFisrtStr = imgNameArr[0]
print(subFisrtStr)
let subStr = imgNameArr[1]
print(subStr)
let subImgNameArr = subStr.components(separatedBy: "/")
print(subImgNameArr)
let subNameStr = subImgNameArr[1]
print(subNameStr)
imageUrl = ""
imageUrl = subFisrtStr + "upload/h_600,w_600/" + subNameStr
print(imageUrl)
}
let url = URL(string: imageUrl)
print(url)
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in
let image = UIImage(data: (data)!)
profileImagesData.append(image!)
}
let imageView = CYCImageScrollView(images: profileImagesData, frame: CGRect(x: 0, y: 20, width: self.view.frame.size.width, height: self.view.frame.size.height-150), pageColor: UIColor.lightGray, currentPageColor: UIColor.orange)
// imageView.layer.cornerRadius = 30
imageView.backgroundColor = UIColor.clear
imageView.contentMode = .scaleAspectFit
imageView.clipsToBounds = true
imageView.layer.masksToBounds = true
self.view.addSubview(imageView)
}
I just want to show image according to their sizes.
Use content mode scaleAspectFill instead of scaleAspectFit
imageView.contentMode = .scaleAspectFill

NavigationBar Image

I got here such an error:
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
What is wrong with my code? I try to add image to navcontroller , I have here image as you can see.
func addNavBarImage() {
let nc = navigationController!
let image = UIImage(named: "2.png")
let imageView = UIImageView(image: image)
let bwidth = nc.navigationBar.frame.size.width
let bheight = nc.navigationBar.frame.size.height
let bannerx = bwidth/2 - (image?.size.width)!/2
let bannery = bheight/2 - (image?.size.height)!/2
imageView.frame = CGRect(x: bannerx, y: bannery, width: bwidth, height: bheight)
imageView.contentMode = .scaleAspectFit
navigationItem.titleView = imageView
}
You get this error message when you don´t have a legit image added to your let image = UIImage(named: "2000"). It is nil. Your code crashes on let bannerx = bwidth/2 - (image?.size.width)!/2. Make sure you have the right image before you continue your execution after that row.
Replace:
let image = UIImage(named: "2.png")
With:
guard let image = UIImage(named: "2") else { return }
By doing this you don´t need the to force optional use the following rows, so replace the below rows for bannerx and bannery with yours:
let bannerx = bwidth/2 - (image.size.width)/2
let bannery = bheight/2 - (image.size.height)/2

UITableView cell is highlighted on selection, but the UIImageView remains opaque. How to highlight the entire cell?

The image is set on the imageView via a URL. When I do not set an image, the tablecell highlights just fine, but when the image is applied to it, it seems to become opaque, almost like the image has a higher z-index compared to the highlight view that the tablview is applying. Does this mean I will have to use a custom highlight?
When the cell is highlighted it sets the highlighted property of labels and images.
You need to provide a highlightedImage to the UIImageView or look at more complicated solutions.
Maybe a bit late for the party but this extension should work for your case.
It loads async an image from an URL and then create the corresponding highlighted image by using the default gray color. If you want you can change the overlay color by working on the fillcolor parameter.
extension UIImageView {
func loadImage(url: String, placeHolder: UIImage?) {
let anURL = URL(string: url)!
self.image = placeHolder
let aRequest = URLRequest(url: anURL, cachePolicy: .returnCacheDataElseLoad, timeoutInterval: 10)
URLSession(configuration: .default).dataTask(with: aRequest, completionHandler: { (data, response, error) in
DispatchQueue.main.async {
if let anError = error as NSError? {
AlertController.presentOkayAlert(error: anError)
}
else if let tmpData = data, let anImage = UIImage(data: tmpData) {
self.image = anImage
self.highlightedImage = self.filledImage(source: anImage, fillColor: UIColor.colorWithHexString("#c0c0c0", alpha: 0.6))
}
}
}).resume()
}
func filledImage(source: UIImage, fillColor: UIColor) -> UIImage {
UIGraphicsBeginImageContextWithOptions(source.size, false, UIScreen.main.scale)
let context = UIGraphicsGetCurrentContext()
fillColor.setFill()
context!.translateBy(x: 0, y: source.size.height)
context!.scaleBy(x: 1.0, y: -1.0)
context!.setBlendMode(CGBlendMode.colorBurn)
let rect = CGRect(x: 0, y: 0, width: source.size.width, height: source.size.height)
context!.draw(source.cgImage!, in: rect)
context!.setBlendMode(CGBlendMode.normal)
context!.addRect(rect)
context!.drawPath(using: CGPathDrawingMode.fill)
let coloredImg : UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return coloredImg
}
}

How to convert a UIView to an image

I want to convert a UIView to an image and save it in my app. Can someone please tell me how to take screenshot of a view or convert it to an image and what is the best way to save it in an app (Not camera roll)? Here is the code for the view:
var overView = UIView(frame: CGRectMake(0, 0, self.view.frame.width/1.3, self.view.frame.height/1.3))
overView.center = CGPointMake(CGRectGetMidX(self.view.bounds),
CGRectGetMidY(self.view.bounds)-self.view.frame.height/16);
overView.backgroundColor = UIColor.whiteColor()
self.view.addSubview(overView)
self.view.bringSubviewToFront(overView)
An extension on UIView should do the trick.
extension UIView {
// Using a function since `var image` might conflict with an existing variable
// (like on `UIImageView`)
func asImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
}
}
Apple discourages using UIGraphicsBeginImageContext starting iOS 10 with the introduction of the P3 color gamut. UIGraphicsBeginImageContext is sRGB and 32-bit only. They introduced the new UIGraphicsImageRenderer API that is fully color managed, block-based, has subclasses for PDFs and images, and automatically manages the context lifetime. Check out WWDC16 session 205 for more details (image rendering begins around the 11:50 mark)
To be sure that it works on every device, use #available with a fallback to earlier versions of iOS:
extension UIView {
// Using a function since `var image` might conflict with an existing variable
// (like on `UIImageView`)
func asImage() -> UIImage {
if #available(iOS 10.0, *) {
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
} else {
UIGraphicsBeginImageContext(self.frame.size)
self.layer.render(in:UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return UIImage(cgImage: image!.cgImage!)
}
}
}
you can use extension
extension UIImage {
convenience init(view: UIView) {
UIGraphicsBeginImageContext(view.frame.size)
view.layer.render(in:UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.init(cgImage: image!.cgImage!)
}
}
Convert your UIView to image by drawViewHierarchyInRect:afterScreenUpdates: which is many times faster than renderInContext
Important note: do not call this function from viewDidLoad or viewWillAppear , make sure you are capturing a view after it is it displayed /loaded fully
Obj C
UIGraphicsBeginImageContextWithOptions(myView.bounds.size, myView.opaque, 0.0f);
[myView drawViewHierarchyInRect:myView.bounds afterScreenUpdates:NO];
UIImage *snapshotImageFromMyView = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
myImageView.image = snapshotImageFromMyView;
Save the edited image Photo album
UIImageWriteToSavedPhotosAlbum(snapshotImageFromMyView, nil,nil, nil);
Swift 3/4
UIGraphicsBeginImageContextWithOptions(myView.bounds.size, myView.isOpaque, 0.0)
myView.drawHierarchy(in: myView.bounds, afterScreenUpdates: false)
let snapshotImageFromMyView = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
print(snapshotImageFromMyView)
myImageView.image = snapshotImageFromMyView
Super easy generalization with extension , iOS11 , swift3/4
extension UIImage{
convenience init(view: UIView) {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
view.drawHierarchy(in: view.bounds, afterScreenUpdates: false)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.init(cgImage: (image?.cgImage)!)
}
}
Use :
//myView is completly loaded/visible , calling this code after only after viewDidAppear is call
imgVV.image = UIImage.init(view: myView)
// Simple image object
let img = UIImage.init(view: myView)
On iOS 10:
extension UIImage {
convenience init(view: UIView) {
UIGraphicsBeginImageContext(view.frame.size)
view.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.init(cgImage: (image?.cgImage)!)
}
}
Best practice as of iOS 10 and Swift 3
while still supporting iOS 9 and earlier
still works as of iOS 13, Xcode 11.1, Swift 5.1
extension UIView {
func asImage() -> UIImage? {
if #available(iOS 10.0, *) {
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
} else {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0.0)
defer { UIGraphicsEndImageContext() }
guard let currentContext = UIGraphicsGetCurrentContext() else {
return nil
}
self.layer.render(in: currentContext)
return UIGraphicsGetImageFromCurrentImageContext()
}
}
}
I am unsure what the question means by:
what is the best way to save it in an app (Not camera roll)?
UIGraphicsBeginImageContext(self.view.bounds.size);
self.view.layer.renderInContext(UIGraphicsGetCurrentContext())
var screenShot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
For example if I have a view of size: 50 50 at 100,100. I can use the following to take a screenshot:
UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), false, 0);
self.view.drawViewHierarchyInRect(CGRectMake(-50,-5-,view.bounds.size.width,view.bounds.size.height), afterScreenUpdates: true)
var image:UIImage = UIGraphicsGetImageFromCurrentImageContext();
In my opinion, the approach with the initialiser isn't that great because it creates two images.
I prefer this:
extension UIView {
var snapshot: UIImage? {
UIGraphicsBeginImageContext(self.frame.size)
guard let context = UIGraphicsGetCurrentContext() else {
return nil
}
layer.render(in: context)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
Swift 4.2, iOS 10
extension UIView {
// If Swift version is lower than 4.2,
// You should change the name. (ex. var renderedImage: UIImage?)
var image: UIImage? {
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { rendererContext in layer.render(in: rendererContext.cgContext) }
}
}
Sample
let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
view.backgroundColor = .blue
let view2 = UIView(frame: CGRect(x: 10, y: 10, width: 20, height: 20))
view2.backgroundColor = .red
view.addSubview(view2)
let imageView = UIImageView(image: view.image)
This works for me for Xcode 9/Swift 3.2/Swift 4 and Xcode 8/Swift 3
if #available(iOS 10.0, *) {
// for Xcode 9/Swift 3.2/Swift 4 -Paul Hudson's code
let renderer = UIGraphicsImageRenderer(size: view!.bounds.size)
let capturedImage = renderer.image {
(ctx) in
view!.drawHierarchy(in: view!.bounds, afterScreenUpdates: true)
}
return capturedImage
} else {
// for Xcode 8/Swift 3
UIGraphicsBeginImageContextWithOptions((view!.bounds.size), view!.isOpaque, 0.0)
view!.drawHierarchy(in: view!.bounds, afterScreenUpdates: false)
let capturedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return capturedImage!
}
Here's how to use it inside a function:
fileprivate func captureUIImageFromUIView(_ view:UIView?) -> UIImage {
guard (view != nil) else{
// if the view is nil (it's happened to me) return an alternative image
let errorImage = UIImage(named: "Error Image")
return errorImage
}
// if the view is all good then convert the image inside the view to a uiimage
if #available(iOS 10.0, *) {
let renderer = UIGraphicsImageRenderer(size: view!.bounds.size)
let capturedImage = renderer.image {
(ctx) in
view!.drawHierarchy(in: view!.bounds, afterScreenUpdates: true)
}
return capturedImage
} else {
UIGraphicsBeginImageContextWithOptions((view!.bounds.size), view!.isOpaque, 0.0)
view!.drawHierarchy(in: view!.bounds, afterScreenUpdates: false)
let capturedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return capturedImage!
}
}
Here's how to do something with the image returned from the function:
#IBOutlet weak fileprivate var myCustomView: UIView!
var myPic: UIImage?
let myImageView = UIImageView()
#IBAction fileprivate func saveImageButtonTapped(_ sender: UIButton) {
myPic = captureUIImageFromUIView(myCustomView)
// display the pic inside a UIImageView
myImageView.image = myPic!
}
I got the Xcode 9/Swift 3.2/Swift 4 answer from Paul Hudson convert uiview to uiimage
I got the Xcode 8/Swift 3 from somewhere on SO a longgg time ago and I forgot where :(
var snapshot = overView.snapshotViewAfterScreenUpdates(false)
or in objective-c
UIView* snapshot = [overView snapshotViewAfterScreenUpdates:NO];
You can use it easily by using the extension like this
// Take a snapshot from a view (just one view)
let viewSnapshot = myView.snapshot
// Take a screenshot (with every views in screen)
let screenSnapshot = UIApplication.shared.snapshot
// Take a snapshot from UIImage initialization
UIImage(view: self.view)
If you wanna use those extension method/variables, implement this
UIImage extension
extension UIImage {
convenience init(view: UIView) {
if let cgImage = view.snapshot?.cgImage {
self.init(cgImage: cgImage)
} else {
self.init()
}
}
}
UIView extension
extension UIView {
var snapshot: UIImage? {
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0.0)
if UIGraphicsGetCurrentContext() != nil {
drawHierarchy(in: bounds, afterScreenUpdates: true)
let screenshot = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return screenshot
}
return nil
}
}
UIApplication extension
extension UIApplication {
var snapshot: UIImage? {
return keyWindow?.rootViewController?.view.snapshot
}
}
or iOS 10+ you can use the new UIGraphicsImageRenderer + the recommended drawHierarchy, which in some situations can be much faster than layer.renderInContext
extension UIView {
func asImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(size: self.bounds.size)
return renderer.image { _ in
self.drawHierarchy(in: CGRect(x: 0, y: 0, width: bounds.size.width, height: bounds.size.height), afterScreenUpdates: false)
}
}
}
Thanks #Bao Tuan Diep ! I want add a supplement.
When you use the code:
yourView.layer.render(in:UIGraphicsGetCurrentContext()!)
You must notice that:
- If you had used `autoLayout` or `Masonry` in `yourView` (that you want to convert) .
- If you did not add `yourView` to another view which means that `yourView` was not used as a subview but just an object.
Then, your must use :
[yourView setNeedsLayout];
[yourView layoutIfNeeded];
to update yourView before yourView.layer.render(in:UIGraphicsGetCurrentContext()!).
Otherwise you may get an image object that contains no elements
Swift 4.2
import Foundation
import UIKit
extension UIImage {
convenience init(view: UIView) {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
view.drawHierarchy(in: view.bounds, afterScreenUpdates: false)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.init(cgImage: (image?.cgImage)!)
}
}
using:
let img = UIImage.init(view: self.holderView)
Implementation in Swift 3 :
Add the code below , out of class scope .
extension UIImage {
convenience init(_ view: UIView) {
UIGraphicsBeginImageContext(view.frame.size)
view.layer.render(in: UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.init(cgImage: (image?.cgImage)!)
}
}
Usage :
let image = UIImage( Your_View_Outlet )
Resorting to an extension, as some have suggested, might be okay for the common case yet not ideal in every application, depending on how one is drawing the view, i.e. as layers, or trying to display a view to its subviews.
If one is creating an image of the subview hierarchy:
let renderer = UIGraphicsImageRenderer(bounds: view.bounds)
let image = renderer.image { ctx in
self.drawHierarchy(in: self.bounds, afterScreenUpdates:
However, if your view consists of sublayers, for example CALayer, including CAShapeLayer comprised of UIBezierCurve... Something more like this may be in order:
let renderer = UIGraphicsImageRenderer(bounds: view.bounds)
let image = renderer.image { rendererContext in
for sublayer in self.layer.sublayers!.reversed() {
sublayer.render(in: rendererContext.cgContext)
}
}
And of course one might want to display a view with subviews which are layered. And since UIView.drawHierarchy() doesn't include sublayers... well, it could get more complicated
I use this for small images (because it does not work for big images):
extension UIView {
public func drawHierarchyAsImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(size: self.bounds.size)
let image = renderer.image { _ in
self.drawHierarchy(in: self.bounds, afterScreenUpdates: true)
}
return image
}
}
For high resolution images i use this:
extension UIView {
public func renderAsImage(scale: CGFloat) -> UIImage {
let format = UIGraphicsImageRendererFormat()
format.scale = scale
let renderer = UIGraphicsImageRenderer(bounds: bounds, format: format)
return renderer.image { rendererContext in
if let presentation = layer.presentation() {
presentation.render(in: rendererContext.cgContext)
}
}
}
}
In my view hierarchy i have rotated views and thats why i use the presentation layer as mentioned here:
renderInContext does not capture rotated subviews
I implemented #Naveed J.'s method like this, and it worked like a charm.
Here was his extentsion:
extension UIView {
// Using a function since `var image` might conflict with an existing variable
// (like on `UIImageView`)
func asImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
}
}
Here is how I implemented it.
//create an image from yourView to display
//determine the frame of the view/imageimage
let screen = self.superview!.bounds
let width = screen.width / 4 //make image 1/4 width of screen
let height = width
let frame = CGRect(x: 0, y: 0, width: width, height: height)
let x = (screen.size.width - frame.size.width) * 0.5
let y = (screen.size.height - frame.size.height) * 0.5
let mainFrame = CGRect(x: x, y: y, width: frame.size.width, height: frame.size.height)
let yourView = YourView() //instantiate yourView
yourView.frame = mainFrame //give it the frame
yourView.setNeedsDisplay() //tell it to display (I am not 100% sure this is needed)
let characterViewImage = yourView.asImage()
Initializer with the new UIGraphicsImageRenderer available since iOS 10:
extension UIImage{
convenience init(view: UIView) {
let renderer = UIGraphicsImageRenderer(size: self.bounds.size)
let canvas = CGRect(x: 0, y: 0, width: bounds.size.width, height: bounds.size.height)
let image = renderer.image { _ in
self.drawHierarchy(in: canvas, afterScreenUpdates: false)
}
self.init(cgImage: (image?.cgImage)!)
}
}
work fine with me !
Swift4
extension UIView {
func toImage() -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0.0)
self.drawHierarchy(in: self.bounds, afterScreenUpdates: false)
let snapshotImageFromMyView = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return snapshotImageFromMyView!
}
}
For view contains blurred subview (e.g. UIVisualEffectView instance), only drawViewHierarchyInRect:afterScreenUpdates works.
#ViJay Avhad's answer is correct for this case.
Using UIGraphicsImageRenderer doesn't work when the view contains Scene Kit subviews, Metal or Sprite Kit ones. In these cases, use
let renderer = UIGraphicsImageRenderer(size: view.bounds.size)
let image = renderer.image { ctx in
view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
}
please try below code.
-(UIImage *)getMainImageFromContext
{
UIGraphicsBeginImageContextWithOptions(viewBG.bounds.size, viewBG.opaque, 0.0);
[viewBG.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}

Resources