web-dev-qa-db-fra.com

Le rendu PDF dans UIWebView iOS 8, provoque une bordure noire autour PDF

Dans iOS 8, lors du rendu d'un fichier .PDF dans une UIWebview, une bordure noire et un arrière-plan entourant le PDF affiché (et non la vue d'arrière-plan complète). Notez que ce n'est pas l'arrière-plan UIWebview qui est défini sur:

myWebView.opaque = NO;
myWebView.backgroundColor = [UIColor clearColor];

Ce n'est pas présent dans <iOS8, (pas de fond coloré noir entourant le .PDF)

Quelqu'un d'autre a-t-il vécu cela qui pourrait nous éclairer?

Je charge mon PDF dans la vue Web comme si ..

- (void)viewWillAppear:(BOOL)animated
{

    [super viewWillAppear:animated];
    if (self.pdfData != nil && self.viewHasUnloaded == YES) {
        self.viewHasUnloaded = NO;
        [self.webView loadData:self.pdfData MIMEType:@"application/pdf" textEncodingName:@"utf-8" baseURL:nil];
    }
}
27
JSA986

Désactivez WebKitDiskImageCacheEnabled et la bordure noire disparaîtra:

Dans applicationDidFinishLaunchingWithOptions, ajoutez les lignes suivantes:

[[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"WebKitDiskImageCacheEnabled"];
[[NSUserDefaults standardUserDefaults] synchronize];
19
railwayparade

Après un peu d'enquête sur le problème, j'ai réussi à comprendre où se trouvait le problème. Donc, la UIWebView est une UIView, avec une UIScrollView (avec la classe privée _UIWebViewScrollView) comme sous-vue. Le chargement d'un PDF dans la UIWebView se déroule sous le flux suivant:
1)UIWebView commence à charger la demande. À ce stade, la méthode déléguée -webViewDidStartLoad: est appelée.
2) Une fois le PDF chargé (par le bas), la méthode de délégation -webViewDidFinishLoad: est appelée. À ce moment-là, UIWebView sait qu'il s'agit d'un fichier PDF et une sous-vue avec une classe privée UIWebPDFView est déjà insérée dans le _UIWebViewScrollView, mais le PDF lui-même n'est pas encore restitué.
Et voici le problème.
3) Le PDF est affiché à l'écran et une fois qu'il est prêt, une nouvelle sous-vue avec la classe privée UIPDFPageView est insérée dans UIWebPDFView et le PDF est affiché. Le problème est que lorsque cette insertion se produit, la UIWebPDFView a sa backgroundColor définie sur noir et cette insertion se produit après l'appel du -webViewDidFinishLoad: (le temps dépend de la taille du rendu de PDF). C'est pourquoi not est une bonne solution pour passer en revue toutes les sous-vues de UIWebView et définir leur propriété backgroundColor en blanc, par exemple.

La bonne nouvelle est que la méthode UIViewController de -viewDidLayoutSubviews est appelée lorsque la UIPDFPageView est insérée dans la hiérarchie de vues de la UIWebView. En fin de compte, la solution consiste à intégrer ce code objectif-C dans notre contrôleur de vue:

-(void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    // Assuming self.webView is our UIWebView
    // We go though all sub views of the UIWebView and set their backgroundColor to white
    UIView *v = self.webView;
    while (v) {
        v.backgroundColor = [UIColor whiteColor];
        v = [v.subviews firstObject];
    }
}

Et à Swift:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    var v:UIView? = self.webView
    while (v != nil) {
        v!.backgroundColor = UIColor.whiteColor()
        v = v!.subviews.first
    }
}
35
graver

Je suppose que c'est un peu un problème de synchronisation, car UIWebPDFView est ajouté comme sous-vue pas tant que le rendu n'est pas terminé et que viewDidLayoutSubviews n'est appelé avant que cela se produise. Et parce qu’il n’ya pas d’événement comme didFinshedRendering, j’ai utilisé un timer pour vérifier quand UIWebPDFView est ajouté.

Ce qui suit fonctionne pour moi.

Placez une variable d'instance dans le fichier d'en-tête ou votre classe:

// instance variable for interval timer
NSTimer *BackgroundIntervalTimer;

Mettez ces méthodes dans le fichier d'implémentation:

- (void)startUIWebViewBackgroundFixTimer {
    // if > iOS 8 start fixing background color of UIWebPDFView
    if (!SYSTEM_VERSION_LESS_THAN(@"8.0")) {
        // hide pdfWebView until background fixed
        self.pdfWebView.alpha = 0.0;
        // start interval timer
        BackgroundIntervalTimer = [NSTimer scheduledTimerWithTimeInterval:0.1  target:self selector:@selector(fixBackground) userInfo:nil repeats:YES];
    }
}

- (void)stopUIWebViewBackgroundFixTimer {
    [BackgroundIntervalTimer invalidate];
    BackgroundIntervalTimer = nil;
}

- (void)fixBackground {
    // stop timer interval
    [self stopUIWebViewBackgroundFixTimer];

    // Assuming self.webView is our UIWebView
    // We go though all sub views of the UIWebView and set their backgroundColor to white
    UIView *v = self.pdfWebView;
    while (v) {
        //v.backgroundColor = [UIColor whiteColor];
        v = [v.subviews firstObject];

        if ([NSStringFromClass([v class]) isEqualToString:@"UIWebPDFView"]) {
            [v setBackgroundColor:[UIColor whiteColor]];

            // background set to white so fade view in and exit
            [UIView animateWithDuration:0.25 delay:0.0 options:UIViewAnimationOptionCurveEaseOut
                             animations:^{
                                 self.pdfWebView.alpha = 1.0;
                             }
                             completion:nil];
            return;
        }
    }
    // UIWebPDFView doesnt exist yet so exit and try later
    [self startUIWebViewBackgroundFixTimer];
}

Maintenant, mettez cette ligne juste après la ligne où vous chargez le pdf:

// if iOS 8 check if pdfWebView got subview of class UIWebPDFView
[self startUIWebViewBackgroundFixTimer];

Astuces:

  • SYSTEM_VERSION_LESS_THAN est une macro permettant de s'assurer que le code n'est exécuté que sur iOS 8 et supérieur.
  • self.pdfWebView est votre UIWebView.
9
Heiko

La solution est ici:

EDIT (lien cassé)

 Comment supprimer une bordure noire de PDF/UIWebView dans iOS 8

Cela a fonctionné pour moi.

L'astuce consiste à changer la couleur de fond après le chargement de WebView.

MODIFIER

Dans iOS 8.0, si vous chargez une page PDF dans UIWebView, vous devrez obtenir la bordure noire autour de la page.

Lorsque vous chargez PDF dans WebView, il insère UIWebPDFView dans Webview.

Lorsque PDF est restitué, ajoutez une nouvelle classe privée UIPDFPageView dans UIWebPDFView qui ont une couleur de fond noire. Cette toute insertion se produit après que la méthode -webViewDidFinishLoad: a été appelée. Donc vous avez besoin pour définir une couleur de fond claire ou blanche après le -webViewDidFinishLoad: méthode

Ajoutez la méthode suivante pour supprimer la couleur de la bordure noire

Appelez la fonction dans -webViewDidFinishLoad:

if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
    [self performSelector:@selector(clearBackground) withObject:nil afterDelay:0.1];
}

.

- (void)clearBackground {
    UIView *v = webVw;
    while (v) {
        //v.backgroundColor = [UIColor whiteColor];
        v = [v.subviews firstObject];

        if ([NSStringFromClass([v class]) isEqualToString:@"UIWebPDFView"]) {
            [v setBackgroundColor:[UIColor whiteColor]];

            // background set to white so fade view in and exit
            [UIView animateWithDuration:0.25 delay:0.0 options:UIViewAnimationOptionCurveEaseOut
                             animations:^{
                                 webVw.alpha = 1.0;
                             }
                             completion:nil];
            return;
        }
    }
}
6
Frade

Voici l'implémentation de railwayparade à Swift:

NSUserDefaults.standardUserDefaults().setObject(false, forKey: "WebKitDiskImageCacheEnabled")
NSUserDefaults.standardUserDefaults().synchronize()
2
RFAustin

J'ai fait la réponse dans Swift. N'hésitez pas à utiliser:

override func viewDidLayoutSubviews() {
    var view :UIView?
    view = PdfView as UIView
    while (view != nil) {
        view?.backgroundColor = UIColor.clearColor()
        view = view?.subviews.first as? UIView
    }
}

PDFView est le nom de mon UIWebView.

2
LiveNL

Pour ceux qui utilisent Xamarin, une variante de solution de graver a fonctionné une fois traduite en C #.

Placez les éléments suivants dans UIViewController:

public override void ViewDidLayoutSubviews()
    {
        base.ViewDidLayoutSubviews();

        var subViews = webView.Subviews;
        foreach (UIView v in subViews) {
            v.BackgroundColor = UIColor.Clear;

            var subv = v.Subviews;
            foreach (UIView w in subv) {
                w.BackgroundColor = UIColor.Clear;

            }
        }
    }
2
jgoldberger - MSFT

J'ai le même problème ... Impossible de résoudre le problème en utilisant la solution suggérée. J'ai également essayé de définir la couleur d'arrière-plan dans la fonction de délégué webViewDidFinishLoad, mais sans utilité ..

Contenu NSLog: 

-[TextualWebView webViewDidFinishLoad:](LNo.:141) PDF VIEW< UIWebView: 0x1c5ec6d0;  
frame = (36 41; 637 810); opaque = NO; autoresize = W+H; layer = < CALayer: 0x1c5ec740>>

-[TextualWebView webViewDidFinishLoad:](LNo.:141) PDF VIEW<_UIWebViewScrollView: 0x1c5f0070;  
 frame = (0 0; 637 810); clipsToBounds = YES; autoresize = H; gestureRecognizers = <NSArray: 0x1c5f02d0>;   
 layer = <CALayer: 0x1c5ef6f0>; contentOffset: {0, 0}; contentSize: {637, 810}>

-[TextualWebView webViewDidFinishLoad:](LNo.:141) PDF VIEW< UIWebPDFView: 0x1c6e6320;  
 frame = (0 0; 637 810); opaque = NO; gestureRecognizers = <NSArray: 0x1c6e0330>;  
 layer = < CALayer: 0x1c6e6410>>
0
CallMeSan

Changer la couleur de fond des sous-vues de UIWebView après le chargement du fichier PDF - l'essence même de la plupart des réponses ci-dessus - fonctionne pour moi. Cependant, j'ai constaté que depuis iOS 5, webViewDidFinishLoad est appelé avant que le chargement de la vue Web ne soit terminé. C'est pourquoi je le fais pour éviter divers problèmes dans mes applications:

- (void)webViewDidFinishLoad:(UIWebView *)documentView {
    [self performSelector:@selector(webViewDidReallyFinishLoad:) withObject:documentView afterDelay:0.5];
}

- (void)webViewDidReallyFinishLoad:(UIWebView *)documentView {
    // now we can restore the transparency
}

Le comportement trompeur de webViewDidFinishLoad pourrait expliquer pourquoi cette approche ne fonctionne pas pour certaines personnes.

Une autre conséquence de ce comportement est que webView.scrollView.contentSize n'est pas encore défini lorsque webViewDidFinishLoad est appelé. J'ai envoyé ce message à Apple sous le numéro de bug 10259756 en octobre 2011, mais ils ont fermé le rapport de bug avec le statut "Behaves correctement".

0
arlomedia

Ceci est un mélange de Heiko et gravers réponses.

La vue responsable de l'arrière-plan noir est une instance de UIWebPDFView. Nous pouvons donc utiliser l'approche de gravers, mais sans que toutes les sous-vues UIWebView soient blanches (ce qui gâcherait l'indicateur de pages).

De plus, cette solution ne fait aucune hypothèse sur la position UIWebPDFView dans la hiérarchie des vues.

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];

    [self fixWebviewBackground:self.webView];
}

- (void)fixWebviewBackground:(UIView *)view
{
    if ([NSStringFromClass([view class]) isEqualToString:@"UIWebPDFView"]) {
        view.backgroundColor = nil;
        return;
    }
    for (UIView *subview in [view subviews]) {
        [self fixWebviewBackground:subview];
    }
}

Nous descendons de manière récursive sur toutes les sous-vues, en modifiant uniquement la couleur de fond de UIWebPDFView.

0
Guillaume Algis

J'avais exactement le même problème, en plus, j'essayais de zoomer sur mon pdf après l'avoir chargé. Si ce code était appelé prématurément, le fichier pdf deviendrait inutilisable - vous ne pourriez pas déplacer le fichier pdf du tout, vous pouvez toujours pincer pour zoomer, mais il ne fait que zoomer dans le coin tout à fait à gauche.

J'avais initialement un code pour supprimer la bordure noire et zoomer sur le pdf après un délai donné, comme ça;

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    [self performSelector:@selector(zoomIntoPDF) withObject:nil afterDelay:0.6];
}

Le délai de 0.6s est inutile cependant. Parfois, il ne serait toujours pas assez long lors des tests sur un iPhone 4S (en cours d'utilisation). Sur les derniers appareils, une performance de 0,6 s est ridicule quand elle fonctionne avec un délai <0,05 s.


La solution de @ graver m'a aidé à étudier le problème. Comme suggéré, j’exécutais NSLog(@"%@", v); dans la boucle while (j’exécutais également NSLog(@"-------------"); avant la boucle while).

J'appelais aussi viewDidLayoutSubviews après un délai de 0,6 seconde comme ceci;

[self performSelector:@selector(viewDidLayoutSubviews) withObject:nil afterDelay:0.6];

Ce sont mes journaux:

-------------
<UIWebView: 0x7fd803c2e920; frame = (0 0; 320 568); opaque = NO; autoresize = W+H; layer = <CALayer: 0x7fd803c18290>>
<_UIWebViewScrollView: 0x7fd803f6a720; frame = (0 0; 320 568); clipsToBounds = YES; autoresize = H; gestureRecognizers = <NSArray: 0x7fd803f6afb0>; layer = <CALayer: 0x7fd803f6a660>; contentOffset: {0, 0}; contentSize: {320, 568}>
<UIWebBrowserView: 0x7fd80487b400; frame = (0 0; 320 568); opaque = NO; gestureRecognizers = <NSArray: 0x7fd803f65290>; layer = <UIWebLayer: 0x7fd803da2050>>
-------------
<UIWebView: 0x7fd803c2e920; frame = (0 0; 320 568); opaque = NO; autoresize = W+H; layer = <CALayer: 0x7fd803c18290>>
<_UIWebViewScrollView: 0x7fd803f6a720; frame = (0 0; 320 568); clipsToBounds = YES; autoresize = H; gestureRecognizers = <NSArray: 0x7fd803f6afb0>; layer = <CALayer: 0x7fd803f6a660>; contentOffset: {0, 0}; contentSize: {320, 568}>
<UIWebBrowserView: 0x7fd80487b400; frame = (0 0; 320 568); opaque = NO; gestureRecognizers = <NSArray: 0x7fd803f65290>; layer = <UIWebLayer: 0x7fd803da2050>>
Reachability Flag Status: -R -----l- networkStatusForFlags
Reachability Flag Status: -R ------- networkStatusForFlags
-------------
<UIWebView: 0x7fd803c2e920; frame = (0 0; 320 568); opaque = NO; autoresize = W+H; layer = <CALayer: 0x7fd803c18290>>
<_UIWebViewScrollView: 0x7fd803f6a720; frame = (0 0; 320 568); clipsToBounds = YES; autoresize = H; gestureRecognizers = <NSArray: 0x7fd803f6afb0>; layer = <CALayer: 0x7fd803f6a660>; contentOffset: {0, 0}; contentSize: {320, 273.14641744548288}>
<UIWebPDFView: 0x7fd803c02570; frame = (0 0; 320 273.146); opaque = NO; gestureRecognizers = <NSArray: 0x7fd803c239d0>; layer = <CALayer: 0x7fd803c16a70>>
<UIPDFPageView: 0x7fd80601cf60; frame = (0 7; 320 259); tag = 1000000; gestureRecognizers = <NSArray: 0x7fd803c27db0>; layer = <CALayer: 0x7fd803ddd2f0>>

Comme vous pouvez le constater, UIPDFPageView n'apparaît que jusqu'à la fin - le troisième et dernier instant viewDidLayoutSubviews a été appelé à partir du délai.


EDIT: Ceci est un principe très similaire à la réponse de @ Heiko, mais utilise une méthode récursive plutôt qu'une minuterie.

Il doit y avoir une meilleure solution, telle que la détection d’un changement dans les sous-vues webViews, mais cette solution fonctionne pour le moment:

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    [self.webView setScalesPageToFit:YES];
    loopCap = 0;
    [self performSelector:@selector(hasPDFLoaded) withObject:nil afterDelay:0.05];
}

- (void)hasPDFLoaded
{
    BOOL containsPDF = NO;
    for (UIView *child in self.webView.scrollView.subviews)
    {
        if ([NSStringFromClass([child class]) isEqualToString:@"UIWebPDFView"]) {
            containsPDF = YES;
        }
    }
    if (containsPDF) {
        [self fixWebviewAndZoom];
    } else {
        if (loopCap < 20) {
            loopCap++;
            [self performSelector:@selector(hasPDFLoaded) withObject:nil afterDelay:0.05];
        }
    }
}

-(void)fixWebviewAndZoom {
    UIView *v = self.webView;
    while (v) {
        v.backgroundColor = [UIColor whiteColor];
        v = [v.subviews firstObject];
    }
    [self performSelector:@selector(zoomIntoPDF) withObject:nil afterDelay:0.05];
}

Veuillez excuser la méthode et les noms de variables, je proposerai des noms plus appropriés demain!

Comme vous pouvez le constater, il s’agit d’une méthode récursive qui s’appelle jusqu’à ce que le pdf soit chargé. Il y a un plafond pour éviter les boucles infinies (il devrait y avoir une alerte ou quelque chose si elle n'a pas été chargée à ce moment-là) La limite est de 20 et le délai est de 0,05 seconde, ce qui laisse 1 seconde au pdf pour se charger. Je vais probablement augmenter le plafond à 40, mais je pense que 0,05 est correct du point de vue des performances, mais je dois effectuer des tests appropriés.

J'espère que cela a été perspicace, mais j'apprécierais vraiment quelques retours ou améliorations à cette solution.

0
Patrick

La solution la plus simple, mais pas idéale, consiste à ajouter une bordure autour de UIWebView de la même couleur que la couleur de fond de votre document pdf. Cela évitera la laideur jusqu'à ce que Apple résolve le problème.

0
Kumar Summan