5

I've been successfully using the function below to take snapshots of UIViews, however, now that I'm using it inside a UITableViewCell, it no longer works.

static func pictureTaker(_ rawView: UIView) -> UIImageView {
        UIGraphicsBeginImageContextWithOptions(rawView.bounds.size, false, 0)
        rawView.drawHierarchy(in: rawView.bounds, afterScreenUpdates: true)
        let screenshot = UIGraphicsGetImageFromCurrentImageContext();
        let viewImage = UIImageView(image: screenshot)
        UIGraphicsEndImageContext();
        viewImage.frame = rawView.frame
        return viewImage
    }

Here is a trimmed down version of the function I'm using to populate the UITableViewCell.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = gamesHolder.dequeueReusableCell(withIdentifier: textCellIdentifier, for: indexPath) as! StandardGameCell
        let row = indexPath.row

        let boardPreviewTemplate = UIView(frame: CGRect(x: 0, y: 0, width: cell.previewBoard.frame.width, height: cell.previewBoard.frame.height))
        GameManagement.setupBoard(boardPreviewTemplate, boardItems: gamesArray[row].board)

        let gamePicture = PageManagement.pictureTaker(boardPreviewTemplate)
        gamePicture.frame = CGRect(x: 0, y: 0, width: gamePicture.frame.width, height: gamePicture.frame.height)

        //Removing preview board items and replacing with picture
        cell.previewBoard.subviews.forEach({ $0.removeFromSuperview() })
        cell.previewBoard.addSubview(gamePicture)

        return cell
    }

In the above tableView function, I'm programmatically creating a new view to replicate cell.previewBoard, but I also tried directly snapshotting cell.previewBoard and that didn't work either. I think the issue has something to do with the timing of the UITableViewCell loading.

I'm also 100% positive that the function is returning an image (but a blank one) and that it's where I'm intending it to be. If I change UIGraphicsBeginImageContextWithOptions(rawView.bounds.size, false, 0) to UIGraphicsBeginImageContextWithOptions(rawView.bounds.size, true, 0), the returned image shows up as entirely black.

As one final note, I discovered that boardPreviewPicture.snapshotView(afterScreenUpdates: true) would return an image, but it made scrolling through the UITableView very slow. Maybe there is another workaround where I can use snapshotView() instead.

SuperCodeBrah
  • 2,874
  • 2
  • 19
  • 33
  • You may want to rethink your design pattern. For one thing, creating / adding / removing a cell's subviews in `cellForRowAt` is generally inadvisable. What all does `GameManagement.setupBoard()` do? You might be able to streamline things there... And why are you adding/removing `UIImageView`s? And maybe other views (looking at your `.subviews.forEach` loop)? Maybe show a couple images of what you're shooting for... – DonMag May 21 '17 at 14:47
  • I'm generating previews of existing boards for a game I'm creating. The reason I need to take the snapshot is that each game piece is comprised of multiple views (that was necessary to get the look I want) and there are multiple game pieces per board preview. Because each cell only takes up part of the screen, I end up having several hundred views per screen, which makes for very choppy scrolling. By getting a snapshot, I can limit it down to a smaller number of views and get smooth scrolling. – SuperCodeBrah May 21 '17 at 15:35

1 Answers1

4

I ran into the same problem. What I noticed is that when UIGraphicsBeginImageContextWithOptions is called inside tableView(_:cellForRowAt:), the result from UIGraphicsGetImageFromCurrentImageContext() is blank. Take note that in the documentation it is stated that UIGraphicsGetImageFromCurrentImageContext:

This function may be called from any thread of your app.

However, the solution that I discovered is to call it inside the main thread. In your case, maybe you can do something like:

DispatchQueue.main.async {
    let gamePicture = PageManagement.pictureTaker(boardPreviewTemplate)
    gamePicture.frame = CGRect(x: 0, y: 0, width: gamePicture.frame.width, height: gamePicture.frame.height)

    //Removing preview board items and replacing with picture
    cell.previewBoard.subviews.forEach({ $0.removeFromSuperview() })
    cell.previewBoard.addSubview(gamePicture)
}
yoninja
  • 1,952
  • 2
  • 31
  • 39