Soru Ekran bölümleri de dahil olmak üzere UIScrollView ekran görüntüsünün alınması


Benim bir UIScrollView Buna benzeyen bir TakeScreenshot yöntemini uygulayan derleme:

-(void)takeScreenshot {  
  CGRect contextRect  = CGRectMake(0, 0, 768, 1004);
  UIGraphicsBeginImageContext(contextRect.size);    
  [self.layer renderInContext:UIGraphicsGetCurrentContext()];
  UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();

  // do something with the viewImage here.
}

Bu temelde kaydırma görünümünün üstüne gider ve görünür alanın ekran görüntüsünü alır. İPad yönlendirilmiş portre olduğunda iyi çalışır, ancak manzaradayken görüntünün alt kısmı kesilir (görünür alanın yüksekliği yalnızca 748, 1004 değil).

Bir anlık görüntüsünü almak mümkün mü UIScrollViewEkranda olmayan alanlar dahil mi? Ya da görünümü aşağı kaydırmalı mıyım, ikinci bir fotoğraf çekmeli ve birleştirmeli miyim?


44
2017-08-22 01:28


Menşei




Cevaplar:


İşte kod çalışır ...

- (IBAction) renderScrollViewToImage
{
    UIImage* image = nil;

    UIGraphicsBeginImageContext(_scrollView.contentSize);
    {
        CGPoint savedContentOffset = _scrollView.contentOffset;
        CGRect savedFrame = _scrollView.frame;

        _scrollView.contentOffset = CGPointZero;
        _scrollView.frame = CGRectMake(0, 0, _scrollView.contentSize.width, _scrollView.contentSize.height);

        [_scrollView.layer renderInContext: UIGraphicsGetCurrentContext()];     
        image = UIGraphicsGetImageFromCurrentImageContext();

        _scrollView.contentOffset = savedContentOffset;
        _scrollView.frame = savedFrame;
    }
    UIGraphicsEndImageContext();

    if (image != nil) {
        [UIImagePNGRepresentation(image) writeToFile: @"/tmp/test.png" atomically: YES];
        system("open /tmp/test.png");
    }
}

Son birkaç satır, resmi /tmp/test.png dosyasına yazar ve ardından Preview.app uygulamasında açar. Bu açıkça sadece Simülatörde çalışmaktadır :-)

Komple proje ScrollViewScreenShot Github Deposu


105
2017-08-22 03:04



Bu, her bir öğeyi birbirinin üzerine yerleştiriyor gibi görünüyor, bu yüzden orijinal ekrana benzemiyor! - Tim Sullivan
Oh ama sonra sen yakınsın! İşlenmeden önce her görünümün kaynağını taşımak için bir dönüşüm ayarlamanız yeterlidir. - Stefan Arentz
Yeni cevabımı dene :-) - Stefan Arentz
Girinti hilesi için yapılan oylama, asla parantez kullanmayı düşünmemişti. - Dan Abramov
Harika bir çözüm, ama UIGraphicsBeginImageContextWithOptions (_scrollView.contentSize, NO, 0.0) kullandım; Daha iyi çözünürlük için. - Gamerlegend


benim için cevap https://stackoverflow.com/a/3539944/4164443 işe yaramadı Bunu iOS 8'de uygulama görevim vardı.

Aslında bu yöntem iPhone için çalışıyor, ancak iPad (hem simu hem de gerçek cihaz) başka bir durum. Sadece görünen kısmı oluşturur ve resmin geri kalanı sadece boş.

İle denedim drawViewHierarchyInRect - şanssız. Bağlı olarak afterScreenUpdates olmak true veya false Resmin gerilmiş bir parçası ya da yine sadece görüntünün bir parçası.

Bu yüzden, doğru ekran görüntülemeyi elde etmenin tek yolu, başka bir temp görünümüne scrollview ekleyip onu oluşturmaktır.

Örnek kod aşağıdadır (VC’imde scrollview çıkışıdır)

func getImageOfScrollView()->UIImage{
    var image = UIImage();

    UIGraphicsBeginImageContextWithOptions(self.scrollView.contentSize, false, UIScreen.mainScreen().scale)

    // save initial values
    let savedContentOffset = self.scrollView.contentOffset;
    let savedFrame = self.scrollView.frame;
    let savedBackgroundColor = self.scrollView.backgroundColor

    // reset offset to top left point
    self.scrollView.contentOffset = CGPointZero;
    // set frame to content size
    self.scrollView.frame = CGRectMake(0, 0, self.scrollView.contentSize.width, self.scrollView.contentSize.height);
    // remove background
    self.scrollView.backgroundColor = UIColor.clearColor()

    // make temp view with scroll view content size
    // a workaround for issue when image on ipad was drawn incorrectly
    let tempView = UIView(frame: CGRectMake(0, 0, self.scrollView.contentSize.width, self.scrollView.contentSize.height))

    // save superview
    let tempSuperView = self.scrollView.superview
    // remove scrollView from old superview
    self.scrollView.removeFromSuperview()
    // and add to tempView
    tempView.addSubview(self.scrollView)

    // render view
    // drawViewHierarchyInRect not working correctly
    tempView.layer.renderInContext(UIGraphicsGetCurrentContext())
    // and get image
    image = UIGraphicsGetImageFromCurrentImageContext();

    // and return everything back
    tempView.subviews[0].removeFromSuperview()
    tempSuperView?.addSubview(self.scrollView)

    // restore saved settings
    self.scrollView.contentOffset = savedContentOffset;
    self.scrollView.frame = savedFrame;
    self.scrollView.backgroundColor = savedBackgroundColor

    UIGraphicsEndImageContext();

    return image
}

12
2018-02-05 11:49



işe yarıyor :). - Krutarth Patel


UIScrollView için kullanım ile UIView Uzantısı Çalışma Örneği:

extension UIView {
    func screenshot() -> UIImage {

            if(self is UIScrollView) {
                let scrollView = self as! UIScrollView

                let savedContentOffset = scrollView.contentOffset
                let savedFrame = scrollView.frame

                UIGraphicsBeginImageContext(scrollView.contentSize)
                scrollView.contentOffset = .zero
                self.frame = CGRect(x: 0, y: 0, width: scrollView.contentSize.width, height: scrollView.contentSize.height)
                self.layer.render(in: UIGraphicsGetCurrentContext()!)
                let image = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext();

                scrollView.contentOffset = savedContentOffset
                scrollView.frame = savedFrame

                return image!
            }

            UIGraphicsBeginImageContext(self.bounds.size)
            self.layer.render(in: UIGraphicsGetCurrentContext()!)
            let image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return image!

        }
}

8
2017-12-02 02:46



Bu iyi çalışıyor - Ben de bundan daha iyi bir temizleyici / daha güvenli bir çözüm gönderdim. - RyanG
Daha iyi kalite için UIGraphicsBeginImageContextWithOptions (boyut, yanlış, 0) kullanın ...;) - M.R


Çözümü @Roopesh Mittal'den aldım ve daha güvenli / daha temiz hale getirdim.

Swift 4 uyumlu

fileprivate extension UIScrollView {
    func screenshot() -> UIImage? {
        let savedContentOffset = contentOffset
        let savedFrame = frame

        UIGraphicsBeginImageContext(contentSize)
        contentOffset = .zero
        frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)

        guard let context = UIGraphicsGetCurrentContext() else { return nil }

        layer.render(in: context)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext();

        contentOffset = savedContentOffset
        frame = savedFrame

        return image
    }
}

4
2018-02-02 20:30



bazı durumlarda frame ve contentOffset geri yüklenmeyecek. ve UIGraphicsEndImageContext çağrılmayacak. - 0xa6a
Bu kodu kullanırken bu davaya girmedim, ama eminim ki bir şekilde mümkün. Her iki durumda da, bazı senaryolarda (benim durumumda olduğu gibi) koruma ve çökme daha cazip olabilir. - RyanG


Kaydırma görünümünüzü tüm ekranın ötesine genişletmek istemiyorsanız (ve yine de otomatik oynatma ile çalışmaz) daha iyi bir yol vardır.

Çekirdek grafik dönüşümlerini, contentOffset Aynı şeyi gerçekleştirmek için kaydırma görünümünün

//
//  ScrollViewSnapshotter.swift
//  ScrollViewSnapshotter
//
//  Created by Moshe Berman on 4/10/16.
//  Copyright  2016 Moshe Berman. All rights reserved.
//

import UIKit

class ScrollViewSnapshotter: NSObject {


func PDFWithScrollView(scrollview: UIScrollView) -> NSData {

    /**
     *  Step 1: The first thing we need is the default origin and size of our pages.
     *          Since bounds always start at (0, 0) and the scroll view's bounds give us
     *          the correct size for the visible area, we can just use that.
     *
     *          In the United States, a standard printed page is 8.5 inches by 11 inches,
     *          but when generating a PDF it's simpler to keep the page size matching the
     *          visible area of the scroll view. We can let our printer software (such
     *          as the Preview app on OS X or the Printer app on iOS) do the scaling.
     *
     *          If we wanted to scale ourselves, we could multiply each of those
     *          numbers by 72, to get the number of points for each dimension.
     *          We would have to change how we generated the the pages below, so
     *          for simplicity, we're going to stick to one page per screenful of content.
     */

    let pageDimensions = scrollview.bounds

    /**
     *  Step 2: Now we need to know how many pages we will need to fit our content.
     *          To get this, we divide our scroll views dimensions by the size
     *          of each page, in either direction.
     *          We also need to round up, so that the pages don't get clipped.
     */

    let pageSize = pageDimensions.size
    let totalSize = scrollview.contentSize

    let numberOfPagesThatFitHorizontally = Int(ceil(totalSize.width / pageSize.width))
    let numberOfPagesThatFitVertically = Int(ceil(totalSize.height / pageSize.height))

    /**
     *  Step 3: Set up a Core Graphics PDF context.
     *
     *          First we create a backing store for the PDF data, then
     *          pass it and the page dimensions to Core Graphics.
     *
     *          We could pass in some document information here, which mostly cover PDF metadata,
     *          including author name, creator name (our software) and a password to
     *          require when viewing the PDF file.
     *
     *          Also note that we can use UIGraphicsBeginPDFContextToFile() instead,
     *          which writes the PDF to a specified path. I haven't played with it, so
     *          I don't know if the data is written all at once, or as each page is closed.
     */

    let outputData = NSMutableData()

    UIGraphicsBeginPDFContextToData(outputData, pageDimensions, nil)

    /**
     *  Step 4: Remember some state for later.
     *          Then we need to clear the content insets, so that our
     *          core graphics layer and our content offset match up.
     *          We don't need to reset the content offset, because that
     *          happens implicitly, in the loop below.
     */

    let savedContentOffset = scrollview.contentOffset
    let savedContentInset = scrollview.contentInset

    scrollview.contentInset = UIEdgeInsetsZero

    /**
     *  Step 6: Now we loop through the pages and generate the data for each page.
     */

    if let context = UIGraphicsGetCurrentContext()
    {
        for indexHorizontal in 0 ..< numberOfPagesThatFitHorizontally
        {
            for indexVertical in 0 ..< numberOfPagesThatFitVertically
            {

                /**
                 *  Step 6a: Start a new page.
                 *
                 *          This automatically closes the previous page.
                 *          There's a similar method UIGraphicsBeginPDFPageWithInfo,
                 *          which allows you to configure the rectangle of the page and
                 *          other metadata.
                 */

                UIGraphicsBeginPDFPage()

                /**
                 *  Step 6b:The trick here is to move the visible portion of the
                 *          scroll view *and* adjust the core graphics context
                 *          appropriately.
                 *
                 *          Consider that the viewport of the core graphics context
                 *          is attached to the top of the scroll view's content view
                 *          and we need to push it in the opposite direction as we scroll.
                 *          Further, anything not inside of the visible area of the scroll
                 *          view is clipped, so scrolling will move the core graphics viewport
                 *          out of the rendered area, producing empty pages.
                 *
                 *          To counter this, we scroll the next screenful into view, and adjust
                 *          the core graphics context. Note that core graphics uses a coordinate
                 *          system which has the y coordinate decreasing as we go from top to bottom.
                 *          This is the opposite of UIKit (although it matches AppKit on OS X.)
                 */

                let offsetHorizontal = CGFloat(indexHorizontal) * pageSize.width
                let offsetVertical = CGFloat(indexVertical) * pageSize.height

                scrollview.contentOffset = CGPointMake(offsetHorizontal, offsetVertical)
                CGContextTranslateCTM(context, -offsetHorizontal, -offsetVertical) // NOTE: Negative offsets

                /**
                 *  Step 6c: Now we are ready to render the page.
                 *
                 *  There are faster ways to snapshot a view, but this
                 *  is the most straightforward way to render a layer
                 *  into a context.
                 */

                scrollview.layer.renderInContext(context)
            }
        }
    }

    /**
     *  Step 7: End the document context.
     */

    UIGraphicsEndPDFContext()

    /**
     *  Step 8: Restore the scroll view.
     */

    scrollview.contentInset = savedContentInset
    scrollview.contentOffset = savedContentOffset

    /**
     *  Step 9: Return the data.
     *          You can write it to a file, or display it the user,
     *          or even pass it to iOS for sharing.
     */

    return outputData
}
}

İşte bir Blog yazısı Süreci açıklamayı yazdım.

PDF oluşturma işlemi, sayfa yerine, bir görüntünün enstantane çekilmesine çok benzemektedir, kaydırma görünümünün boyutuna uyan büyük bir tuval oluşturmanız ve sonra içeriği parçalara ayırmanız gerekir.


1
2018-04-11 13:39





SWIFT 3 sürümü:

func snapshot() -> UIImage?
{      
    UIGraphicsBeginImageContext(scrollView.contentSize)

    let savedContentOffset = scrollView.contentOffset
    let savedFrame = scrollView.frame

    scrollView.contentOffset = CGPoint.zero
    scrollView.frame = CGRect(x: 0, y: 0, width: scrollView.contentSize.width, height: scrollView.contentSize.height)

    scrollView.layer.render(in: UIGraphicsGetCurrentContext()!)
    let image = UIGraphicsGetImageFromCurrentImageContext()

    scrollView.contentOffset = savedContentOffset
    scrollView.frame = savedFrame

    UIGraphicsEndImageContext()

    return image
}

Bu benim için çalıştı


1
2018-02-07 06:09



Hızlı 4'te mükemmel bir şekilde çalıştı. Cevap için +1. Mutlu kodlama - Prashant Gaikwad


Çok fazla bilmiyorum ama tahmin edersem eğer contextRect Bu manzara için böyle iyi çalışabilir:

  CGRect contextRect  = CGRectMake(0, 0, 1004, 768*2);

Çünkü bu contextRect büyüklüğünü belirleyecektir. UIGraphicsBeginImageContext bu yüzden umarım çiftin problemini çözebilir


0
2017-08-22 01:54



Görünen alanın sonunda sadece keser gibi görünüyor. Yüksekliği arttırmak yardımcı olmaz. - Tim Sullivan


İşte bunu yapmanın başka bir yolu da zum seviyesini hesaba katıyor. İçinde 4 farklı UIImageView katmanı olan bir kaydırma görünümüm var ve mevcut durumlarının ekran görüntüsünü almak istiyorum:

float theScale = 1.0f / theScrollView.zoomScale;
// The viewing rectangle in absolute coordinates
CGRect visibleArea = CGRectMake((int)(theScrollView.contentOffset.x * theScale), (int)(theScrollView.contentOffset.y * theScale),
                                (int)(theScrollView.bounds.size.width * theScale), (int)(theScrollView.bounds.size.height * theScale));

NSArray *layers = [NSArray arrayWithObjects:imageLayer1, imageLayer2, imageLayer3, imageLayer4, nil];
UIGraphicsBeginImageContext(visibleArea.size);
for (UIImageView *layer in layers) {
    CALayer *coreLayer = layer.layer;
    coreLayer.bounds = CGRectMake(layer.frame.origin.x - visibleArea.origin.x, layer.frame.origin.y - visibleArea.origin.y, layer.frame.size.width, layer.frame.size.height);
    [coreLayer renderInContext:UIGraphicsGetCurrentContext()];
}
UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Bu ekran görüntüsünü mutlak koordinatlarda alır. Yani, kaydırma görünümünde 2048 * 2048 görüntünüz varsa ve yaklaşık dörtte birini görebilirsiniz, ekranınızın çözünürlüğünden bağımsız olarak 512 * 512 ekran görüntüsü alırsınız. Ekran çözünürlüğünüzde bir ekran görüntüsü almak istiyorsanız (örneğin, 320 * 480), yukarıdaki kodu aşağıdaki gibi doğrudan görüntüleyebilirsiniz:

UIGraphicsBeginImageContext(theScrollView.frame.size);
[screenshot drawInRect:CGRectMake(0, 0, theScrollView.frame.size.width, theScrollView.frame.size.height)];
UIImage *smallScreenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

0
2018-06-02 01:40





@Gleb vodovozov sayesinde SWIFT 3 sürümü:

func getImageOfScrollView()->UIImage{
    var image = UIImage();

    UIGraphicsBeginImageContextWithOptions(self.scrollView.contentSize, false, UIScreen.main.scale)

    // save initial values
    let savedContentOffset = self.scrollView.contentOffset;
    let savedFrame = self.scrollView.frame;
    let savedBackgroundColor = self.scrollView.backgroundColor

    // reset offset to top left point
    self.scrollView.contentOffset = CGPoint.zero;
    // set frame to content size
    self.scrollView.frame = CGRect(x: 0, y: 0, width: self.scrollView.contentSize.width, height: self.scrollView.contentSize.height)
    // remove background
    self.scrollView.backgroundColor = UIColor.clear

    // make temp view with scroll view content size
    // a workaround for issue when image on ipad was drawn incorrectly
    let tempView = UIView(frame: CGRect(x: 0, y: 0, width: self.scrollView.contentSize.width, height: self.scrollView.contentSize.height))

    // save superview
    let tempSuperView = self.scrollView.superview
    // remove scrollView from old superview
    self.scrollView.removeFromSuperview()
    // and add to tempView
    tempView.addSubview(self.scrollView)

    // render view
    // drawViewHierarchyInRect not working correctly
    tempView.layer.render(in: UIGraphicsGetCurrentContext()!)
    // and get image
    image = UIGraphicsGetImageFromCurrentImageContext()!;

    // and return everything back
    tempView.subviews[0].removeFromSuperview()
    tempSuperView?.addSubview(self.scrollView)

    // restore saved settings
    self.scrollView.contentOffset = savedContentOffset;
    self.scrollView.frame = savedFrame;
    self.scrollView.backgroundColor = savedBackgroundColor

    UIGraphicsEndImageContext();

    return image
}

0
2017-08-24 08:54