Bắt chước Facebook ẩn / hiển thị mở rộng / hợp đồng Thanh điều hướng


128

Trong ứng dụng Facebook dành cho iPhone iOS7 mới, khi người dùng cuộn lên, navigationBardần dần ẩn mình đến một điểm mà nó hoàn toàn biến mất. Sau đó khi người dùng cuộn xuống navigationBardần dần hiển thị chính nó.

Làm thế nào bạn sẽ tự thực hiện hành vi này? Tôi biết giải pháp sau đây nhưng nó biến mất ngay lập tức và nó không bị ràng buộc với tốc độ của cử chỉ cuộn của người dùng.

[navigationController setNavigationBarHidden: YES animated:YES];

Tôi hy vọng đây không phải là một bản sao vì tôi không chắc cách tốt nhất để mô tả hành vi "mở rộng / ký kết hợp đồng".


2
Các vấn đề tương tự: stackoverflow.com/questions/21929220/ Khăn Lưu ý rằng cực kỳ khó để khớp hoàn toàn với hành vi của Safari. Có một số quy tắc rất, rất phức tạp trong đó!
Fattie

1
Trong dự án của tôi, tôi đã sử dụng dự án này và nó hoạt động tốt. Hãy xem tài liệu của nó.
Vinicius

github.com/bryankeller/BLKFlexibleHeightBar sẽ cho phép bạn làm những gì bạn muốn và hơn thế nữa. Nó cho phép bạn xác định chính xác cách thanh nhìn vào từng giai đoạn chuyển đổi từ tối đa hóa sang tối thiểu hóa. Nó thậm chí còn cho phép bạn chỉ định các hành vi của riêng mình, để nó có thể hoạt động như Safari, Facebook hoặc một số ứng dụng khác.
blkhp19

Tôi đã không sử dụng một thanh uinavlationbar nhưng thay vào đó đã thêm một uiview. Chế độ xem sao chép thanh điều hướng sẽ mở rộng và hợp đồng dựa trên cuộn. Tôi đã sử dụng phương thức ủy nhiệm scrollViewDidScroll để đạt được nhiệm vụ. Bạn có thể muốn kiểm tra và thực thi mã nguồn bên dưới .. dropbox.com/s/b2c0zw6yvchaia5/FailedBanks.zip?dl=0
Deepak Thakur

Câu trả lời:


162

Giải pháp được đưa ra bởi @peerless là một khởi đầu tuyệt vời, nhưng nó chỉ khởi động một hình ảnh động mỗi khi bắt đầu kéo, mà không xem xét tốc độ của cuộn. Điều này dẫn đến trải nghiệm choppier hơn bạn nhận được trong ứng dụng Facebook. Để phù hợp với hành vi của Facebook, chúng tôi cần:

  • ẩn / hiển thị thanh điều hướng ở tốc độ tỷ lệ thuận với tốc độ kéo
  • khởi động một hình ảnh động để ẩn hoàn toàn thanh nếu cuộn dừng khi thanh bị ẩn một phần
  • làm mờ dần các mục của thanh điều hướng khi thanh co lại.

Trước tiên, bạn sẽ cần các thuộc tính sau:

@property (nonatomic) CGFloat previousScrollViewYOffset;

Và đây là các UIScrollViewDelegatephương pháp:

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

Bạn cũng sẽ cần các phương thức trợ giúp này:

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

Đối với một hành vi hơi khác, thay thế dòng định vị lại thanh khi cuộn ( elsekhối trong scrollViewDidScroll) bằng hành vi này:

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

Điều này định vị thanh dựa trên tỷ lệ phần trăm cuộn cuối cùng, thay vì số lượng tuyệt đối, dẫn đến độ mờ chậm hơn. Hành vi ban đầu giống Facebook hơn, nhưng tôi cũng thích cái này.

Lưu ý: Giải pháp này chỉ dành cho iOS 7+. Hãy chắc chắn để thêm các kiểm tra cần thiết nếu bạn đang hỗ trợ các phiên bản iOS cũ hơn.


Những công việc này. Ngoại trừ bạn đang giả sử các mục nút thanh có chế độ xem tùy chỉnh. Trong trường hợp của tôi, tôi không có chế độ xem tùy chỉnh. Vì vậy, ở trên không ẩn các nút thanh. Tôi nghĩ rằng giải pháp @peerless tốt hơn cho việc ẩn và hiển thị các mục trên thanh điều hướng
Dhanush

1
Bạn đúng rồi. Tôi có thể xem xét một giải pháp tổng quát hơn vào cuối tuần này. Không nên quá khó để nhắm mục tiêu các mục mặc định thay vì customView.
Wayne

Tôi chưa giải quyết vấn đề của @ Dhanush nhưng tôi có một bản cập nhật.
Wayne

1
Tôi đã khắc phục sự cố với các nút thanh chứng khoán, nhưng mã này có một vấn đề khác. Nếu ScrollView's contentSizenhỏ hơn khung hình, hoạt hình trượt không làm việc. Ngoài ra, hãy đảm bảo bạn đặt lại tất cả alpha của mục điều hướng trở lại 1.0 in viewDidDisappear.
Legoless

1
Bạn đã kiểm tra giải pháp này trên các bộ điều khiển được sử dụng AutoLayout chưa? Trong trường hợp của tôi, mọi thứ đều hoạt động tốt mà không cần AutoLayout, nhưng khi bật lên, nó vẫn hiển thị một dải màu trắng kỳ lạ bên dưới nó
Aliaksandr B.

52

EDIT: Chỉ dành cho iOS 8 trở lên.

Bạn có thể thử sử dụng

self.navigationController.hidesBarsOnSwipe = YES;

Làm việc cho tôi.

Nếu mã hóa của bạn nhanh chóng, bạn phải sử dụng cách này (từ https://stackoverflow.com/a/27662702/2283308 )

navigationController?.hidesBarsOnSwipe = true

điều này không khả dụng trong iOS <8.0
Petar

1
Như pet60t0 đã nói, và tôi xin lỗi, chỉ hoạt động cho iOS 8 trở lên.
Pedro Romão

4
Làm thế nào để bạn mang nó trở lại sau đó?
C0D3

hành vi đó hơi lạ. Tôi đã không khám phá nhiều, nhưng nếu bạn cuộn nhanh hơn, nó sẽ hiển thị lại.
Pedro Romão

@ PedroRomão câu trả lời tuyệt vời.
Gagan_iOS

43

Đây là một triển khai nữa: TLYShyNavBar v1.0.0 được phát hành!

Tôi quyết định tự làm sau khi thử các giải pháp được cung cấp, và với tôi, chúng hoạt động kém, có rào cản lớn về mã nhập và mã nồi hơi, hoặc thiếu chế độ xem mở rộng bên dưới thanh điều hướng. Để sử dụng thành phần này, tất cả những gì bạn phải làm là:

self.shyNavBarManager.scrollView = self.scrollView;

Ồ, và đó là trận chiến được thử nghiệm trong ứng dụng của chúng ta.


@TimArnold Cảm ơn phản hồi của bạn! Tôi đã khắc phục sự cố đó, chỉ chưa cập nhật nhóm> _ <sẽ thực hiện ngay bây giờ! .. Pod đã cập nhật!
Mazyod

Tôi sẽ cho nó một phát súng nữa! Cảm ơn!
Tim Camber

Tôi đánh giá cao sự giúp đỡ của bạn. Có vẻ như cam kết của bạn đã giúp. Tôi vẫn gặp phải một vấn đề lạ khi UICollectionView của tôi không được thay đổi kích thước chính xác như thanh điều hướng, và vì vậy các ô đi lên nơi thanh điều hướng được sử dụng sẽ bị cắt bớt, vì chúng nằm ngoài giới hạn của bộ sưu tập. Bạn có biết tại sao điều này có thể xảy ra?
Tim Camber

4
Hình: tìm thấy giải pháp của tôi vài giây sau khi viết bình luận này. Tôi phải đảm bảo extendedLayoutIncludesOpaqueBarsđược đặt thành YEStrênUICollectionViewController
Tim Camber

@TimArnold Thật tuyệt vời! Có một anh chàng khác có cùng một vấn đề , và hy vọng giải pháp của bạn sẽ giúp anh ta.
Mazyod

33

Bạn có thể xem GTScrollNavulationBar của tôi . Tôi đã phân lớp UINavlationBar để làm cho nó cuộn dựa trên việc cuộn UIScrollView.

Lưu ý: Nếu bạn có thanh điều hướng OPAQUE, cuộn xem phải MỞ RỘNG khi thanh điều hướng được HIDDEN. Đây chính xác là những gì GTScrollNavulationBar làm. (Giống như trong ví dụ Safari trên iOS.)


BTW cho bất cứ ai đọc, chính xác làm thế nào để gọi initWithNavlationBarClass ... stackoverflow.com/questions/22286166
Fattie

@Thuy người đàn ông làm việc tuyệt vời! Vì vậy, tôi có điều này hoạt động hoàn hảo trên bộ điều khiển xem bảng ngoại trừ một điều ... Tôi có kéo để làm mới được thực hiện ở trên cùng. Nó trở nên kỳ lạ khi cố gắng kéo xuống và làm mới. Có thể có một cách giải quyết cho điều này?
aherrick

@Thuy cũng ... giả sử tôi có bộ điều khiển xem trong đó tôi đã triển khai chế độ xem bảng ở phía dưới. Tôi muốn kết nối với chế độ xem bảng đó hoạt động, tuy nhiên tôi có một chế độ xem khác đang ngồi phía trên chế độ xem bảng mà tôi cũng muốn biến mất. Làm thế nào điều này có thể làm việc?
aherrick

25

iOS8 bao gồm các thuộc tính để có được thanh điều hướng ẩn miễn phí. Có một video WWDC thể hiện điều đó, tìm kiếm "Xem tiến bộ điều khiển trong iOS 8".

Ví dụ :

class QuotesTableViewController: UITableViewController {

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

    navigationController?.hidesBarsOnSwipe = true
}

}

Các tài sản khác:

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

Tìm thấy qua http://natashatherobot.com/navulation-bar-interilities-ios8/


12

Tôi có một số loại giải pháp nhanh chóng và bẩn cho điều đó. Không thực hiện bất kỳ thử nghiệm chuyên sâu nào nhưng đây là ý tưởng:

Thuộc tính đó sẽ giữ tất cả các mục trong thanh điều hướng cho lớp UITableViewControll của tôi

@property (strong, nonatomic) NSArray *navBarItems;

Trong cùng một lớp UITableViewControll tôi có:

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

Điều đó chỉ dành cho ios> = 7, thật xấu xí tôi biết nhưng một cách nhanh chóng để đạt được điều này. Mọi ý kiến ​​/ đề xuất đều được chào đón :)


12

Điều này hoạt động cho iOS 8 trở lên và đảm bảo rằng thanh trạng thái vẫn giữ nguyên nền của nó

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

Và nếu bạn muốn hiển thị thanh điều hướng khi bạn nhấn vào thanh trạng thái, bạn có thể làm điều này:

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

10

Đây là cách thực hiện của tôi: SherginScrollableNavulationBar .

Theo cách tiếp cận của tôi, tôi đang sử dụng KVOđể quan sát UIScrollViewtrạng thái, vì vậy không cần thiết phải sử dụng một đại biểu (và bạn có thể sử dụng đại biểu này cho bất cứ điều gì khác mà bạn cần).


FYI này không phải lúc nào cũng hoạt động chính xác. Tôi cũng đã thử điều này và nó hoạt động miễn là bạn không "nảy" xem cuộn. Có vẻ như KVO không được kích hoạt khi ở phần nảy. Cuộc gọi đại biểu cho nội dung Offerset được kích hoạt mặc dù.
Joris Mans

7

Vui lòng thử giải pháp này của tôi và cho tôi biết lý do tại sao điều này không tốt như các câu trả lời trước.

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

1
Tôi đã làm một cái gì đó tương tự như thế này, và đây dễ dàng là giải pháp tốt nhất. Tôi đã thử một số bản hack và thư viện và đây là bản duy nhất hoạt động cho iOS 9 với bảngView không bao phủ toàn bộ màn hình. Thanh danh!
skensell

6

Một cách mà tôi đã hoàn thành điều này là như sau.

Đăng ký bộ điều khiển xem UIScrollViewDelegatecủa bạn là UITableViewví dụ của bạn .

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

Từ bên trong UIScrollViewDelegatecác phương thức de, bạn có thể lấy nội dung mới và cài đặt UINavigationBarlên hoặc xuống theo.

Việc đặt alpha của các cuộc phỏng vấn cũng có thể được thực hiện dựa trên một số giá trị ngưỡng và các yếu tố bạn có thể đặt và tính toán.

Hy vọng nó giúp!


Tìm thấy bài tương tự ở đây
Diana Sule

Cảm ơn Diana! Tôi nghi ngờ tôi có thể phải thực hiện các phương pháp UIScrollViewDelegate nhưng có vẻ như nó có thể hơi quá mức. Một khi tôi nhận ra điều này tôi sẽ đăng nó lên. Chúc mừng!
El Mộcoso

4

Ngoài câu trả lời của Iwburk, tôi đã thêm vào phần sau để khắc phục sự cố alpha trên các thanh điều hướng không tùy chỉnh và để đặt lại thanh điều hướng trong phương thức 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];
}

Có cách nào khác ngoài việc lặp qua các cuộc phỏng vấn không?
này4

Từ những gì tôi có thể nói trên một thanh điều hướng không tùy chỉnh, có 4 tổng số lượt xem: _UINavlationBarBackground, UINavlationItemView, UINavlationItemButtonView và _UINavlationBarBackIndicatorView. Vòng lặp khá nhanh và dường như không ảnh hưởng đến bất kỳ hiệu suất nào của ứng dụng của tôi.
blueice

Vì _UINavlationBarBackground dường như luôn là cuộc phỏng vấn đầu tiên mà bạn chỉ có thể truy cập trực tiếp vào phần còn lại: ((UIView *) self.navlationControll.navlationBar.subview [1]). Alpha = alpha;
màu xanh

4

Tôi đang tìm kiếm một giải pháp cho phép cho mọi phong cách và mọi hành vi. Bạn sẽ nhận thấy rằng hành vi ngưng tụ thanh là khác nhau trong nhiều ứng dụng khác nhau. Và tất nhiên, giao diện của thanh hoàn toàn khác nhau giữa các ứng dụng.

Tôi đã tạo một giải pháp cho vấn đề này với https://github.com/bryankeller/BLKFlexibleHeightBar/

Bạn có thể xác định các quy tắc hành vi của riêng mình để kiểm soát cách thức và thời điểm thanh co lại và phát triển, và bạn có thể xác định chính xác cách bạn muốn các cuộc phỏng vấn của thanh phản ứng với sự ngưng tụ hoặc phát triển của thanh.

Hãy xem dự án của tôi nếu bạn muốn có nhiều sự linh hoạt để tạo ra bất kỳ loại thanh tiêu đề nào bạn có thể nghĩ ra.


Làm cách nào tôi có thể thêm một nút vào customHeaderView này sẽ ẩn khi tôi cuộn lên. Tôi không cần nút tĩnh. Tôi có thể không? Tôi đã thử tạo một nút dưới dạng xem phụ. Nhưng nó không nhận được bất kỳ chạm nào.
abhimuralidharan 15/03/2016

3

Tôi đã cố gắng mô phỏng hành vi này trong một tình huống mà tôi cần một tiêu đề tùy chỉnh ngồi về một UITableView. Tôi đã cuộn thanh "điều hướng" của riêng mình vì thanh này nằm bên dưới một loạt các nội dung khác trên trang và tôi muốn các tiêu đề của phần tuân theo hành vi "lắp ghép" mặc định. Tôi nghĩ rằng tôi đã tìm thấy một cách khá thông minh và cô đọng để điều chỉnh UITableView / UIScrollView cùng với một đối tượng khác theo phong cách tương tự như đã thấy trong Facebook / Instagram / Chrome / v.v. ứng dụng.

Trong tệp .xib của tôi, tôi có các thành phần của mình được tải vào chế độ xem dạng tự do: http://imgur.com/0z9yebJ (xin lỗi, không có đại diện cho hình ảnh nội tuyến)

Lưu ý rằng, trong thanh bên trái, bảng được sắp xếp phía sau chế độ xem tiêu đề chính. Bạn không thể biết từ ảnh chụp màn hình, nhưng nó cũng có cùng vị trí y với chế độ xem tiêu đề chính. Vì nó mở rộng ra khỏi tầm nhìn, thuộc tính contentInset trên UITableView được đặt thành 76 (chiều cao của chế độ xem tiêu đề chính).

Để làm cho chế độ xem tiêu đề chính trượt lên đồng nhất với UIScrollView, tôi sử dụng các phương thức scrollViewDidScroll của UIScrollViewDelegate để thực hiện một số tính toán và thay đổi nội dung của UIScrollView cũng như khung nhìn của tiêu đề chính.

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

Câu lệnh if đầu tiên thực hiện hầu hết các thao tác nặng, nhưng tôi phải đưa vào hai câu lệnh còn lại để xử lý các tình huống mà người dùng đang kéo mạnh và các giá trị ban đầu của nội dung được gửi đến scrollViewDidScroll nằm ngoài phạm vi của câu lệnh if đầu tiên .

Cuối cùng, điều này đang làm việc thực sự tốt cho tôi. Tôi ghét tải lên các dự án của tôi với một loạt các lớp con cồng kềnh. Tôi không thể nói liệu đây có phải là giải pháp hiệu quả tốt nhất hay không (Tôi luôn do dự khi đặt bất kỳ mã nào trong scrollViewDidScroll vì nó được gọi mọi lúc), nhưng dấu chân mã là nhỏ nhất tôi từng thấy giải pháp cho vấn đề này và nó không liên quan đến việc lồng một UITableView trong UIScrollView (Apple khuyên chống lại điều này trong tài liệu và các sự kiện chạm sẽ kết thúc một chút thú vị trên UITableView). Hy vọng điều này sẽ giúp được ai đó!



2

Tôi đã thử triển khai GTScrollNavlationBar nhưng ứng dụng của tôi yêu cầu tôi sửa đổi các ràng buộc bố cục tự động. Tôi quyết định đưa ra một ví dụ về việc triển khai GitHub của mình trong trường hợp bất kỳ ai khác phải làm điều này với bố cục tự động. Vấn đề khác tôi gặp phải với hầu hết các triển khai khác là mọi người không đặt giới hạn của chế độ xem cuộn để tránh hiệu ứng cuộn thị sai mà bạn tạo trong khi bạn cuộn và điều chỉnh kích thước của chế độ xem cuộn.

Kiểm tra JSCollapsingNavBarViewControll nếu bạn cần làm điều này với bố cục tự động. Tôi đã bao gồm hai phiên bản, một phiên bản chỉ có thanh điều hướng và phiên bản khác có thanh phụ bên dưới thanh điều hướng bị sập trước khi thu gọn thanh điều hướng.


1

Tôi đã thử nó theo cách này, tôi hy vọng nó sẽ giúp. chỉ cần triển khai mã trong phương thức ủy nhiệm và được đặt thành chế độ xem / xem phụ mong muốn

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

1

Phần mở rộng câu trả lời của @Iwburk ... Thay vì thay đổi nguồn gốc của thanh điều hướng, tôi cần mở rộng / thu nhỏ kích thước của thanh điều hướng.

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

Nó không hoạt động với stoppedScrollingphương thức này, tôi sẽ đăng một bản cập nhật khi tôi có nó


0

Tất cả những cách tiếp cận này có vẻ quá phức tạp ... Vì vậy, một cách tự nhiên, tôi tự xây dựng:

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
    }

}

navigationBar.height? scrollView.height? Bạn có sử dụng một phần mở rộng trên frametài sản?
Ely

0

Tôi tìm thấy tất cả các câu trả lời được đưa ra trong Mục tiêu-C. Đây là câu trả lời của tôi trong Swift 3. Đây là mã rất chung chung và có thể được sử dụng trực tiếp. Nó hoạt động với cả UIScrollView và 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
    }
}

Logic của việc đặt alpha cho các mục điều hướng được sao chép từ câu trả lời @ WayneBurkett và viết lại trong Swift 3.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.