Mobile Safari memungkinkan Anda untuk berpindah halaman dengan memasukkan tampilan halaman horizontal UIScrollView dengan kontrol halaman di bagian bawah.
Saya mencoba meniru perilaku khusus ini di mana UIScrollView yang dapat digulir secara horizontal menunjukkan beberapa konten tampilan berikutnya.
Contoh yang diberikan Apple: PageControl menunjukkan cara menggunakan UIScrollView untuk paging horizontal, tetapi semua tampilan menggunakan lebar layar secara keseluruhan.
Bagaimana cara mendapatkan UIScrollView untuk menampilkan beberapa konten tampilan berikutnya seperti yang dilakukan Safari seluler?
ios
iphone
cocoa-touch
uiscrollview
firstresponder
sumber
sumber
Jawaban:
A
UIScrollView
dengan paging diaktifkan akan berhenti di kelipatan lebar frame (atau tinggi). Jadi, langkah pertama adalah mencari tahu seberapa lebar halaman yang Anda inginkan. Buat itu menjadi lebar fileUIScrollView
. Kemudian, atur ukuran subview Anda betapapun besar yang Anda inginkan, dan atur pusatnya berdasarkan kelipatanUIScrollView
lebarnya.Kemudian, karena Anda ingin melihat halaman lain, tentu saja, ditetapkan
clipsToBounds
untukNO
sebagai mhjoy dinyatakan. Bagian triknya sekarang adalah membuatnya menggulir ketika pengguna memulai tarik di luar rentangUIScrollView
bingkai. Solusi saya (ketika saya harus melakukan ini baru-baru ini) adalah sebagai berikut:Buat
UIView
subclass (yaituClipView
) yang akan berisiUIScrollView
dan subviews itu. Pada dasarnya, itu harus memiliki kerangka dari apa yang Anda anggapUIScrollView
akan terjadi dalam keadaan normal. TempatkanUIScrollView
di tengahClipView
. PastikanClipView
'sclipsToBounds
disetel keYES
jika lebarnya kurang dari tampilan induknya. Juga,ClipView
kebutuhan referensi keUIScrollView
.Langkah terakhir adalah mengganti
- (UIView *)hitTest:withEvent:
di dalamClipView
.- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { return [self pointInside:point withEvent:event] ? scrollView : nil; }
Ini pada dasarnya memperluas area sentuh
UIScrollView
ke bingkai tampilan induknya, persis seperti yang Anda butuhkan.Pilihan lain adalah melakukan subkelas
UIScrollView
dan mengganti- (BOOL)pointInside:(CGPoint) point withEvent:(UIEvent *) event
metodenya, namun Anda masih memerlukan tampilan kontainer untuk melakukan pemotongan, dan mungkin sulit untuk menentukan kapan harus kembaliYES
hanya berdasarkanUIScrollView
bingkai.CATATAN: Anda juga harus melihat hitTest: withEvent: modifikasi Juri Pakaste jika Anda mengalami masalah dengan interaksi pengguna subview.
sumber
UIScrollView
subclass untuk lazy loading of views (inlayoutSubviews
), dan menggunakan aclippingRect
untuk melakukanpointInside:withEvent:
pengujian. Bekerja dengan sangat baik, dan tidak diperlukan tampilan penampung tambahan.UIScrollView
subclass seperti @ohhorob tapi dengan pandangan wadah untuk kliping dan kembali[super pointInside:CGPointMake(self.contentOffset.x + self.frame.size.width/2, self.contentOffset.y + self.frame.size.height/2) withEvent:event];
dipointInside:withEvent:
. Ini bekerja dengan sangat baik dan tidak ada masalah dengan interaksi pengguna yang disebutkan dalam jawaban @ JuriPakaste.- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
ditambahkan ke UIScrollViewDelegate di iOS5 berarti Anda tidak perlu melalui palaver ini lagi.The
ClipView
solusi di atas bekerja untuk saya, tapi saya harus melakukan yang berbeda-[UIView hitTest:withEvent:]
implementasi. Versi Ed Marty tidak mendapatkan interaksi pengguna yang bekerja dengan tampilan gulir vertikal yang saya miliki di dalam yang horizontal.Versi berikut berfungsi untuk saya:
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event { UIView* child = nil; if ((child = [super hitTest:point withEvent:event]) == self) return self.scrollView; return child; }
sumber
Tetapkan ukuran bingkai scrollView sebagai ukuran halaman Anda:
[self.view addSubview:scrollView]; [self.view addGestureRecognizer:mainScrollView.panGestureRecognizer];
Sekarang Anda dapat menggeser
self.view
, dan konten di scrollView akan digulir.Juga gunakan
scrollView.clipsToBounds = NO;
untuk mencegah kliping konten.sumber
Saya akhirnya menggunakan UIScrollView kustom sendiri karena menurut saya itu adalah metode tercepat dan sederhana. Namun, saya tidak melihat kode persisnya, jadi saya pikir saya akan membagikannya. Kebutuhan saya adalah UIScrollView yang memiliki konten kecil dan oleh karena itu UIScrollView itu sendiri kecil untuk mencapai pengaruh paging. Karena pos menyatakan Anda tidak dapat menggeser. Tapi sekarang kamu bisa.
Buat kelas CustomScrollView dan subclass UIScrollView. Kemudian yang perlu Anda lakukan adalah menambahkan ini ke file .m:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { return (point.y >= 0 && point.y <= self.frame.size.height); }
Ini memungkinkan Anda untuk menggulir dari sisi ke sisi (horizontal). Sesuaikan batas yang sesuai untuk mengatur area sentuh gesek / gulir Anda. Nikmati!
sumber
Saya telah membuat implementasi lain yang dapat mengembalikan tampilan gulir secara otomatis. Jadi tidak perlu memiliki IBOutlet yang akan membatasi penggunaan ulang dalam proyek.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { if ([self pointInside:point withEvent:event]) { for (id view in [self subviews]) { if ([view isKindOfClass:[UIScrollView class]]) { return view; } } } return nil; }
sumber
Saya memiliki modifikasi lain yang berpotensi berguna untuk implementasi hitTest ClipView. Saya tidak suka harus memberikan referensi UIScrollView ke ClipView. Implementasi saya di bawah ini memungkinkan Anda untuk menggunakan kembali kelas ClipView untuk memperluas area uji-hit apa pun, dan tidak harus menyediakannya dengan referensi.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { if (([self pointInside:point withEvent:event]) && (self.subviews.count >= 1)) { // An extended-hit view should only have one sub-view, or make sure the // first subview is the one you want to expand the hit test for. return [self.subviews objectAtIndex:0]; } return nil; }
sumber
Saya menerapkan saran yang diberi suara positif di atas, tetapi
UICollectionView
saya menggunakan sesuatu yang dianggap keluar dari bingkai untuk keluar dari layar. Ini menyebabkan sel-sel di sekitar hanya merender di luar batas saat pengguna menggulir ke arahnya, yang tidak ideal.Apa yang akhirnya saya lakukan adalah meniru perilaku scrollview dengan menambahkan metode di bawah ini ke delegasi (atau
UICollectionViewLayout
).- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity { if (velocity.x > 0) { proposedContentOffset.x = ceilf(self.collectionView.contentOffset.x / pageSize) * pageSize; } else { proposedContentOffset.x = floorf(self.collectionView.contentOffset.x / pageSize) * pageSize; } return proposedContentOffset; }
Ini untuk menghindari pendelegasian tindakan gesek seluruhnya, yang juga merupakan bonus. The
UIScrollViewDelegate
memiliki metode serupa yang disebutscrollViewWillEndDragging:withVelocity:targetContentOffset:
yang dapat digunakan untuk UITableViews halaman dan UIScrollViews.sumber
Aktifkan kejadian tap tembak pada tampilan anak dari tampilan gulir sambil mendukung teknik pertanyaan SO ini. Menggunakan referensi ke tampilan gulir (self.scrollView) agar mudah dibaca.
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event { UIView *hitView = nil; NSArray *childViews = [self.scrollView subviews]; for (NSUInteger i = 0; i < childViews.count; i++) { CGRect childFrame = [[childViews objectAtIndex:i] frame]; CGRect scrollFrame = self.scrollView.frame; CGPoint contentOffset = self.scrollView.contentOffset; if (childFrame.origin.x + scrollFrame.origin.x < point.x + contentOffset.x && point.x + contentOffset.x < childFrame.origin.x + scrollFrame.origin.x + childFrame.size.width && childFrame.origin.y + scrollFrame.origin.y < point.y + contentOffset.y && point.y + contentOffset.y < childFrame.origin.y + scrollFrame.origin.y + childFrame.size.height ){ hitView = [childViews objectAtIndex:i]; return hitView; } } hitView = [super hitTest:point withEvent:event]; if (hitView == self) return self.scrollView; return hitView; }
Tambahkan ini ke tampilan anak Anda untuk merekam acara sentuh:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
(Ini adalah variasi dari solusi user1856273. Dibersihkan untuk keterbacaan dan memasukkan perbaikan bug Bartserk. Saya berpikir untuk mengedit jawaban user1856273 tetapi perubahan itu terlalu besar untuk dibuat.)
sumber
versi saya Menekan tombol yang ada di gulungan - kerja =)
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { UIView* child = nil; for (int i=0; i<[[[[self subviews] objectAtIndex:0] subviews] count];i++) { CGRect myframe =[[[[[self subviews] objectAtIndex:0] subviews]objectAtIndex:i] frame]; CGRect myframeS =[[[self subviews] objectAtIndex:0] frame]; CGPoint myscroll =[[[self subviews] objectAtIndex:0] contentOffset]; if (myframe.origin.x < point.x && point.x < myframe.origin.x+myframe.size.width && myframe.origin.y+myframeS.origin.y < point.y+myscroll.y && point.y+myscroll.y < myframe.origin.y+myframeS.origin.y +myframe.size.height){ child = [[[[self subviews] objectAtIndex:0] subviews]objectAtIndex:i]; return child; } } child = [super hitTest:point withEvent:event]; if (child == self) return [[self subviews] objectAtIndex:0]; return child; }
tetapi hanya [[self subviews] objectAtIndex: 0] yang harus berupa scroll
sumber
Berikut adalah jawaban Swift berdasarkan jawaban Ed Marty tetapi juga termasuk modifikasi oleh Juri Pakaste untuk memungkinkan ketukan tombol dll di dalam scrollview.
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { let view = super.hitTest(point, with: event) return view == self ? scrollView : view }
sumber