1

I am making an app using Swift 4.0. It is a sort of social media app. My app has a collectionView (the users feed) with multiple collectionViewCells. The problem with this however is that for every cell I have, I am blurring a portion or sometimes all of the image. This causes heavy lag on the device after a couple scrolls to the point where you can barely use it.

In order to counter this I thought it would make much more sense instead of blurring the cells image I would just blur it when the user uploads it into the sql database and when it shows up in other users feed/timeline they're iPhone won't have to blur the image because the image they pulled from the database is already pulled.

I have had some luck turning UIImageViews into UIImages but whenever I render it to an image the blurring effect gets distorted and essentially turns into a white overlay with a some transparency.

Code for turning UIImageViews into UIImages:

extension UIImage {
    convenience init(view: UIView) {
        UIGraphicsBeginImageContext(view.frame.size)
        view.layer.render(in:UIGraphicsGetCurrentContext()!)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        self.init(cgImage: image!.cgImage!)
    }
}

After that I simply call it with

let background = UIImageView(frame: CGRect(x: 0, y: 0, width: cell.frame.width, height: 200))
background.image = profileImage
let label = UIBlurEffect(style: UIBlurEffectStyle.prominent)
let blurEffectView = UIVisualEffectView(effect: label)
blurEffectView.frame = CGRect(x: 0, y: 0, width: background.frame.width, height: background.frame.height)
background.addSubview(blurEffectView)
let secondImage = UIImage(view: background)

This Is What I Get

This Is What I Want

It may be hard to see but there is no blur in the first image it is just a transparent white overlay

rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • This may be your answer https://dzone.com/articles/creating-a-custom-variable-blur-filter-in-core-ima – Jake Feb 17 '18 at 05:56

2 Answers2

2

// try like this

public extension UIImageView {
public func snapshotImage() -> UIImage? {
    UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
    drawHierarchy(in: bounds, afterScreenUpdates: false)
    let snapshotImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return snapshotImage
}

}

// get image from your imageview 
 let image = background.snapshotImage()
Satyanarayana
  • 1,059
  • 6
  • 16
  • i checked your code, we have to add subview background view on any otherview in that case only it will create context, then only you can get screen shot of that view – Satyanarayana Feb 19 '18 at 11:26
0

You need to take a snapshot of the container view after the visual effect view is applied. I will demonstrate a flow which will work.

Your Controller:

class PreviewController: UIViewController,VisualEffectCustomDelegate{
@IBOutlet weak var screenshotView: UIView!
@IBOutlet weak var previewImageView: UIImageView!


override func viewDidLoad() {
    super.viewDidLoad()


    addBlur()
}

func addBlur(){
    let label = UIBlurEffect(style: UIBlurEffectStyle.prominent)
    let blurEffectView = CustomVisualEffect(effect: label)
    blurEffectView.frame = CGRect(x: 0, y: 0, width: screenshotView.frame.width, height: screenshotView.frame.height)
    blurEffectView.delegate = self
    screenshotView.addSubview(blurEffectView)

}

func viewAppeared() {
    print("Appeared")
    previewImageView.image = screenshotView.screenShot
}


}


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
}
}

We implement a protocol VisualEffectCustomDelegate to the controller class.

protocol VisualEffectCustomDelegate {

func viewAppeared()
}

Create your own custom class as a child of VisualEffectView class:

class CustomVisualEffect: UIVisualEffectView {

var delegate: VisualEffectCustomDelegate!

override init(effect: UIVisualEffect?) {
    super.init(effect: effect)
}

convenience init(){
    self.init()
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

override func didMoveToWindow() {
    print("Visible Now")
    delegate.viewAppeared()
}
}

The above implementation will work. What happens here is as follows: Your controller class confirms to a protocol VisualEffectCustomDelegate, which has a function named viewAppeared. We are going to use this method when we know for sure that the visual effect view is visible in the window. We use the didMoveToWindow method of the UIView class to fire the protocol method. When you initiate the CustomVisualEffect view in your controller, pass the controller to the view object. I hope this is clear.

Since you are dealing with a UITableView/UICollectionView, make the changes to the code as you see fit.

How Do I Take a Screen Shot of a UIView?

SWAT
  • 1,107
  • 8
  • 19
  • your screenshotView must have the imageView you want to capture and the blur effect view as child views within it. It will work. – SWAT Feb 17 '18 at 16:46
  • thats what I had – Zachary Gameiro Feb 17 '18 at 16:50
  • check the last function createPostCell – Zachary Gameiro Feb 17 '18 at 17:13
  • Well, sorry. The snapshot method is fired before the BlurView appears in the window. That is why it is not showing up. I will fix the issue and rewrite the code. But you will have to make changes to that code as per your requirement. – SWAT Feb 17 '18 at 19:04
  • let me try it out I'm still figuring it out – Zachary Gameiro Feb 17 '18 at 19:47
  • is it worth mentioning that I am creating all elements programmatically and not through the storyboard – Zachary Gameiro Feb 17 '18 at 20:10
  • or wouldn't it be easier to just add a one second delay to make sure the blur effect has been loaded and then screenshot the view? – Zachary Gameiro Feb 17 '18 at 20:12
  • I wouldn't use the delay. When we do that, we assume that the blur view is visible after the delay, which may not always be the case. And adding delayed method in a collectionView/tableView isn't exactly pleasing. – SWAT Feb 18 '18 at 02:56