UITableView
内のUIScrollView
に問題があります。外部scrollView
をスクロールすると、table
は最初のタッチでwillSelect/didSelect
イベントを受け取りませんが、2番目のタッチでは受け取ります。さらに奇妙なことに、delegate
がそうでない場合でも、セル自体がタッチと強調表示状態を取得します。
私のビュー階層:
UIView
- UIScrollView (outerscroll)
- Some other views and buttons
- UITableView (tableView)
スクロールビュー内には、動的に展開/閉じられる追加のビューがあります。テーブルビューは、ビューの他の要素と一緒に上部に「固定」する必要があるため、このレイアウトを作成したのは、Appleが推奨する方法と同様の方法で要素を簡単に移動できるためです。スクロールが発生したときに変換を使用します。
テーブルビューは、outerscrollが次のように移動すると、変換効果で変換されます。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == self.outerScrollView) {
CGFloat tableOffset = scrollView.contentOffset.y - self.fixedHeaderFrame.Origin.y;
if (tableOffset > 0) {
self.tableView.contentOffset = CGPointMake(0, tableOffset);
self.tableView.transform = CGAffineTransformMakeTranslation(0, tableOffset);
}
else {
self.tableView.contentOffset = CGPointMake(0, 0);
self.tableView.transform = CGAffineTransformIdentity;
}
// Other similar transformations are done here, but not involving the table
}
私のセルでは、これらのメソッドを実装すると:
- (void)setSelected:(BOOL)selected {
[super setSelected:selected];
if (selected) {
NSLog(@"selected");
}
}
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
[super setHighlighted:highlighted animated:animated];
if (highlighted) {
NSLog(@"highlighted");
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
NSLog(@"touchesBegan");
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
NSLog(@"touchesEnded");
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesCancelled:touches withEvent:event];
NSLog(@"touchesCancelled");
}
Yは、失敗したときにこの出力を見ることができます(最初のタップ):
2014-02-10 13:04:40.940 MyOrderApp[5588:70b] highlighted
2014-02-10 13:04:40.940 MyOrderApp[5588:70b] touchesBegan
2014-02-10 13:04:40.978 MyOrderApp[5588:70b] touchesEnded
そして、これが機能する場合(2回目のタップ):
2014-02-10 13:05:30.359 MyOrderApp[5588:70b] highlighted
2014-02-10 13:05:30.360 MyOrderApp[5588:70b] touchesBegan
2014-02-10 13:05:30.487 MyOrderApp[5588:70b] touchesEnded
2014-02-10 13:05:30.498 MyOrderApp[5588:70b] expanded
最初のタップと2番目のタップの間では、他のフレームの変更、アニメーション、または他のビューの操作は行われません。また、大量にスクロールする場合にのみバグが表示されますが、わずか数ピクセルのスクロールではすべてが正常に機能し続けます。
いくつかのプロパティを変更することも試みましたが、運はありませんでした。私がやったことのいくつか:
userInteractionEnabled
を削除しますsetNeedsLayout
が発生したときに、テーブル、スクロール、およびメインビューでscrollViewDidScroll
への呼び出しを追加します。UITableViews
をUIScrollViews
に埋め込む予期しない動作に関するコメントを見ましたが、Appleの公式ドキュメントにはそのような警告が表示されないため、動作することを期待しています。
アプリはiOS7以降のみです。
誰も同様の問題を経験した?これはなぜですかどうすれば解決できますか?セルのタップジェスチャをインターセプトし、カスタムデリゲートなどで渡すことができると思いますが、テーブルに適切なイベントを受信させたいなので、UITableViewDelegate
は期待どおりに受信します。
Apple Documentation から、UITableView
をUIScrollView
の中に埋め込むべきではありません。
重要:UIScrollViewオブジェクトにUIWebViewまたはUITableViewオブジェクトを埋め込まないでください。これを行うと、2つのオブジェクトのタッチイベントが混同され、誤って処理される可能性があるため、予期しない動作が発生する可能性があります。
あなたの問題はあなたのUIScrollView
が何をするかに本当に関係しています。
ただし、必要に応じてテーブルビューを非表示にするだけの場合(私の場合)、スーパービューでUITableView
を移動できます。
ここに小さな例を書いた: https://github.com/rvirin/SoundCloud/
内側のUITableViewのscrollEnabled
プロパティはYES
のままにしておきます。これにより、内部のUITableView
がUIScrollView
のスクロール関連のタッチを正しく処理することを認識できます。
私はこの同じ問題に遭遇し、解決策を見つけました!!
スクロールビューが失敗したスクロールジェスチャー(タップ)をその子に送信するように、スクロールビューでdelaysTouchesBegan
をtrueに設定する必要があります。
var delaysTouchesBegan: Bool
-受信側がビューへの開始フェーズでタッチの送信を遅延させるかどうかを決定するブール値。
プロパティの値がYESの場合、ウィンドウはビューへのUITouchPhaseBeganフェーズでタッチオブジェクトの配信を一時停止します。ジェスチャ認識エンジンがその後ジェスチャを認識すると、これらのタッチオブジェクトは破棄されます。ただし、ジェスチャレコグナイザがジェスチャを認識しない場合、ウィンドウはこれらのオブジェクトをtouchesBegan:withEvent:メッセージ(および場合によってはtouchesMoved:withEvent:メッセージでタッチの現在位置を通知するメッセージ)でビューに配信します。
しかし、落とし穴があります...スクロールビューで直接行うと機能しません!
// Does NOT work
self.myScrollview.delaysTouchesBegan = true
どうやら、これはこのプロパティの設定が機能しないiOSのバグです(ありがとう、Apple)。ただし、簡単な回避策があります。スクロールビューのパンジェスチャでプロパティを直接設定します。案の定、これは私にとって完璧に機能しました:
// This works!!
self.myScrollview.panGestureRecognizer.delaysTouchesBegan = true
UiTableViewがタップを認識しないようです。あなたはそれを使用しようとしましたか:
- (BOOL)gestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UISwipeGestureRecognizer *)otherGestureRecognizer
{
if ([otherGestureRecognizer.view isKindOfClass:[UITableView class]]) {
return YES;
}
return NO;
}
Appleからのメモ:
gestureRecognizerまたはotherGestureRecognizerの一方の認識が他方によってブロックされる場合に呼び出されます。 YESを返すと、両方が同時に認識できるようになります。デフォルトの実装はNOを返します(デフォルトでは、2つのジェスチャを同時に認識できません)
注:YESを返すと、同時認識が保証されます。他のジェスチャーのデリゲートがYESを返す可能性があるため、NOを返すことで同時認識を防止することは保証されません。
それが役立つことを願っています。
ジェスチャレコグナイザーは、2つの埋め込みスクロールビューまたはサブクラスに対して正しく機能しません。
回避策を試してください。
透明、カスタム、およびセルUIButton
のすべてを適切なタグでオーバーレイするか、サブクラスUIButton
を使用して、インデックスパスプロパティを追加し、再利用セルで毎回上書きします。
このボタンをプロパティとしてカスタムセルに追加します。
クラスを採用しているUITableViewDelegateプロトコルを指す目的のUIControlEvent
(1つ以上)のターゲットを追加します。
IBでの選択を無効にし、コードから選択を手動で管理します。
このソリューションでは、単一/複数選択の場合に注意が必要です。
レガシーコードのUIScrollView内でscrollEnabledがNOのUITableViewに遭遇しました。既存の階層を簡単に変更したり、スクロールを有効にしたりすることはできませんでしたが、最初のタップの問題に対して次の回避策を考え出しました。
@interface YourOwnTableView : UITableView
@end
@implementation YourOwnTableView
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesCancelled:touches withEvent:event];
// Note that this is a hack and it can stop working at some point.
// Looks like a table view with scrollEnabled being NO does not handle cancellation cleanly,
// so let's repeat begin/end touch sequence here hoping it'll reset its own internal state properly
// but won't trigger cell selection (the touch passed is in its cancelled phase, perhaps there is a part
// of code inside which actually checks it)
[super touchesBegan:touches withEvent:event];
[super touchesEnded:touches withEvent:event];
}
@end
繰り返しますが、これは私の特定のケースで動作する回避策です。スクロールビュー内にテーブルビューを表示することは、依然として間違っています。
ネストされたUITableViewでも同じ問題があり、これに対する回避策が見つかりました。
innerTableView.scrollEnabled = YES;
innerTableView.alwaysBounceVertical = NO;
ユーザーが外側のビューをスクロールしたときにスクロールしないように、内側のテーブルビューの高さをセルの合計の高さと一致するように設定する必要があります。
お役に立てれば。
私の間違いはデリゲートメソッドを実装することでした:
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
私が実装するつもりだったものの代わりに:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
したがって、最初のセルがdeが選択されたときに、タップされた2番目のセルでのみ呼び出されます。オートコンプリートの助けを借りて行われた愚かな間違い。ここをさまよい、あなたも同じ過ちを犯したことに気づかないかもしれないあなたのための考えです。
View Controllerで、このコードをcellForRowAtIndexPathに配置します
cell.btnRowSelect.tag = indexPath.row
cell.btnRowSelect.addTarget(self, action: Selector("rowSelect:"), forControlEvents: .TouchUpInside)
この関数をviewControllerにも追加します。
func rowSelect (sender:UIButton) {
// "sendet.tag" give you the selected row
// do whatever you want to do in didSelectRowAtIndexPath
}
この関数「rowSelect」は、「sender.tag」として行「indexPath.row」を取得するdidSelectRowAtIndexPathとして機能します。
外側のスクロールビューを実際にスクロールするときに、セルを強調表示状態にしないなどのオプションを探すことをお勧めします。これは非常に扱いやすく、推奨される方法です。これを行うには、ブール値を取得し、以下のメソッドで切り替えます。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
Scrollviewは、ユーザーの意図がスクロールするかどうかを判断しようとしているため、意図的に最初のタッチを遅らせています。 delaysContentTouches
をNO
に設定すると、これをオフにできます。
それは、タッチテーブルビューの場合、適切に機能することです。また、同じView Controllerのスクロールビューも使用できます。
tableview.scrollEnabled = true;
他の回答が示すように、スクロールビューにテーブルビューを配置するべきではありません。とにかくUITableViewはUIScrollViewを継承するので、そこから混乱が生じると思います。この状況で私がいつもしていることは:
1)UITableViewControllerをサブクラス化し、プロパティUIView * headViewを含めます。
2)親UIViewControllerで、コンテナUIViewにすべての上位のものを作成します
3)カスタムUITableViewを初期化し、tableViewのビューをView Controllerのフルサイズに追加します
[self.view addSubview: self.myTableView.view];
4)headViewをUIViewガビンに設定します
self.tableView.headView = myHeadViewGubbins.
5)tableViewControllerメソッド内
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger *)section;
行う:
if ( section == 0 ) {
return self.headView;
}
これで、上部に他のシズルがたくさんあるテーブルビューができました。
楽しい!
私は同じ問題を抱えています。それから、lxxが言ったように「スクロールビューのネスト」を参照してください。
クロススクロールの例は、Stocksアプリケーションにあります。上部のビューはテーブルビューですが、下部のビューはページングモードを使用して構成された水平スクロールビューです。 3つのサブビューのうち2つはカスタムビューですが、3番目のビュー(ニュース記事を含む)は、水平スクロールビューのサブビューであるUITableView(UIScrollViewのサブクラス)です。ニュースビューまで水平にスクロールした後、そのコンテンツを垂直にスクロールできます。
仕事です