How do I screenshot a uiview without adding it to the subview? - ios

I have multiple subviews on a parent view, and I need to convert the uiview to a uiimage but of only certain subviews. So I added a tag to the views I needed to take a screenshot of and added it to its own view, but when I try to screenshot that I get a black screen. However, when I use the regular parent view I get a photo with all the subviews.
let viewPic = UIView()
for subview in self.view.subviews {
if(subview.tag == 6) {
viewPic.addSubview(subview)
}
if(subview.tag == 8) {
viewPic.addSubview(subview)
}
}
let picImage = viewPic.getSnapshotImage() //This is a black screen
getSnapshotImage
extension UIView {
public func getSnapshotImage() -> UIImage {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0)
self.drawHierarchy(in: self.bounds, afterScreenUpdates: false)
let snapshotItem: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return snapshotItem
}
}

First of all, your viewPic doesn't set the frame size so the default will be zero frame which may cause the issue.
Secondly, I tried to use your getSanpshotImage() on my sample project, but I always get the blank image.
Have a look at the demo code, I can get what you want (see the screenshot):
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let viewPic = UIView()
viewPic.frame = self.view.frame
let view1 = UIView()
view1.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
view1.backgroundColor = UIColor.red
viewPic.addSubview(view1)
let view2 = UIView()
view2.frame = CGRect(x: 0, y: 200, width: 100, height: 100)
view2.backgroundColor = UIColor.blue
viewPic.addSubview(view2)
let picImage = viewPic.convertToImage() //This is a black screen
print(picImage)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension UIView {
func convertToImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
}
}

Related

Custom thumb image of UISlider doesn't show full of image icon size

I'm making a custom UISlider with thumb like this:
But here is what I get:
I want my thumb show with full height of the grey container, my image has size 160x160 for #2x. But when set into UISlider it doesn't act like that. Here is the view debugging:
I'm use a custom class for this UISlider, where's wrong in the code?:
class CustomSlider: UISlider {
#IBInspectable var trackHeight: CGFloat = 0.0001
// #IBInspectable var thumbRadius: CGFloat = 20
// Custom thumb view which will be converted to UIImage
// and set as thumb. You can customize it's colors, border, etc.
private lazy var thumbView: UIView = {
let thumb = UIImageView()
// thumb.backgroundColor = .white
// thumb.layer.borderWidth = 0.4
// thumb.layer.borderColor = UIColor.white.cgColor
thumb.image = UIImage(named: "icon_slider")
return thumb
}()
override func awakeFromNib() {
super.awakeFromNib()
let thumb = thumbImage()
setThumbImage(thumb, for: .normal)
}
private func thumbImage() -> UIImage {
thumbView.frame = CGRect(x: 0, y: 50/2, width: 50, height: 50)
// thumbView.dropShadow(color: .red, opacity: 1, offSet: CGSize(width: -1, height: 1), radius: 3, scale: true)
thumbView.layer.cornerRadius = 4
thumbView.layer.masksToBounds = true
// Convert thumbView to UIImage
if #available(iOS 10.0, *) {
let renderer = UIGraphicsImageRenderer(bounds: thumbView.bounds)
return renderer.image { rendererContext in
thumbView.layer.render(in: rendererContext.cgContext)
}
} else {
UIGraphicsBeginImageContext(thumbView.frame.size)
self.layer.render(in:UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return UIImage(cgImage: image!.cgImage!)
}
}
override func trackRect(forBounds bounds: CGRect) -> CGRect {
// Set custom track height
// As seen here: https://stackoverflow.com/a/49428606/7235585
var newRect = super.trackRect(forBounds: bounds)
newRect.size.height = trackHeight
return newRect
}
}

Transform UIView to UIImage and set it with UIImageView

I refered to this question, and did add extension to UIView:
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!)
}
}
}
Then i create very simple test view:
private class FakeTestView: BaseView {
override func prepare() {
backgroundColor = .blue
setup()
}
private func setup(){
let lbl = LabelSL.regular()
lbl.text = "LabelSL.regular()"
addSubview(lbl)
lbl.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
lbl.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
}
}
That view showing correctly when treated as UIView.
Finally, i tried:
let newSlideFrame = CGRect(x: CGFloat(i) * event.expectedWidth(),
y: 0,
width: event.expectedWidth(),
height: frame.size.height)
let imgView = UIImageView()
imgView.contentMode = .scaleAspectFit
imgView.frame = newSlideFrame
imgView.image = FakeTestView().asImage()
scroll.addSubview(imgView)
But there is nothing showing. Code from above work when i try to add UIView, or UIImageView with sample images.

Why UIScrollView's content is not centered?

I am embedding a UIImageView inside UIContentView to make it zoomable and panable (reference. The problem is that the image is not centered correctly. It looks like the entire image content is shifted out of top left corner:
While the printed content Offset is near zero: content offset is: (0.0, -20.0)
Here is my implementation:
import UIKit
class ZoomableImageView: UIScrollView {
private struct Constants {
static let minimumZoomScale: CGFloat = 0.5;
static let maximumZoomScale: CGFloat = 6.0;
}
// public so that delegate can access
public let imageView = UIImageView()
// gw: must be called to complete a setting
public func setImage(image: UIImage) {
imageView.image = image
imageView.bounds = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
self.contentSize = image.size
let scaleFitZoomScale: CGFloat = min(
self.frame.width / image.size.width ,
self.frame.height / image.size.height
)
// reset scale and offset on each resetting of image
self.zoomScale = scaleFitZoomScale
}
// MARK - constructor
init() {
// gw: there is no super.init(), you have to use this constructor as hack
super.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0)) // gw: relies on autolayout constraint later
minimumZoomScale = Constants.minimumZoomScale
maximumZoomScale = Constants.maximumZoomScale
addSubview(imageView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
ViewController:
class ViewController: UIViewController, UIScrollViewDelegate {
let zoomableImageView = ZoomableImageView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(zoomableImageView)
zoomableImageView.delegate = self
}
override func viewDidLayoutSubviews() {
zoomableImageView.frame = view.frame
let image = UIImage(imageLiteralResourceName: "kelly")
zoomableImageView.setImage(image: image)
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
// print("scale factor is: \(scrollView.zoomScale)")
return zoomableImageView.imageView
}
func scrollViewDidZoom(_ scrollView: UIScrollView) {
print("scale factor is: \(scrollView.zoomScale)")
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("content offset is: \(scrollView.contentOffset)")
}
}
However, the centering of image works perfectly fine if I don't wrap the UIImageView and UIScrollView under the custom class:
class ViewController: UIViewController, UIScrollViewDelegate {
let imageView: UIImageView = {
let image = UIImage(imageLiteralResourceName: "kelly")
let imageView = UIImageView(image: image)
imageView.bounds = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
return imageView
} ()
let scrollView: UIScrollView = {
let scrollView = UIScrollView()
return scrollView
} ()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
view.addSubview(scrollView)
scrollView.addSubview(imageView)
scrollView.delegate = self
self.scrollView.minimumZoomScale = 0.5;
self.scrollView.maximumZoomScale = 6.0;
}
override func viewDidLayoutSubviews() {
scrollView.frame = view.frame
if let size = imageView.image?.size {
scrollView.contentSize = size
}
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imageView
}
}
Did I missed something in the first implementation?
In your ZoomableImageView class, you are setting bounds when it should be frame
So change:
imageView.bounds = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
to
imageView.frame = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)

Add view on UIScrollView without it scrolling (programatically)

I am trying to add a Page Control on top of my UIScrollView Custom class. Everything is wokring fine. But whenever I slide the scrollView, the Page Control will slide along the first Image as well.
Here is my code:
import Foundation
import UIKit
import SwiftyGif
class MBSPagingScrollView: UIScrollView {
var pageControl = UIPageControl()
var imagesArray = [String]()
override init (frame : CGRect) {
super.init(frame : frame)
}
convenience init () {
self.init(frame:CGRect.zero)
}
required init(coder aDecoder: NSCoder) {
fatalError("This class does not support NSCoding")
}
init(frame : CGRect, imagesarray : [String]){
super.init(frame : frame)
imagesArray = imagesarray
self.isPagingEnabled = true
self.showsHorizontalScrollIndicator = false
for (index, imageName) in imagesArray.enumerated() {
var imageView = UIImageView()
if imageName.hasSuffix(".gif") {
let gifManager = SwiftyGifManager(memoryLimit:30)
let gif = UIImage(gifName: imageName)
imageView = UIImageView(gifImage: gif, manager: gifManager)
}
else {
imageView = UIImageView(image: UIImage(named: imageName))
imageView.contentMode = UIViewContentMode.scaleToFill
}
imageView.frame = CGRect(x: CGFloat(index) * self.frame.size.width, y: 0, width: self.frame.size.width, height: self.frame.size.height)
self.addSubview(imageView)
}
self.contentSize = CGSize(width:self.frame.size.width * CGFloat(imagesArray.count),height: self.frame.size.height)
configurePageControl()
pageControl.addTarget(self, action: #selector(self.changePage(sender:)), for: UIControlEvents.valueChanged)
}
func configurePageControl() {
self.pageControl.frame = CGRect(x: 0, y: self.frame.size.height - 50, width: self.frame.size.width, height: 50)
self.pageControl.numberOfPages = imagesArray.count
self.pageControl.currentPage = 0
self.pageControl.tintColor = UIColor.red
self.pageControl.pageIndicatorTintColor = UIColor.black
self.pageControl.currentPageIndicatorTintColor = MainOrangeColor
self.addSubview(pageControl)
}
// // MARK : TO CHANGE WHILE CLICKING ON PAGE CONTROL
func changePage(sender: AnyObject) -> () {
let x = CGFloat(pageControl.currentPage) * self.frame.size.width
self.setContentOffset(CGPoint(x:x, y:0), animated: true)
}
func selfDidEndDecelerating(_ scrollView: UIScrollView) {
let pageNumber = round(self.contentOffset.x / self.frame.size.width)
pageControl.currentPage = Int(pageNumber)
}
}
And here are images of the problem happening
As you can see the Page Control is sliding with the first image. I need it to stay in the center.
Any help would be appreciated!
I think you can do that by going to Storyboard, and from the left menu of the UI Builder, drag your page control down and put it out of your scroll view and under it, like in the following picture:
Then you'll need to add the suitable constraints to place your page control properly.
Good luck!
If you don’t want it to scroll... don’t add it to the scroll View.
Just add the page control to the self.view instead of to the scroll view.

Add insets/margin to Blurred ModalView?

I am trying to open a ModalView with blurred background (but with some space around)
Why does this code ignore the inset/frame command? I can see the blurred image. But it's fullscreen.
PS: This VC is assigned via Storyboard
class InfoViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.setupView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func setupView() {
// Add some space around the view
self.view.clipsToBounds = true
self.view.frame = UIEdgeInsetsInsetRect(self.view.frame, UIEdgeInsetsMake(20, 10, 10, 10)) // <-- not working?
// Take screenshot from parent VC
let parentVC : UIViewController = self.presentingViewController
UIGraphicsBeginImageContext(parentVC.view.bounds.size)
parentVC.view.drawViewHierarchyInRect(parentVC.view.bounds, afterScreenUpdates:true)
var image:UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// Blur screnshot
image = image.applyBlurWithRadius(20, tintColor: UIColor(white: 1.0, alpha: 0.2), saturationDeltaFactor: 1.3, maskImage: nil)
// Add screenshot to view
let backgroundImageView : UIImageView = UIImageView(image:image)
self.view.addSubview(backgroundImageView)
}
}

Resources