Pertanyaan UIScrollView menyentuh sentuhan vs. subview


Tolong bisakah seseorang membantu menyortir noob keluar? Saya telah memposting masalah ini di berbagai forum dan tidak menerima jawaban, sementara banyak pencarian untuk jawaban lain telah muncul stackOverflow, jadi saya berharap ini adalah tempatnya.

Saya punya BeachView.h (subkelas UIScrollView, gambar pantai berpasir) yang ditutupi dengan nomor acak Stone.h (subkelas dari UIImageView, PNG acak dari batu, userInteractionEnabled = YES untuk menerima sentuhan).

Jika pengguna menyentuh dan bergerak di pantai, ia harus menggulir. Jika pengguna mengetuk batu, itu harus memanggil metode "menyentuhStone". Jika pengguna mengetuk pantai di mana tidak ada batu, itu harus memanggil metode "menyentuhPantai".

Sekarang, saya menyadari ini terdengar sederhana mati. Semua orang dan semua mengatakan kepada saya bahwa jika ada sesuatu di UIScrollView yang menerima sentuhan yang harus melewati kontrol untuk itu. Jadi ketika saya menyentuh dan menyeret, itu harus menggulir; tetapi jika saya mengetuk, dan itu di atas batu, itu harus mengabaikan keran pantai dan menerima keran batu, ya?

Namun, tampaknya kedua pandangan tersebut menerima ketukan dan panggilan keduanya menyentuhStone AND touchingBeach. Selain itu, keran pantai terjadi lebih dulu, jadi saya bahkan tidak bisa memasukkan tanda bendera "jika dijamah".

Ini beberapa kode. Di BeachView.m


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
     if (self.decelerating) { didScroll = YES; }
        else { didScroll = NO; }

     UITouch *touch = [[event allTouches] anyObject];
     CGPoint touchLocation = [touch locationInView:touch.view];
     NSLog(@"touched beach = %@", [touch view]);
     lastTouch = touchLocation;
     [super touchesBegan:touches withEvent:event];
}

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

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
     if (didScroll == NO && isPaused == NO) { 
          [self touchedBeach:YES location:lastTouch];
     }
     [super touchesEnded:touches withEvent:event];
}

Di Stone.m


-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
     [parent stoneWasTouched]; // parent = ivar pointing from stone to beachview
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
     UITouch *touch = [[event allTouches] anyObject];
     CGPoint touchLocation = [touch locationInView:touch.view];
     NSLog(@"touched stone = %@", [touch view]);
     [parent touchedStone:YES location:touchLocation];
}

Setelah tap batu, NSLog saya terlihat seperti ini:


Touched beach = <BeachView: 0x1276a0>
ran touchedBeach
Touched Stone = <Stone: 0x1480c0>
ran touchedStone

Jadi itu sebenarnya menjalankan keduanya. Apa yang bahkan lebih aneh adalah jika saya mengambil sentuhan Mulai dan menyentuh Keluar dari Stone.m tapi tinggalkan userInteractionEnabled = YES, register beachView menyentuh keduanya, tetapi mengembalikan Batu ketika pandangannya menyentuh (kedua kalinya).


Touched beach = <BeachView: 0x1276a0>
ran touchedBeach
Touched beach = <Stone: 0x1480c0>
ran touchedBeach

Jadi, PLEASE, saya sudah mencoba untuk mengurutkan ini selama berhari-hari. Bagaimana cara membuatnya sehingga panggilan batu yang disadap hanya menyentuhStone dan panggilan pantai yang disadap hanya menyentuhPantai? Di mana saya salah?


7
2018-05-18 13:47


asal


Jawaban:


Sebelum iPhone OS 3.0, UIScrollView's hitTest: withEvent: metode selalu mengembalikan diri sehingga menerima UIEvent secara langsung, hanya meneruskannya ke subview yang sesuai jika dan ketika menentukan itu tidak terkait dengan menggulir atau zoom.

Saya tidak bisa mengomentari OS iPhone 3.0 seperti di bawah NDA, tetapi periksa "Catatan rilis SDK iPhone untuk iPhone OS 3.0 beta 5" :)

Jika Anda perlu menargetkan pra-3.0, Anda dapat mengganti hitTest: withEvent: di BeachView dan mengatur bendera untuk mengabaikan sentuhan pantai berikutnya jika CGPoint sebenarnya berada dalam sebuah batu.

Tetapi sudahkah Anda mencoba memindahkan panggilan ke [super touch *: withEvent:] dari akhir metode yang ditimpa Anda ke awal? Ini mungkin menyebabkan keran batu terjadi lebih dulu.


2
2018-05-18 14:23



Benar, iPhone SDK 3.0 dan lebih tinggi, jangan lewat sentuhan -touchesBegan: dan -touchesEnded: **UIScrollview** metode subclass lagi. Anda dapat menggunakan touchesShouldBegin dan touchesShouldCancelInContentView metode yang tidak sama.

Jika Anda benar-benar ingin mendapatkan sentuhan ini, miliki satu meretas yang memungkinkan ini.

Di subclass Anda dari UIScrollView menimpa hitTest metode seperti ini:

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

  UIView *result = nil;
  for (UIView *child in self.subviews)
    if ([child pointInside:point withEvent:event])
      if ((result = [child hitTest:point withEvent:event]) != nil)
        break;

  return result;
}

Ini akan mengalihkan kepada Anda subkelas sentuhan ini, namun Anda tidak dapat membatalkan sentuhan UIScrollViewkelas super.


3
2017-11-03 16:52



Saya memiliki masalah serupa dengan simpleView dan itu ditambahkan ke a scrollView , dan setiap kali saya menyentuh simpleView , yang scrollView digunakan untuk mendapatkan sentuhan dan bukannya simpleView, yang scrollView pindah. Untuk menghindari ini, saya menonaktifkan srcolling dari scrollView ketika pengguna menyentuh simpleView dan sebaliknya bergulir itu diaktifkan.

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

    UIView *result = [super hitTest:point withEvent:event] ;
    if (result == simpleView)
    {
        scrollView.scrollEnabled = NO ;
    }
    else
    {
        scrollView.scrollEnabled = YES ;
    }

    return result ;

}

0
2017-11-19 05:52



Ini mungkin terkait dengan bug di iOS7, tinjau masalah saya (laporan bug dikirimkan)

UIScrollView subclass telah mengubah perilaku di iOS7


0
2017-11-26 21:11