Tiru Facebook hide / show expand / contracting Navigation Bar

128

Di aplikasi iPhone Facebook iOS7 yang baru, ketika pengguna menggulir ke atas, navigationBarperlahan - lahan menyembunyikan dirinya ke titik di mana ia sepenuhnya menghilang. Kemudian ketika pengguna menggulir ke bawah navigationBarsecara bertahap menunjukkan dirinya.

Bagaimana Anda menerapkan perilaku ini sendiri? Saya menyadari solusi berikut ini tetapi menghilang segera dan tidak terikat pada kecepatan gerakan gulir pengguna sama sekali.

[navigationController setNavigationBarHidden: YES animated:YES];

Saya harap ini bukan duplikat karena saya tidak yakin bagaimana cara terbaik untuk menggambarkan perilaku "memperluas / membuat kontrak".

El Mocoso
sumber
2
Masalah yang sama: stackoverflow.com/questions/21929220/... Perhatikan bahwa sangat sulit untuk benar - benar cocok dengan perilaku Safari. Ada beberapa aturan yang sangat, sangat rumit di sana!
Fattie
1
Dalam proyek saya, saya menggunakan proyek ini dan itu bekerja dengan baik. Lihatlah dokumentasinya.
Vinicius
github.com/bryankeller/BLKFlexibleHeightBar akan memungkinkan Anda melakukan apa yang Anda inginkan dan banyak lagi. Ini memungkinkan Anda menentukan dengan tepat bagaimana bilah terlihat pada setiap tahap transisi dari dimaksimalkan ke diminimalkan. Bahkan memungkinkan Anda menentukan perilaku Anda sendiri, sehingga dapat bertindak seperti Safari, Facebook, atau aplikasi lain.
blkhp19
Saya tidak menggunakan uinavigationbar tetapi menambahkan uiview sebagai gantinya. Tampilan mereplikasi bilah navigasi akan diperluas dan dikontrak berdasarkan gulir. Saya menggunakan metode delegasi scrollViewDidScroll untuk mencapai tugas. Anda mungkin ingin memeriksa dan mengeksekusi kode sumber di bawah ini .. dropbox.com/s/b2c0zw6yvchaia5/FailedBanks.zip?dl=0
Deepak Thakur

Jawaban:

162

Solusi yang diberikan oleh @peerless adalah awal yang baik, tetapi itu hanya memulai animasi setiap kali menyeret dimulai, tanpa mempertimbangkan kecepatan gulir. Ini menghasilkan pengalaman choppier daripada yang Anda dapatkan di aplikasi Facebook. Untuk mencocokkan perilaku Facebook, kita perlu:

  • sembunyikan / tampilkan navbar pada kecepatan yang sebanding dengan laju hambatan
  • mulai animasi untuk sepenuhnya menyembunyikan bilah jika gulir berhenti ketika bilah disembunyikan sebagian
  • pudar item navbar saat bilah menyusut.

Pertama, Anda membutuhkan properti berikut:

@property (nonatomic) CGFloat previousScrollViewYOffset;

Dan berikut adalah UIScrollViewDelegatemetodenya:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGRect frame = self.navigationController.navigationBar.frame;
    CGFloat size = frame.size.height - 21;
    CGFloat framePercentageHidden = ((20 - frame.origin.y) / (frame.size.height - 1));
    CGFloat scrollOffset = scrollView.contentOffset.y;
    CGFloat scrollDiff = scrollOffset - self.previousScrollViewYOffset;
    CGFloat scrollHeight = scrollView.frame.size.height;
    CGFloat scrollContentSizeHeight = scrollView.contentSize.height + scrollView.contentInset.bottom;

    if (scrollOffset <= -scrollView.contentInset.top) {
        frame.origin.y = 20;
    } else if ((scrollOffset + scrollHeight) >= scrollContentSizeHeight) {
        frame.origin.y = -size;
    } else {
        frame.origin.y = MIN(20, MAX(-size, frame.origin.y - scrollDiff));
    }

    [self.navigationController.navigationBar setFrame:frame];
    [self updateBarButtonItems:(1 - framePercentageHidden)];
    self.previousScrollViewYOffset = scrollOffset;
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    [self stoppedScrolling];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView 
                  willDecelerate:(BOOL)decelerate
{
    if (!decelerate) {
        [self stoppedScrolling];
    }
}

Anda juga akan memerlukan metode pembantu ini:

- (void)stoppedScrolling
{
    CGRect frame = self.navigationController.navigationBar.frame;
    if (frame.origin.y < 20) {
        [self animateNavBarTo:-(frame.size.height - 21)];
    }
}

- (void)updateBarButtonItems:(CGFloat)alpha
{
    [self.navigationItem.leftBarButtonItems enumerateObjectsUsingBlock:^(UIBarButtonItem* item, NSUInteger i, BOOL *stop) {
        item.customView.alpha = alpha;
    }];
    [self.navigationItem.rightBarButtonItems enumerateObjectsUsingBlock:^(UIBarButtonItem* item, NSUInteger i, BOOL *stop) {
        item.customView.alpha = alpha;
    }];
    self.navigationItem.titleView.alpha = alpha;
    self.navigationController.navigationBar.tintColor = [self.navigationController.navigationBar.tintColor colorWithAlphaComponent:alpha];
}

- (void)animateNavBarTo:(CGFloat)y
{
    [UIView animateWithDuration:0.2 animations:^{
        CGRect frame = self.navigationController.navigationBar.frame;
        CGFloat alpha = (frame.origin.y >= y ? 0 : 1);
        frame.origin.y = y;
        [self.navigationController.navigationBar setFrame:frame];
        [self updateBarButtonItems:alpha];
    }];
}

Untuk perilaku yang sedikit berbeda, ganti baris yang memposisikan ulang bar saat menggulir ( elseblokir scrollViewDidScroll) dengan yang ini:

frame.origin.y = MIN(20, 
                     MAX(-size, frame.origin.y - 
                               (frame.size.height * (scrollDiff / scrollHeight))));

Ini memposisikan bilah berdasarkan persentase gulir terakhir, bukan jumlah absolut, yang menghasilkan pudar yang lebih lambat. Perilaku aslinya lebih seperti Facebook, tapi saya juga suka yang ini.

Catatan: Solusi ini hanya untuk iOS 7+. Pastikan untuk menambahkan cek yang diperlukan jika Anda mendukung versi iOS yang lebih lama.

Wayne
sumber
Ini bekerja. Kecuali Anda mengasumsikan item tombol bar memiliki tampilan kustom. Dalam kasus saya, saya tidak memiliki tampilan khusus. Jadi di atas tidak menyembunyikan tombol bar. Saya pikir solusi @peerless lebih baik untuk menyembunyikan dan menunjukkan item navbar
Dhanush
1
Kamu benar. Saya mungkin melihat ke solusi yang lebih umum akhir pekan ini. Seharusnya tidak terlalu sulit untuk menargetkan item default customView.
Wayne
Saya belum membahas masalah Dhanush tetapi saya memiliki pembaruan.
Wayne
1
Saya memperbaiki masalah dengan tombol stock bar, tetapi kode ini memiliki masalah lain. Jika ScrollView's contentSizelebih kecil dari frame, animasi geser tidak bekerja. Pastikan juga Anda mengatur ulang alpha semua item navigasi kembali ke 1.0 in viewDidDisappear.
Tanpa hukum
1
Apakah Anda memeriksa solusi ini pada pengontrol yang menggunakan AutoLayout? Dalam kasus saya semuanya bekerja dengan baik tanpa AutoLayout, tetapi ketika dihidupkan, masih terlihat beberapa strip putih aneh di bawahnya
Aliaksandr B.
52

EDIT: Hanya untuk iOS 8 ke atas.

Anda dapat mencoba menggunakannya

self.navigationController.hidesBarsOnSwipe = YES;

Bekerja untukku.

Jika pengodean Anda cepat, Anda harus menggunakan cara ini (dari https://stackoverflow.com/a/27662702/2283308 )

navigationController?.hidesBarsOnSwipe = true
Pedro Romão
sumber
ini tidak tersedia di iOS <8.0
Petar
1
Seperti yang dikatakan pet60t0, dan saya minta maaf, hanya berfungsi untuk iOS 8 ke atas.
Pedro Romão
4
Bagaimana Anda membawanya kembali setelah itu?
C0D3
perilaku itu agak aneh. Saya tidak banyak menjelajahi, tetapi jika Anda menggulir lebih cepat itu akan muncul lagi.
Pedro Romão
@ PedroRomão jawaban yang bagus.
Gagan_iOS
43

Berikut ini satu lagi implementasi: TLYShyNavBar v1.0.0 dirilis!

Saya memutuskan untuk membuat sendiri setelah mencoba solusi yang disediakan, dan bagi saya, mereka berkinerja buruk, memiliki penghalang yang tinggi untuk masuk dan kode plat boiler, atau tidak memiliki tampilan ekstensi di bawah navbar. Untuk menggunakan komponen ini, yang harus Anda lakukan adalah:

self.shyNavBarManager.scrollView = self.scrollView;

Oh, dan itu adalah pertempuran yang diuji di aplikasi kita sendiri.

Mazyod
sumber
@TimArnold Terima kasih atas tanggapan Anda! Saya telah memperbaiki masalah itu, hanya belum memperbarui podnya> _ <akan melakukannya sekarang! .. Pod diperbarui!
Mazyod
Saya akan mencobanya lagi! Terima kasih!
Tim Camber
Saya menghargai bantuan Anda. Sepertinya komit Anda membantu. Saya masih mendapatkan masalah aneh di mana UICollectionView saya tidak diubah ukurannya dengan benar seperti bilah nav, dan begitu sel naik di mana bilah nav USED akan terpotong, karena mereka berada di luar batas tampilan koleksi. Apakah Anda tahu mengapa ini terjadi?
Tim Camber
4
Angka: menemukan solusi saya beberapa detik setelah menulis komentar ini. Saya harus memastikan extendedLayoutIncludesOpaqueBarssudah diatur YESpada sayaUICollectionViewController
Tim Camber
@TimArnold Itu luar biasa! Ada pria lain yang memiliki masalah yang sama , dan semoga solusi Anda akan membantunya.
Mazyod
33

Anda dapat melihat GTScrollNavigationBar saya . Saya telah mensubklasifikasikan UINavigationBar untuk membuatnya gulir berdasarkan gulir dari UIScrollView.

Catatan: Jika Anda memiliki bilah navigasi OPAQUE, scrollview harus BUKA ketika bilah navigasi HIDDEN. Inilah yang dilakukan oleh GTScrollNavigationBar. (Sama seperti di misalnya Safari di iOS.)

Thuy
sumber
BTW untuk siapa saja yang membaca, persis bagaimana cara memanggil initWithNavigationBarClass ... stackoverflow.com/questions/22286166
Fattie
@ Terima pria pekerja hebat! Jadi saya memiliki ini berfungsi dengan baik pada pengontrol tampilan tabel kecuali satu hal ... Saya telah menarik untuk menyegarkan diimplementasikan di atas. Menjadi aneh ketika mencoba menarik ke bawah dan menyegarkan. Mungkinkah ada solusi untuk ini?
aherrick
@ Thuy juga ... katakanlah saya punya view controller di mana saya telah mengimplementasikan tampilan tabel di bagian bawah. Saya ingin terhubung ke tampilan tabel yang berfungsi, namun saya memiliki tampilan lain yang duduk di atas tampilan tabel yang ingin saya hilangkan juga. Bagaimana ini bisa berhasil?
aherrick
25

iOS8 menyertakan properti untuk menyembunyikan bilah navigasi secara gratis. Ada video WWDC yang menunjukkannya, cari "View Advancements Advancements in iOS 8".

Contoh :

class QuotesTableViewController: UITableViewController {

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    navigationController?.hidesBarsOnSwipe = true
}

}

Properti lainnya:

class UINavigationController : UIViewController {

    //... truncated

    /// When the keyboard appears, the navigation controller's navigationBar toolbar will be hidden. The bars will remain hidden when the keyboard dismisses, but a tap in the content area will show them.
    @availability(iOS, introduced=8.0)
    var hidesBarsWhenKeyboardAppears: Bool
    /// When the user swipes, the navigation controller's navigationBar & toolbar will be hidden (on a swipe up) or shown (on a swipe down). The toolbar only participates if it has items.
    @availability(iOS, introduced=8.0)
    var hidesBarsOnSwipe: Bool
    /// The gesture recognizer that triggers if the bars will hide or show due to a swipe. Do not change the delegate or attempt to replace this gesture by overriding this method.
    @availability(iOS, introduced=8.0)
    var barHideOnSwipeGestureRecognizer: UIPanGestureRecognizer { get }
    /// When the UINavigationController's vertical size class is compact, hide the UINavigationBar and UIToolbar. Unhandled taps in the regions that would normally be occupied by these bars will reveal the bars.
    @availability(iOS, introduced=8.0)
    var hidesBarsWhenVerticallyCompact: Bool
    /// When the user taps, the navigation controller's navigationBar & toolbar will be hidden or shown, depending on the hidden state of the navigationBar. The toolbar will only be shown if it has items to display.
    @availability(iOS, introduced=8.0)
    var hidesBarsOnTap: Bool
    /// The gesture recognizer used to recognize if the bars will hide or show due to a tap in content. Do not change the delegate or attempt to replace this gesture by overriding this method.
    @availability(iOS, introduced=8.0)
    unowned(unsafe) var barHideOnTapGestureRecognizer: UITapGestureRecognizer { get }
}

Ditemukan melalui http://natashatherobot.com/navigation-bar-interactions-ios8/

Michael Peterson
sumber
12

Saya punya semacam solusi cepat dan kotor untuk itu. Belum pernah melakukan pengujian mendalam, tetapi inilah idenya:

Properti itu akan menyimpan semua item di navbar untuk kelas UITableViewController saya

@property (strong, nonatomic) NSArray *navBarItems;

Di kelas UITableViewController yang sama yang saya miliki:

-(void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
{
    if([[[UIDevice currentDevice] systemVersion] floatValue] < 7.0f){
        return;
    }

    CGRect frame = self.navigationController.navigationBar.frame;
    frame.origin.y = 20;

    if(self.navBarItems.count > 0){
        [self.navigationController.navigationBar setItems:self.navBarItems];
    }

    [self.navigationController.navigationBar setFrame:frame];
}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if([[[UIDevice currentDevice] systemVersion] floatValue] < 7.0f){
        return;
    }

    CGRect frame = self.navigationController.navigationBar.frame;
    CGFloat size = frame.size.height - 21;

    if([scrollView.panGestureRecognizer translationInView:self.view].y < 0)
    {
        frame.origin.y = -size;

        if(self.navigationController.navigationBar.items.count > 0){
            self.navBarItems = [self.navigationController.navigationBar.items copy];
            [self.navigationController.navigationBar setItems:nil];
        }
    }
    else if([scrollView.panGestureRecognizer translationInView:self.view].y > 0)
    {
        frame.origin.y = 20;

        if(self.navBarItems.count > 0){
            [self.navigationController.navigationBar setItems:self.navBarItems];
        }
    }

    [UIView beginAnimations:@"toggleNavBar" context:nil];
    [UIView setAnimationDuration:0.2];
    [self.navigationController.navigationBar setFrame:frame];
    [UIView commitAnimations];
}

Itu hanya untuk ios> = 7, itu jelek saya tahu tetapi cara cepat untuk mencapai ini. Ada komentar / saran dipersilahkan :)

tak ada taranya
sumber
12

Ini berfungsi untuk iOS 8 dan di atasnya dan memastikan bahwa bilah status masih mempertahankan latar belakangnya

self.navigationController.hidesBarsOnSwipe = YES;
CGRect statuBarFrame = [UIApplication sharedApplication].statusBarFrame;
UIView *statusbarBg = [[UIView alloc] initWithFrame:statuBarFrame];
statusbarBg.backgroundColor = [UIColor blackColor];
[self.navigationController.view addSubview:statusbarBg];

Dan jika Anda ingin menampilkan bilah navigasi ketika Anda mengetuk bilah status, Anda dapat melakukan ini:

- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView {
     self.navigationController.navigationBarHidden = NO;
}
Zhong Huiwen
sumber
10

Inilah implementasi saya: SherginScrollableNavigationBar .

Dalam pendekatan saya, saya menggunakan KVOuntuk mengamati UIScrollViewkeadaan, jadi tidak perlu menggunakan delegasi (dan Anda dapat menggunakan delegasi ini untuk apa pun yang Anda butuhkan).

Valentin Shergin
sumber
FYI ini tidak selalu berfungsi dengan benar. Saya mencoba ini juga dan berfungsi selama Anda tidak "memantul" scrollview. Sepertinya KVO tidak terpicu ketika di bagian bouncing. Panggilan delegasi untuk contentOffset dipicu.
Joris Mans
7

Silakan coba solusi saya ini dan beri tahu saya mengapa ini tidak sebagus jawaban sebelumnya.

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
    if (fabs(velocity.y) > 1)
        [self hideTopBar:(velocity.y > 0)];
}

- (void)hideTopBar:(BOOL)hide
{
    [self.navigationController setNavigationBarHidden:hide animated:YES];
    [[UIApplication sharedApplication] setStatusBarHidden:hide withAnimation:UIStatusBarAnimationSlide];
}
Nishant
sumber
1
Saya melakukan sesuatu yang mirip dengan ini, dan ini dengan mudah solusi terbaik. Saya telah mencoba beberapa peretasan dan pustaka, dan ini adalah satu-satunya yang berfungsi untuk iOS 9 dengan tableView tidak menutupi seluruh layar. Pujian!
skensell
6

Salah satu cara yang saya lakukan ini adalah sebagai berikut.

Daftarkan pengontrol tampilan Anda untuk menjadi UIScrollViewDelegatemilik Anda UITableViewmisalnya.

- (void)scrollViewDidScroll:(UIScrollView *)scrollView;
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;

Dari dalam UIScrollViewDelegatemetode Anda bisa mendapatkan contentOffset baru dan menerjemahkan UINavigationBarnaik atau turun Anda sesuai.

Mengatur alpha dari subview juga dapat dilakukan berdasarkan pada beberapa nilai ambang batas dan faktor yang dapat Anda atur dan hitung.

Semoga ini bisa membantu!

Diana Sule
sumber
Ditemukan pos serupa di sini
Diana Sule
Terima kasih Diana! Saya curiga saya mungkin harus menerapkan metode UIScrollViewDelegate tetapi sepertinya itu mungkin sedikit berlebihan. Setelah saya mengetahui hal ini saya akan mempostingnya. Bersulang!
El Mocoso
4

Selain jawaban Iwburk, saya menambahkan yang berikut ini untuk memperbaiki masalah alfa pada bilah navigasi yang tidak kustom dan untuk mereset bilah navigasi di metode viewWillDisappear:

- (void)updateBarButtonItems:(CGFloat)alpha
{
    for (UIView *view in self.navigationController.navigationBar.subviews) {
        NSString *className = NSStringFromClass([view class]);

        if ( ![className isEqualToString:@"_UINavigationBarBackground"] ) {
            view.alpha = alpha;
        }
    }
}

- (void)resetNavigationBar {
    CGRect frame = self.navigationController.navigationBar.frame;
    frame.origin.y = 20;
    [self.navigationController.navigationBar setFrame:frame];
    [self updateBarButtonItems:1.0f];
}
blueice
sumber
Apakah ada cara lain selain mengulangi subview?
thisiscrazy4
Dari apa yang dapat saya katakan pada bilah navigasi non-kustom ada 4 total subview: _UINavigationBarBackground, UINavigationItemView, UINavigationItemButtonView, dan _UINavigationBarBackIndicatorView. Pengulangan cukup cepat dan sepertinya tidak memengaruhi kinerja aplikasi saya.
blueice
Karena _UINavigationBarBackground tampaknya selalu menjadi subview pertama, Anda dapat mengakses sisanya secara langsung: ((UIView *) self.navigationController.navigationBar.subviews [1]). Alpha = alpha;
blueice
4

Saya mencari solusi yang memungkinkan gaya dan perilaku apa pun. Anda akan melihat bahwa perilaku kondensasi batang berbeda di banyak aplikasi berbeda. Dan tentu saja, tampilan bilah benar-benar berbeda antara aplikasi.

Saya membuat solusi untuk masalah ini dengan https://github.com/bryankeller/BLKFlexibleHeightBar/

Anda bisa menentukan aturan perilaku Anda sendiri untuk mengontrol bagaimana dan kapan bar menyusut dan tumbuh, dan Anda bisa menentukan dengan tepat bagaimana Anda ingin subview bar bereaksi terhadap bar yang mengkondensasi atau tumbuh.

Lihat proyek saya jika Anda ingin banyak fleksibilitas untuk membuat jenis header bar apa pun yang dapat Anda pikirkan.

blkhp19
sumber
Bagaimana saya bisa menambahkan tombol ke customHeaderView ini yang akan disembunyikan ketika saya gulir ke atas. Saya tidak perlu tombol statis. Apakah mungkin? Saya mencoba membuat satu tombol sebagai subview. Tetapi tidak menerima sentuhan.
abhimuralidharan
3

Saya mencoba meniru perilaku ini dalam situasi di mana saya memerlukan tajuk yang disesuaikan yang membahas tentang UITableView. Saya memutar bilah "navigasi" saya sendiri karena ini berada di bawah banyak hal lain pada halaman dan saya ingin header bagian untuk mengikuti perilaku "docking" default. Saya pikir saya menemukan cara yang cukup pintar dan ringkas untuk menyesuaikan UITableView / UIScrollView bersama-sama dengan objek lain dalam gaya yang mirip dengan yang terlihat di Facebook / Instagram / Chrome / etc. aplikasi.

Dalam file .xib saya, komponen saya dimuat ke tampilan bentuk bebas: http://imgur.com/0z9yebJ (maaf, tidak punya rep untuk gambar inline)

Perhatikan bahwa, di bilah sisi kiri, tabel disusun di belakang tampilan tajuk utama. Anda tidak dapat membedakan dari tangkapan layar, tetapi juga memiliki posisi y yang sama dengan tampilan header utama. Karena tidak terlihat, properti contentInset pada UITableView diatur ke 76 (ketinggian tampilan header utama).

Untuk membuat tampilan tajuk utama geser bersamaan dengan UIScrollView, saya menggunakan metode scrollViewDidScroll UIScrollViewDelegate untuk melakukan beberapa perhitungan dan mengubah contentInset UIScrollView serta bingkai tampilan tajuk utama.

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    UIEdgeInsets insets = scrollView.contentInset;
    //tableViewInsetDelta and tableViewOriginalInsetValue are NSInteger variables that I set to 0 and 76, respectively, in viewDidLoad
    tableViewInsetDelta = tableViewOriginalInsetValue + scrollView.contentOffset.y;
    insets.top = tableViewOriginalInsetValue - tableViewInsetDelta;

    if (scrollView.contentOffset.y > -76 && scrollView.contentOffset.y < 0) {
        [scrollView setContentInset:insets];
        self.pathTitleContainer.frame = CGRectMake(self.pathTitleContainer.frame.origin.x, 44 - tableViewInsetDelta, self.pathTitleContainer.frame.size.width, self.pathTitleContainer.frame.size.height);
    } else if (scrollView.contentOffset.y > 0) {
        insets.top = 0;
        [scrollView setContentInset:insets];
        self.pathTitleContainer.frame = CGRectMake(self.pathTitleContainer.frame.origin.x, -32, self.pathTitleContainer.frame.size.width, self.pathTitleContainer.frame.size.height);
    } else if (scrollView.contentOffset.y < -76) {
        insets.top = 76;
        [scrollView setContentInset:insets];
        self.pathTitleContainer.frame = CGRectMake(self.pathTitleContainer.frame.origin.x, 44, self.pathTitleContainer.frame.size.width, self.pathTitleContainer.frame.size.height);
    }
}

Pernyataan if pertama melakukan sebagian besar pengangkatan berat, tetapi saya harus menyertakan dua lainnya untuk menangani situasi di mana pengguna menyeret paksa dan nilai contentOffset awal yang dikirim ke scrollViewDidScroll berada di luar kisaran pernyataan if pertama .

Pada akhirnya, ini bekerja sangat baik untuk saya. Saya benci memuat proyek saya dengan sekelompok subclass yang membengkak. Saya tidak dapat berbicara apakah ini solusi terbaik berdasarkan kinerja (Saya selalu ragu untuk memasukkan kode apa pun di scrollViewDidScroll karena dipanggil sepanjang waktu), tetapi jejak kode adalah yang terkecil yang pernah saya lihat dalam solusi untuk masalah ini dan itu tidak melibatkan bersarang UITableView di UIScrollView (Apple menyarankan agar hal ini tidak ada dalam dokumentasi dan acara sentuh berakhir agak funky pada UITableView). Semoga ini bisa membantu seseorang!

Brian
sumber
2

Saya mencoba menerapkan GTScrollNavigationBar tetapi aplikasi saya mengharuskan saya untuk memodifikasi batasan tata letak otomatis. Saya memutuskan untuk memberikan contoh implementasi saya di GitHub kalau-kalau ada orang lain yang melakukan ini dengan tata letak otomatis. Masalah lain yang saya miliki dengan sebagian besar implementasi lainnya adalah bahwa orang tidak menetapkan batas-batas tampilan gulir untuk menghindari efek gulir paralaks yang Anda buat saat Anda menggulir dan menyesuaikan ukuran tampilan gulir secara bersamaan.

Periksa JSCollapsingNavBarViewController jika Anda perlu melakukan ini dengan tata letak otomatis. Saya telah menyertakan dua versi, satu dengan bilah nav saja dan yang lain dengan bilah-bilah di bawah bilah nav yang runtuh sebelum menciutkan bilah nav.

jwswart
sumber
1

Saya mencobanya dengan cara ini, saya harap ini akan membantu. cukup implementasikan kode dalam metode delegasi dan atur ke tampilan / subview yang diinginkan

-(void)scrollViewDidScroll:(UIScrollView *)scrollView{ 
            CGRect frame=self.view.frame;
            CGRect resultFrame=CGRectZero;
            if(scrollView.contentOffset.y==0 || scrollView.contentOffset.y<0){
                self.lastContentOffset=0;
                self.offset=0;
                resultFrame=CGRectMake(0, frame.size.height-(40-self.offset.intValue), frame.size.width, 40-self.offset.intValue);
    // Pass the resultFrame
                [self showHide:YES withFrame:resultFrame];
            }else if (self.lastContentOffset > scrollView.contentOffset.y){
                NSNumber *temp=[NSNumber numberWithDouble:self.lastContentOffset-scrollView.contentOffset.y];
                if(temp.intValue>40 || self.offset.intValue<temp.intValue){
                    self.offset=[NSNumber numberWithInt:0];
                    resultFrame=CGRectMake(0, frame.size.height-(40-self.offset.intValue), frame.size.width, 40-self.offset.intValue);
    // Pass the resultFrame
                    [self showHide:YES withFrame:resultFrame];
                }else{
                    if(temp.intValue>0){
                        self.offset=[NSNumber numberWithInt:self.offset.intValue-temp.intValue];
                        resultFrame=CGRectMake(0, frame.size.height-(40-self.offset.intValue), frame.size.width, 40-self.offset.intValue);
    // Pass the resultFrame
                        [self showHide:YES withFrame:resultFrame];
                    }
                }
            }else if (self.lastContentOffset < scrollView.contentOffset.y){
                NSNumber *temp=[NSNumber numberWithDouble:scrollView.contentOffset.y-self.lastContentOffset];
                if(self.offset.intValue>40 || (self.offset.intValue+temp.intValue)>40){
                    self.offset=[NSNumber numberWithInt:40];
    // Pass the resultFrame
                    [self showHide:NO withFrame:resultFrame];
                }else{
                    self.offset=[NSNumber numberWithInt:self.offset.intValue+temp.intValue];
                    resultFrame=CGRectMake(0, frame.size.height-(40-self.offset.intValue), frame.size.width, 40-self.offset.intValue);
    // Pass the resultFrame
                    [self showHide:YES withFrame:resultFrame];
                }
            }
            self.lastContentOffset = scrollView.contentOffset.y;

        }

-(void)showHide:(Boolean)boolView withFrame:(CGRect)frame{
               if(showSRPFilter){
                        //Assign value of "frame"to any view on which you wan to to perform animation
                }else{
                       //Assign value of "frame"to any view on which you wan to to perform animation
                }
        }
pengguna2968901
sumber
1

Perpanjangan dari jawaban @Iwburk ... Alih-alih mengubah asal bilah navigasi, saya perlu memperluas / mengecilkan ukuran bilah navigasi.

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGRect frame = self.previousRect; // a property set in the init method to hold the initial size of the uinavigationbar
    CGFloat size = frame.size.height;
    CGFloat framePercentageHidden = ((MINIMUMNAVBARHEIGHT - frame.origin.y) / (frame.size.height - 1));
    CGFloat scrollOffset = scrollView.contentOffset.y;
    CGFloat scrollDiff = scrollOffset - self.previousScrollViewYOffset;
    CGFloat scrollHeight = scrollView.frame.size.height;
    CGFloat scrollContentSizeHeight = scrollView.contentSize.height + scrollView.contentInset.bottom;

    if (scrollOffset <= -scrollView.contentInset.top) {
        frame.origin.y = -MINIMUMNAVBARHEIGHT;
    } else if ((scrollOffset + scrollHeight) >= scrollContentSizeHeight) {
        frame.origin.y = -size;
    } else {
        frame.origin.y = MIN(-MINIMUMNAVBARHEIGHT, MAX(-size, frame.origin.y - scrollDiff));
    }

    self.previousRect = CGRectMake(0, frame.origin.y, self.jsExtendedBarView.frame.size.width, 155);
    self.layoutConstraintExtendedViewHeight.constant = MAXIMUMNAVBARHEIGHT + frame.origin.y + MINIMUMNAVBARHEIGHT;
    [self updateBarButtonItems:(1 - framePercentageHidden)];
    self.previousScrollViewYOffset = scrollOffset;
}

Itu belum berfungsi dengan stoppedScrollingmetode ini, sakit posting pembaruan ketika saya memilikinya

jsetting32
sumber
0

Semua pendekatan ini tampaknya terlalu rumit ... Jadi secara alami, saya membangun sendiri:

class ViewController: UIViewController, UIScrollViewDelegate {
    var originalNavbarHeight:CGFloat = 0.0
    var minimumNavbarHeight:CGFloat = 0
    weak var scrollView:UIScrollView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        // setup delegates 
        scrollView.delegate = self
        // save the original nav bar height
        originalNavbarHeight = navigationController!.navigationBar.height
    }


    func scrollViewDidScroll(scrollView: UIScrollView) {
        // will relayout subviews
        view.setNeedsLayout() // calls viewDidLayoutSubviews
    }

    override func viewDidLayoutSubviews() {
        var percentageScrolled = min(scrollView.contentOffset.y / originalNavbarHeight, 1)
        navigationController?.navigationBar.height = min(max((1 - percentageScrolled) * originalNavbarHeight, minimumNavbarHeight), originalNavbarHeight)
        // re-position and scale scrollview
        scrollView.y = navigationController!.navigationBar.height + UIApplication.sharedApplication().statusBarFrame.height
        scrollView.height = view.height - scrollView.y
    }

    override func viewWillDisappear(animated: Bool) {
        navigationController?.navigationBar.height = originalNavbarHeight
    }

}
Oxcug
sumber
navigationBar.height? scrollView.height? Apakah Anda menggunakan ekstensi di frameproperti?
Ely
0

Saya menemukan semua jawaban yang diberikan di Objective-C. Ini jawaban saya di Swift 3. Ini adalah kode yang sangat umum dan dapat digunakan secara langsung. Ia bekerja dengan UIScrollView dan UITableView.

var lastContentOffset: CGPoint? = nil
var maxMinus: CGFloat           = -24.0
var maxPlus: CGFloat            = 20.0
var initial: CGFloat            = 0.0

override func viewDidLoad() {
    super.viewDidLoad()

    self.title = "Alarm Details"
    self.lastContentOffset = self.alarmDetailsTableView.contentOffset
    initial = maxPlus
}

func scrollViewDidScroll(_ scrollView: UIScrollView)
{
    var navigationBarFrame: CGRect   = self.navigationController!.navigationBar.frame
    let currentOffset = scrollView.contentOffset

    if (currentOffset.y > (self.lastContentOffset?.y)!) {
        if currentOffset.y > 0 {
            initial = initial - fabs(CGFloat(currentOffset.y - self.lastContentOffset!.y))
        }
        else if scrollView.contentSize.height < scrollView.frame.size.height {
            initial = initial + fabs(CGFloat(currentOffset.y - self.lastContentOffset!.y))
        }
    }
    else {
        if currentOffset.y < scrollView.contentSize.height - scrollView.frame.size.height {
            initial = initial + fabs(CGFloat(currentOffset.y - self.lastContentOffset!.y))
        }
        else if scrollView.contentSize.height < scrollView.frame.size.height && initial < maxPlus {
            initial = initial - fabs(CGFloat(currentOffset.y - self.lastContentOffset!.y))
        }
    }

    initial = (initial <= maxMinus) ? maxMinus : initial
    initial = (initial >= maxPlus) ? maxPlus : initial

    navigationBarFrame.origin.y = initial

    self.navigationController!.navigationBar.frame = navigationBarFrame
    scrollView.frame = CGRect(x: 0.0, y: initial + navigationBarFrame.size.height , width: navigationBarFrame.size.width, height: self.view.frame.size.height - (initial + navigationBarFrame.size.height))

    let framePercentageHidden: CGFloat              = ((20 - navigationBarFrame.origin.y) / (navigationBarFrame.size.height));
    self.lastContentOffset                          = currentOffset;
    self.updateBarButtonItems(alpha: 1 - framePercentageHidden)
}

func updateBarButtonItems(alpha: CGFloat)
{
    self.navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.darkGray.withAlphaComponent(alpha)]
    self.navigationController?.navigationBar.isUserInteractionEnabled = (alpha < 1) ? false: true

    guard (self.navigationItem.leftBarButtonItems?.count) != nil else { return }

    for (_, value) in self.navigationItem.leftBarButtonItems!.enumerated() {
        value.customView?.alpha = alpha
    }

    guard (self.navigationItem.rightBarButtonItems?.count) != nil else { return }

    for (_, value) in (self.navigationItem.rightBarButtonItems?.enumerated())! {
        value.customView?.alpha = alpha
    }
}

Logika pengaturan alpha ke item navigasi disalin dari @ WayneBurkett jawab dan ditulis ulang dalam Swift 3.

Dev
sumber