8

次のサブビュー チェーンがあります。

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

サブクラスでは、ビュー コントローラーがナビゲーション バーを非表示にする を発行するだけでなく、 を介してシングル タップ タッチで-touchesEnded:forEvent:を表示および非表示にするために、そのメソッドをオーバーライドします。UIToolbarCAAnimationNSNotification

UIWebViewView Controllerのビューにサブビューとして追加しないと、これは正しく機能します。

UIWebView次に、ビュー コントローラーのビューのサブビューとしてを追加すると、 が表示されUIToolbarず、アニメーション効果が得られません。はUIWebViewタッチに応答しますが、サブクラスは応答しUIViewません。ただし、通知は発生し、ナビゲーション バーは非表示になります。

これらのサブビューを次のように配置する最良の方法は何ですか?

  1. UIToolbarを画面上または画面外にスライドさせることができます
  2. 表示されUIWebViewている は、通常のタッチ イベントを受け取ることができます: ズームイン/ズームアウト、ダブルタップしてズーム レベルをリセットします。

正解は両方の基準を満たします。


編集

これである程度の進歩はありましたが、タッチイベントを区別できないため、ツールバーの非表示/表示メソッドを起動してはならないときに起動します。

ビュー コントローラーのビュー プロパティをUIViewサブクラスに設定し、.-subviews経由で配列の先頭に Web ビューを挿入しました[self.view insertSubview:self.webView atIndex:0]

UIViewサブクラスに次のメソッドを追加しました。

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

このメソッドは、 を非表示/表示する-timedを-toggleToolbarView:トリガーします。NSTimerCAAnimationUIToolbar*

問題は、タッチとドラッグの組み合わせが発生-toggleToolbarView:することです (および Web ビューのズームインまたはズームアウト)。私がやりたいの-toggleToolbarView:は、タッチのみのイベントがある場合にのみ発生させることです。

上記のメソッドを呼び出すと、タッチを吸う-hitTest:withEvent:ように見えます。UIWebView2 つのステートメントNSLogUIView*、親から返される がまたは(画面上でタッチされている場合) であることを示しています。配列が空であるため、タッチ イベントの種類を区別できません。UIView*-hitTest:withEvent:UIWebViewUIToolbartouches

もう 1 つ試してみたいことがありますが、他の人からの簡単な提案がある場合に備えて、この試みをここに記録しておきます。いつもお世話になっております。


編集Ⅱ

UIWebViewピンチ/ズームとダブルタップへの応答を取得することで、いくつかの進歩がありました。シングルタップでツールバーを非表示/表示することもできます。

UIWebViewインスタンスの privateをいじる代わりに、UIScrollerその内部の private にアクセスしますUIWebDocumentView。ただし、ズームインした後、Web ビュー全体が適切に再描画されません。

これが意味すること: たとえば、ドキュメントに PDF または SVG ベクター イラストが含まれている場合、レンダリングされた PDF またはベクター イラスト コンテンツを構成する画像タイルが再レンダリングされていないかのように、拡大するとコンテンツがぼやけます。新しいズーム倍率。

これを文書化する目的で、私は次のことを行います。

  1. 代わりUIView (subclass)ViewerOverlayView

  2. UIWebView呼ばれるViewerWebView

どちらもそれぞれUIViewとのサブクラスUIWebViewです。

MyViewerWebViewには新しいivarが含まれています(@property+付き@synthesized):

UIView *privateWebDocumentView;

(これUIViewは実際にはインスタンスへのポインターですが、UIWebDocumentViewApple がアプリに拒否のフラグを立てるのを避けるために、UIViewタッチ イベントを渡すという単純な目的でこれを にキャストします。)

ViewerWebViewオーバーライドの実装-hitTest:withEvent:

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

MyViewerOverlayViewには新しいivarが含まれています(@property+付き@synthesized):

ViewerWebView *webView;
UIView *webDocumentView;

オーバーレイ ビューの実装では、オーバーライドして-touchesBegan:withEvent:-touchesMoved:withEvent:-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];
}

ビューコントローラーでは、次のことを行います。

  1. をインスタンス化しViewerOverlayView、View Controller のviewプロパティのサブビューとして追加します

  2. ビューコントローラーのViewerWebViewインスタンスをインスタンス化し、サブビュー配列の一番下に挿入します

  3. ViewerOverlayViewWeb ビュー プロパティをビュー コントローラーの Web ビューに設定します。

これで、トランスペアレントViewerOverlayViewはズーム/ストレッチ/ダブルタップ タッチ イベントをViewerWebViewインスタンスのプライベートUIWebDocumentViewインスタンスに正しく渡します。その後UIWebDocumentView、サイズが変更されます。

ただし、コンテンツは新しいスケールで再描画されません。そのため、ベクターベースのコンテンツ (PDF、SVG など) は、拡大するとぼやけて見えます。

スケーリングなしのビューは次のようになります。素晴らしくシャープです。

UIWebView、スケーリングなし

ズームインしたときのビューは次のようになります。これは、このすべてのカスタム イベント変更がない場合ほど鮮明ではありません。

UIWebView、ぼやけたズーム

興味深いことに、2 つのズーム レベル間でのみ再スケーリングできます。任意のレベルでズームインするためにダブルタップストレッチを停止すると、上の画像に表示されているフレームに「スナップバック」します。

とその内部インスタンスを試し-setNeedsDisplayました。どちらのメソッド呼び出しも機能しませんでした。ViewerWebViewUIWebDocumentView

プライベート メソッドを避けたいにもかかわらず、最終的にはこのアプリを承認してもらいたいので、プライベートメソッドへのアクセスに関する次の提案も試しました。UIWebDocumentView-_webCoreNeedsDisplay

[(UIWebDocumentView *)self.webDocumentView _webCoreNeedsDisplay];

unrecognized selectorこのメソッドにより、アプリが例外からクラッシュしました。おそらく、Apple はこのメソッドの名前を SDK 3.0 から 3.1.2 に変更しました。

Web ビューのコンテンツを再描画する方法がない限り、Web ビューを適切に機能させるには、透明なオーバーレイ ビューを破棄して Web ビューを描画する必要があると思います。それは最悪です!

スケーリング後の再描画について誰かが考えている場合は、お気軽に回答を追加してください。

ご意見をお寄せいただきありがとうございました。余談ですが、私はこの質問の報奨金をキャンセルしませんでした。以前の経験から予想していたように、このスレッドへの最初の回答に賞金が自動的に授与されなかった理由はわかりません。


編集Ⅲ

アプリケーションウィンドウをサブクラス化してタップを監視し、それらのタップをView Controllerに転送する方法を示すこのWebページを見つけました。

アプリケーション全体のどこにでもデリゲートを実装する必要はなく、 のタップを観察したい場所にのみ実装する必要があるUIWebViewため、これはドキュメント ビューアーにとって非常にうまく機能する可能性があります。

これまでのところ、タップを観察したり、ツールバーを非表示および表示したりUIWebView、Web ビューのように動作したりできます。Web ビューをズームインおよびズームアウトでき、ドキュメントは適切に再レンダリングされます。

より一般的な方法でタップを管理する方法を学ぶのは良いことですが、これは非常に優れたソリューションのように見えます.

4

4 に答える 4

3

私があなたの質問を読んでいる方法では、イベントをインターセプトして何らかのアクション (ツールバーのアニメーション化、通知の投稿) をトリガーし、イベントが自然な目的地に到達できるようにしたいと考えています。

これを行おうとしている場合は、 をUIToolbarのサブビューとして直接配置しUIViewController.viewます。直接のUIWebViewサブビューも残ります。

はのUIViewController.viewサブクラスであるUIView必要があり、オーバーライドする必要があります

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

ビューは、イベントを受信したいビュー (UIToolbar または UIWebView) を送り返すことで、イベント処理の一部になることを回避できますが、必要なアクションをトリガーする機会は引き続き得られます。

例は次のとおりです。

- (UIView *)hitTest:(CGPoint) point withEvent:(UIEvent *)event {
    UIView* subview = [super hitTest:point withEvent:event];
    /* Use the event.type, subview to decide what actions to perform */
    // Optionally nominate a default event recipient if your view is not completely covered by subviews
    if (subview == self) return self.webViewOutlet;
    return subview;
}
于 2009-12-09T08:02:01.863 に答える
1

テイク 2: 上記と同じですが、次のようにメソッドをオーバーライドします。

-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    hasMoved = NO;
    [[[webView subviews] objectAtIndex:0] touchesBegan:touches withEvent:event];
}

-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    hasMoved = YES;
    [[[webView subviews] objectAtIndex:0] touchesMoved:touches withEvent:event];
}

-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    if(!hasMoved) [self toggleToolbar];
    [[[webView subviews] objectAtIndex:0] touchesEnded:touches withEvent:event];
}

-(void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    [[[webView subviews] objectAtIndex:0] touchesCancelled:touches withEvent:event];
}

これはうまくいきます...ちょっと。これにより、Web ビューをドラッグすることができます。しかし、他のタッチイベントを適切に処理していません (リンクをたどる、ピンチするなど)。そう。おそらくあまり役​​に立ちませんが、私にとっては進歩でした!

于 2009-12-16T09:39:21.480 に答える
1

UIView サブクラスをビュー コントローラーのビューに追加することから始めて、説明どおりに UIWebView を追加すると、UIWebView は UIView サブクラスを完全にカバーします (質問のコメントで述べたように、両方のビューが完全に重複していると仮定します)。

あなたはそれを望んでいないように思えます.Webビューの上にサブクラスが必要です。それらをView Controllerのビューに逆の順序で追加する(Webビューを追加してからカスタムUIViewを追加する)か、次を呼び出す必要があります。

[viewContoller.view insertSubview:webView belowSubview:subclass];

それはさておき、タッチ イベントをインターセプトする方法について説明しましょう。似たようなことを可能にする別のアプローチを提案します。これが私が作成した質問と回答です。この手法の詳細については、そこを参照してください。

アイデアは、UIApplication をサブクラス化し、-sendEvent: メソッドをオーバーライドすることです。あなたのものは次のようになります。

- (void)sendEvent:(UIEvent *)event {
    [super sendEvent:event];

    NSSet *allTouches = [event allTouches];
    if ([allTouches count] > 0) {
        UITouch *touch = [allTouches anyObject];
        if ([allTouches count] == 1 && touch.phase == UITouchPhaseEnded && touch.tapCount == 1) {
            // Do something here
        }
    }
}

これで 100% 達成できるとは思いませんが、それがスタートであることを願っています。特に、扱っている UITouches のプロパティをチェックする if 条件を試してみるとよいでしょう。正しいチェックを含めているのか、それともあなたが探している正確な条件をキャッチできるのかどうかはわかりません.

幸運を!

于 2009-12-09T08:02:51.853 に答える
1

さて、これはどうですか:

-カスタムUIViewサブクラスで、へのポインターを追加しますUIWebView

-ivar をUIViewサブクラスに追加します。BOOL hasMoved;

-UIViewサブクラスでは、次のようにタッチ メソッドをオーバーライドします。

-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    hasMoved = NO;
    [webView touchesBegan:touches withEvent:event];
}

-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    hasMoved = YES;
    [webView touchesMoved:touches withEvent:event];
}

-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    if(!hasMoved) [self hideToolbar];
    [webView touchesEnded:touches withEvent:event];
}

次に、カスタム ビューを Web ビューの上に配置します。単純すぎるように思えますが、おそらくそうです -- 複数のタッチを正しく処理するにはいくつかの魔法が必要になる場合がありNSTimertouchesBegan:メソッドで a を遅延として使用してダブルタップの状況を処理する必要がある場合もありますが、一般的な考え方は適切だと思います.. 。多分?

于 2009-12-11T06:12:39.620 に答える