Ce qui fonctionne pour passer des touches grâce à (et l'affichage de toutes) les sous-vues?

J'ai le texte suivant de la chaîne de sous-vue:

UIViewController.view -+
                       |-> UIView (subclass) -+
                       |                      +-> UIToolbar 
                       |
                       +------------------------> UIWebView

Dans la sous-classe, je remplace son -touchesEnded:forEvent: méthode afin de masquer et d'afficher le UIToolbar sur un seul robinet toucher, par le biais d'un CAAnimation, ainsi que d'émettre un NSNotification que provoque la vue-contrôleur de cacher sa barre de navigation.

Si je ne suis pas d'ajouter le UIWebView comme une sous-vue à la vue du contrôleur de vue, cela fonctionne correctement.

Si je puis ajouter le UIWebView comme une sous-vue de la vue du contrôleur de la vue, puis l' UIToolbar n'apparaît pas, et je ne reçois pas l'effet d'animation. Le UIWebView répond au toucher, mais le sous-classé UIView ne le fait pas. La notification de me faire virer, même si, et la barre de navigation ne seront cachés.

Quelle est la meilleure façon de disposer de ces sous-vues, de sorte que:

  1. La UIToolbar peut être faite à glisser sur et en dehors de l'écran
  2. La UIWebView est visible peut encore recevoir son typique touch-événements: zoom avant/arrière et appuyez deux fois sur pour réinitialiser le niveau de zoom

Une réponse correcte sera répondent à ces deux critères.


MODIFIER

J'ai fait quelques progrès, mais je ne peux pas distinguer les événements tactiles et donc j'ai le feu d'une barre d'outils afficher/masquer méthode, elle ne doit pas être tiré.

J'ai la vue du contrôleur de la propriété en vue de la UIView sous-classe, et j'ai inséré le web dans la partie supérieure de la -subviews tableau via [self.view insertSubview:self.webView atIndex:0].

J'ai ajouté la méthode suivante à la UIView sous-classe:

- (UIView *) hitTest:(CGPoint) point withEvent:(UIEvent *)event {
    UIView *subview = [super hitTest:point withEvent:event];

    if (event.type == UIEventTypeTouches) {
        [self toggleToolbarView:self];

    //get touches
    NSSet *touches = [event allTouches];

    NSLog(@"subview: %@", subview);
    NSLog(@"touches: %@", touches);

    //individual touches
    for (UITouch *touch in touches) {
        switch (touch.phase) {
            case UITouchPhaseBegan:
                NSLog(@"UITouchPhaseBegan");
                break;
            case UITouchPhaseMoved:
                NSLog(@"UITouchPhaseMoved");
                break;
            case UITouchPhaseCancelled:
                NSLog(@"UITouchPhaseCancelled");
                break;
            case UITouchPhaseStationary:
                NSLog(@"UITouchPhaseStationary");
                break;      
            default:
                NSLog(@"other phase...");
                break;
            }
        }
    }

    return subview;
}

La méthode -toggleToolbarView: déclenche une NSTimer-chronométré CAAnimation qui cache/montre un UIToolbar*.

Le problème est qu'un toucher-glisser combinaison de causes -toggleToolbarView: à feu (ainsi que de l'utilisation du zoom de l'affichage sur le web). Ce que je voudrais faire est de provoquer -toggleToolbarView: d'être licencié que lorsqu'il n'y est un toucher événement.

Lorsque j'appelle la méthode ci-dessus -hitTest:withEvent:, il semble que la UIWebView aspire la touche. Les deux NSLog déclarations montrent que le UIView* qui est renvoyé par le parent UIView*'s -hitTest:withEvent: est le UIWebView ou la UIToolbar (quand il est à l'écran et touché). Le touches tableau est vide et donc le toucher types d'événements ne peuvent pas être distingués.

Il y a encore une chose que je vais essayer, mais j'ai voulu enregistrer cette tentative ici au cas où d'autres ont rapidement suggestions. Merci pour votre aide permanente.


EDITION II

J'ai fait quelques progrès avec l'obtention de la UIWebView de pincement/zoom et de répondre à une double-tap. Je peux aussi masquer/afficher la barre d'outils avec un seul robinet.

Au lieu de vous embêter avec la UIWebView instance privée UIScroller, j'ai accès à son intérieur, privé,UIWebDocumentView. Mais la vue web dans son ensemble n'a pas mis à jour correctement après un zoom avant.

Ce que cela signifie: Si mon document contient un PDF ou SVG vector illustration, par exemple, le zoom en cause le contenu d'être floues, comme si l'image tuiles qui composent le rendu PDF ou illustration vectorielle contenu ne sont pas re-rendus, un nouveau facteur de zoom.

Pour les fins de cette documentation, je vais:

  1. Appeler le UIView (subclass) au lieu ViewerOverlayView

  2. La UIWebView est appelé ViewerWebView

Les deux sont des sous-classes de UIView et UIWebView, respectivement.

Mon ViewerWebView contient une nouvelle ivar (avec @property + @synthesized):

UIView *privateWebDocumentView;

(Ce UIView est en fait un pointeur vers un UIWebDocumentView exemple, mais pour éviter de Pomme de repérage de l'application de refus, j'ai choisi ce à un UIView pour le simple but de transmettre les événements tactiles.)

La mise en œuvre de ViewerWebView remplace -hitTest:withEvent:

- (UIView *) hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView *_subview = [super hitTest:point withEvent:event];
    self.privateWebDocumentView = _subview;
    return _subview;
}

Mon ViewerOverlayView contient de nouvelles ivars (avec @property + @synthesized):

ViewerWebView *webView;
UIView *webDocumentView;

Dans la mise en œuvre de la superposition de vue, je remplace -touchesBegan:withEvent:, -touchesMoved:withEvent: et -touchesEnded:withEvent:

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
self.touchHasMoved = NO;
if (self.webView) {
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];
self.webDocumentView = [self.webView hitTest:touchPoint withEvent:event];
}
[super touchesBegan:touches withEvent:event];
[self.webDocumentView touchesBegan:touches withEvent:event];
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
self.touchHasMoved = YES;
if (self.webView) {
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];
self.webDocumentView = [self.webView hitTest:touchPoint withEvent:event];
}
[super touchesMoved:touches withEvent:event];
[self.webDocumentView touchesMoved:touches withEvent:event];
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if(!self.touchHasMoved) 
[self toggleToolbarView:self];
if (self.webView) {
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];
self.webDocumentView = [self.webView hitTest:touchPoint withEvent:event];
}
[super touchesEnded:touches withEvent:event];
[self.webDocumentView touchesEnded:touches withEvent:event];
}

Dans la vue du contrôleur, je ne les suivants:

  1. Instancier la ViewerOverlayView et ajoutez-la à la sous-vue de la vue du contrôleur de view propriété

  2. Instancier la vue du contrôleur de ViewerWebView instance et l'insérer à la partie inférieure de la sous-vues tableau

  3. Définir la ViewerOverlayView web de propriété en vue de l'affichage de contrôleur de la vue web

Alors maintenant, mon transparente ViewerOverlayView correctement passes zoom/stretch/double-tap touch événements de la ViewerWebView instance privée UIWebDocumentView instance. Le UIWebDocumentView puis redimensionne.

Toutefois, le contenu ne se redessiner avec la nouvelle échelle. Alors vecteur basé sur le contenu (par exemple, PDF, SVG) paraît floue lors du zoom avant.

Voici ce que le point de vue ressemble sans mise à l'échelle, agréable et forte:

Ce qui fonctionne pour passer des touches grâce à (et l'affichage de toutes) les sous-vues?

Voici ce que l'affichage apparaît comme lorsque vous utilisez le zoom, ce qui n'est pas aussi forte qu'elle serait autrement sans cet événement personnalisé munging:

Ce qui fonctionne pour passer des touches grâce à (et l'affichage de toutes) les sous-vues?

Il est intéressant de noter, je ne peux redimensionner entre les deux niveaux de zoom. Une fois que j'ai arrêter de double-tap-étirement de zoom à n'importe quel niveau, il "revient" à l'image que vous voyez dans l'image ci-dessus.

J'ai essayé -setNeedsDisplay sur le ViewerWebView et son intérieur UIWebDocumentView instance. Ni l'appel de la méthode a fonctionné.

En dépit de vouloir éviter les méthodes privées, parce que je veux à terme, obtenir cette application approuvée, j'ai aussi essayé cette suggestion de l'accès au privé UIWebDocumentView méthode -_webCoreNeedsDisplay:

[(UIWebDocumentView *)self.webDocumentView _webCoreNeedsDisplay];

Cette méthode causé à l'application de crash d'un unrecognized selector exception. Peut-être que Apple a changé le nom de cette méthode de SDK 3.0 pour 3.1.2.

Sauf s'il existe un moyen pour que le web pour redessiner son contenu, je suppose que je vais avoir à la ferraille de la superposition transparente afficher et il suffit de dessiner une vue web, afin d'obtenir l'affichage web pour fonctionner correctement. Qui suce!

Si quelqu'un a des pensées de redessiner après mise à l'échelle, n'hésitez pas à ajouter une réponse.

Merci encore à vous tous pour vos commentaires. En aparté, je n'ai pas d'annuler le bounty sur cette question -- je ne sais pas pourquoi il n'était pas attribuée automatiquement à la première réponse à ce fil de discussion, comme je l'ai été autrement s'attendait à arriver à partir de l'expérience précédente.


MODIFIER III

J'ai trouvé cette page web qui affiche comment la sous-classe de la fenêtre de l'application afin de pouvoir observer les robinets et avant que ces coups à la vue du contrôleur.

Il n'est pas nécessaire de mettre en œuvre son délégué partout dans l'application, seulement lorsque je veux observer les robinets sur UIWebView, donc cela peut très bien fonctionner pour une visionneuse de documents.

Jusqu'à présent, je peux observer les robinets, masquer et afficher la barre d'outils, et la UIWebView se comporte comme un affichage web. Je peux faire un zoom dans et hors de la vue web et le document est re-rendus correctement.

Alors qu'il serait bien d'apprendre à gérer les robinets dans un plus générique de la mode, cela ressemble à une excellente solution.

De quelle taille sont vos points de vue, et comment sont-ils qui se chevauchent? Est votre coutume UIView juste la taille de la UIToolbar, ou êtes-vous vouloir un appuyez n'importe où sur l'écran pour se cacher?
Les vues sont de la taille de l'écran. Ils sont parfaitement superposées. La coutume UIView est la taille de l'écran de sorte à accepter touche n'importe où sur l'écran.
Avez-vous essayé de l'annulation de la bascule lorsqu'un simple toucher progresse dans le glisser/zoom phase? Je suppose que l'annulation d'une horloge qui retarde le début de l'animation, juste assez longtemps pour détecter le changement de toucher-glisser/zoom?
Je ne sais pas d'un moyen de faire la distinction entre les touchez, puis faites glisser les événements, comme [event allTouches] tableau est vide. En raison de la tableau vide, je n'ai aucun moyen de l'émission d'une notification pour annuler la minuterie, par exemple.
Intéressant que vous avez annulé le bounty, même quand vous avez eu une réponse à votre question initiale. Je suis nouveau ici, mais il ne semble pas juste que la prime disparaît, même si vous avez changé votre question.

OriginalL'auteur Alex Reynolds | 2009-12-06