เลียนแบบการซ่อน / แสดงการขยาย / การทำสัญญาแถบนำทางของ Facebook


128

ในแอพ iOS7 Facebook iPhone ใหม่เมื่อผู้ใช้เลื่อนขึ้นnavigationBarค่อยๆซ่อนตัวเองไปยังจุดที่มันหายไปอย่างสมบูรณ์ จากนั้นเมื่อผู้ใช้เลื่อนลงnavigationBarค่อยๆแสดงตัวเอง

คุณจะใช้พฤติกรรมนี้ด้วยตัวเองอย่างไร ฉันรับทราบถึงวิธีแก้ไขปัญหาต่อไปนี้ แต่หายไปทันทีและไม่ได้เชื่อมโยงกับความเร็วของรูปแบบการเลื่อนของผู้ใช้เลย

[navigationController setNavigationBarHidden: YES animated:YES];

ฉันหวังว่านี่จะไม่ซ้ำกันเนื่องจากฉันไม่แน่ใจว่าวิธีที่ดีที่สุดในการอธิบายพฤติกรรม "ขยาย / หดตัว"


2
ปัญหาเดียวกัน: stackoverflow.com/questions/21929220/... หมายเหตุว่ามันเป็นเรื่องยากอย่างไม่น่าเชื่อการอย่างตรงกับพฤติกรรมซาฟารี มีกฎที่ซับซ้อนมาก ๆ อยู่ในนั้น!
Fattie

1
ในโครงการของฉันฉันใช้โครงการนี้และใช้งานได้ดี ดูเอกสารของมัน
Vinicius

github.com/bryankeller/BLKFlexibleHeightBarจะช่วยให้คุณทำสิ่งที่คุณต้องการและอีกมากมาย มันช่วยให้คุณระบุได้อย่างแม่นยำว่าแถบนั้นมองแต่ละขั้นตอนของการเปลี่ยนจากขยายให้ใหญ่สุดเป็นย่อขนาดเล็กสุดอย่างไร มันยังช่วยให้คุณระบุพฤติกรรมของคุณเองดังนั้นมันสามารถทำหน้าที่เหมือน Safari, Facebook หรือแอพอื่น ๆ
blkhp19

ฉันไม่ได้ใช้ uinavigationbar แต่เพิ่ม uiview แทน มุมมองการจำลองแถบการนำทางจะขยายและหดตามการเลื่อน ฉันใช้วิธีการมอบหมาย scrollViewDidScroll เพื่อให้งานสำเร็จ คุณอาจต้องการตรวจสอบและดำเนินการซอร์สโค้ดด้านล่าง .. dropbox.com/s/b2c0zw6yvchaia5/FailedBanks.zip?dl=0
Deepak Thakur

คำตอบ:


162

วิธีการแก้ปัญหาที่ได้รับจาก @peerless เป็นการเริ่มต้นที่ยอดเยี่ยม แต่มันจะเริ่มการเคลื่อนไหวเมื่อใดก็ตามที่การลากเริ่มขึ้นโดยไม่คำนึงถึงความเร็วของการเลื่อน สิ่งนี้ส่งผลให้เกิดประสบการณ์ที่เปลี่ยนแปลงเร็วกว่าที่คุณได้รับในแอพ Facebook เพื่อให้ตรงกับพฤติกรรมของ Facebook เราจำเป็นต้อง:

  • ซ่อน / แสดงแถบนำทางในอัตราที่เป็นสัดส่วนกับอัตราการลาก
  • เริ่มภาพเคลื่อนไหวเพื่อซ่อนแถบทั้งหมดหากการเลื่อนหยุดลงเมื่อแถบถูกซ่อนอยู่บางส่วน
  • ลบรายการของ navbar ตามที่แถบย่อ

ก่อนอื่นคุณต้องมีคุณสมบัติดังต่อไปนี้:

@property (nonatomic) CGFloat previousScrollViewYOffset;

และนี่คือUIScrollViewDelegateวิธีการ:

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

คุณจะต้องใช้วิธีการช่วยเหลือเหล่านี้ด้วย:

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

สำหรับพฤติกรรมที่แตกต่างกันเล็กน้อยให้แทนที่บรรทัดที่เปลี่ยนตำแหน่งของแถบเมื่อเลื่อน ( elseบล็อกในscrollViewDidScroll) ด้วยอันนี้:

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

ซึ่งวางตำแหน่งแถบตามเปอร์เซ็นต์การเลื่อนล่าสุดแทนจำนวนสัมบูรณ์ซึ่งทำให้จางหายช้าลง พฤติกรรมดั้งเดิมนั้นเหมือน Facebook มากกว่า แต่ฉันก็ชอบสิ่งนี้เช่นกัน

หมายเหตุ: วิธีนี้เป็น iOS 7 ขึ้นไปเท่านั้น ตรวจสอบให้แน่ใจว่าได้เพิ่มการตรวจสอบที่จำเป็นหากคุณสนับสนุน iOS เวอร์ชันเก่า


วิธีนี้ใช้ได้ผล ยกเว้นว่าคุณกำลังสมมติว่ารายการปุ่มแถบมีมุมมองที่กำหนดเอง ในกรณีของฉันฉันไม่มีมุมมองที่กำหนดเอง ดังนั้นข้างต้นไม่ซ่อนปุ่มแถบ ผมคิดว่าวิธีการแก้ปัญหา @peerless จะดีกว่าสำหรับการซ่อนและแสดง navbar รายการ
Dhanush

1
คุณพูดถูก ฉันอาจดูวิธีแก้ปัญหาทั่วไปเพิ่มเติมในสุดสัปดาห์นี้ customViewไม่ควรจะยากเกินไปที่จะกำหนดเป้าหมายรายการเริ่มต้นแทน
Wayne

ฉันไม่ได้แก้ไขปัญหาของ @ Dhanush แต่ฉันมีการอัปเดต
Wayne

1
ฉันแก้ไขปัญหาด้วยปุ่มแถบหุ้น แต่รหัสนี้มีปัญหาอื่น หากScrollView's contentSizeมีขนาดเล็กกว่ากรอบการเคลื่อนไหวของการเลื่อนไม่ได้ทำงาน นอกจากนี้ยังให้แน่ใจว่าคุณตั้งค่าอัลฟากลับรายการนำทางของทั้งหมดถึง 1.0 viewDidDisappearใน
Legoless

1
คุณตรวจสอบวิธีแก้ไขปัญหานี้กับคอนโทรลเลอร์ที่ใช้ AutoLayout หรือไม่ ในกรณีของฉันทุกอย่างทำงานได้ดีโดยไม่มี AutoLayout แต่เมื่อเปิดใช้งานจะยังคงมองเห็นแถบสีขาวแปลก ๆ อยู่ใต้นั้น
Aliaksandr B.

52

แก้ไข: สำหรับ iOS 8 ขึ้นไปเท่านั้น

คุณสามารถลองใช้

self.navigationController.hidesBarsOnSwipe = YES;

ได้ผลสำหรับฉัน

หากการเข้ารหัสของคุณรวดเร็วคุณต้องใช้วิธีนี้ (จากhttps://stackoverflow.com/a/27662702/2283308 )

navigationController?.hidesBarsOnSwipe = true

ไม่สามารถใช้ได้ใน iOS <8.0
Petar

1
ในฐานะที่เป็น pet60t0 กล่าวและฉันขอโทษเฉพาะสำหรับ iOS 8 ขึ้นไป
Pedro Romão

4
คุณจะนำมันกลับมาหลังจากนั้นได้อย่างไร
C0D3

พฤติกรรมมันแปลกไปหน่อย ฉันไม่ได้สำรวจมากนัก แต่ถ้าคุณเลื่อนเร็วขึ้น
Pedro Romão

@ PedroRomãoคำตอบที่ดี
Gagan_iOS

43

นี่คืออีกหนึ่งการใช้งาน: TLYShyNavBar v1.0.0 เปิดตัว!

ฉันตัดสินใจที่จะทำด้วยตัวเองหลังจากลองวิธีแก้ปัญหาที่ให้มาและสำหรับฉันแล้วพวกเขาทำงานได้ไม่ดีมีอุปสรรคในการเข้าและรหัสจานหม้อไอน้ำสูงหรือขาดมุมมองส่วนขยายที่ด้านล่างแถบนำทาง ในการใช้องค์ประกอบนี้สิ่งที่คุณต้องทำคือ:

self.shyNavBarManager.scrollView = self.scrollView;

โอ้และมันคือการทดสอบการต่อสู้ในแอพของเราเอง


@ TimArnold ขอบคุณสำหรับความคิดเห็นของคุณ! ฉันได้แก้ไขปัญหานั้นแล้ว แต่ยังไม่ได้อัปเดตพ็อด> _ <จะทำเช่นนั้นในตอนนี้! .. Pod อัพเดทแล้ว!
Mazyod

ฉันจะให้อีกนัด! ขอบคุณ!
Tim Camber

ฉันขอขอบคุณสำหรับความช่วยเหลือของคุณ ดูเหมือนว่าคุณจะช่วยได้ ฉันยังคงได้รับปัญหาแปลก ๆ ที่ UICollectionView ของฉันไม่ได้ปรับขนาดอย่างเหมาะสมขณะที่แถบนำทางอยู่และเซลล์ก็ขึ้นไปที่แถบนำทางที่ใช้จะถูกตัดเนื่องจากอยู่นอกขอบเขตของมุมมองคอลเลกชัน คุณรู้ไหมว่าทำไมสิ่งนี้ถึงเกิดขึ้น
Tim Camber

4
ตัวเลข: พบวิธีแก้ปัญหาของฉันไม่กี่วินาทีหลังจากเขียนความคิดเห็นนี้ ฉันต้องทำให้แน่ใจว่าextendedLayoutIncludesOpaqueBarsได้ตั้งค่าไว้YESที่UICollectionViewController
Tim Camber

@TimArnold ยอดเยี่ยมมาก! มีผู้ชายอีกคนที่มีปัญหาเดียวกันและหวังว่าวิธีแก้ปัญหาของคุณจะช่วยเขาได้
Mazyod

33

คุณสามารถดูได้ที่ฉันGTScrollNavigationBar ฉันได้ subclassed UINavigationBar เพื่อให้เลื่อนตามการเลื่อนของ UIScrollView

หมายเหตุ: หากคุณมีแถบนำทาง OPAQUE แถบเลื่อนจะต้องขยายเมื่อแถบนำทางได้รับ HIDDEN นี่คือสิ่งที่ GTScrollNavigationBar ทำ (เช่นเดียวกับในตัวอย่างเช่น Safari บน iOS)


BTW สำหรับทุกคนที่อ่านว่าวิธีการที่จะเรียก initWithNavigationBarClass ... stackoverflow.com/questions/22286166
Fattie

@Thuy คนทำงานที่ดี! ดังนั้นฉันจึงทำงานนี้อย่างสมบูรณ์แบบในตัวควบคุมมุมมองตารางยกเว้นสิ่งหนึ่ง ... ฉันได้ดึงเพื่อนำมาใช้ใหม่ที่ด้านบน มันดูแปลก ๆ เมื่อพยายามดึงและรีเฟรช อาจมีวิธีแก้ปัญหาสำหรับสิ่งนี้
aherrick

@Thuy อีกด้วย ... สมมติว่าฉันมีตัวควบคุมมุมมองที่ฉันใช้มุมมองตารางที่ด้านล่าง ฉันต้องการเชื่อมต่อกับมุมมองตารางที่ใช้งานได้ แต่ฉันมีมุมมองอื่นที่นั่งอยู่เหนือมุมมองตารางที่ฉันต้องการให้หายไปเช่นกัน มันจะทำงานยังไง?
aherrick

25

iOS8 มีคุณสมบัติเพื่อซ่อนแถบนำทางโดยไม่เสียค่าใช้จ่าย มีวิดีโอ WWDC ที่แสดงให้เห็นแล้วค้นหา "ดูคอนโทรลเลอร์ขั้นสูงใน iOS 8"

ตัวอย่าง :

class QuotesTableViewController: UITableViewController {

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

    navigationController?.hidesBarsOnSwipe = true
}

}

คุณสมบัติอื่น ๆ :

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

พบได้ที่http://natashatherobot.com/navigation-bar-interactions-ios8/


12

ฉันมีวิธีแก้ปัญหาที่รวดเร็วและสกปรกสำหรับเรื่องนั้น ยังไม่ได้ทำการทดสอบในเชิงลึก แต่นี่เป็นแนวคิด:

คุณสมบัตินั้นจะเก็บรายการทั้งหมดไว้ใน navbar สำหรับคลาส UITableViewController ของฉัน

@property (strong, nonatomic) NSArray *navBarItems;

ในคลาส UITableViewController เดียวกันฉันมี:

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

นั่นเป็นเพียงสำหรับ iOS> = 7 มันน่าเกลียดที่ฉันรู้ แต่เป็นวิธีที่รวดเร็วในการบรรลุเป้าหมาย ความเห็น / ข้อเสนอแนะยินดีต้อนรับ :)


12

สิ่งนี้ใช้ได้กับ iOS 8 ขึ้นไปและทำให้แน่ใจว่าแถบสถานะยังคงพื้นหลังของมัน

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

และถ้าคุณต้องการแสดงแถบนำทางเมื่อคุณแตะบนแถบสถานะคุณสามารถทำได้:

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

10

นี่คือการดำเนินการของฉัน: SherginScrollableNavigationBar

ในแนวทางของฉันฉันกำลังใช้KVOสำหรับการสังเกตUIScrollViewสถานะดังนั้นจึงไม่มีความจำเป็นต้องใช้ผู้รับมอบสิทธิ์ (และคุณสามารถใช้ผู้รับมอบสิทธิ์นี้สำหรับสิ่งที่คุณต้องการ)


FYI สิ่งนี้ทำงานไม่ถูกต้องเสมอไป ฉันลองสิ่งนี้เช่นกันและใช้งานได้ตราบใดที่คุณไม่ "กระเด้ง" scrollview ดูเหมือนว่า KVO จะไม่ถูกกระตุ้นเมื่ออยู่ในส่วนที่ตีกลับ การเรียกผู้รับมอบสิทธิ์สำหรับ contentOffset จะถูกเรียกใช้
Joris Mans

7

โปรดลองวิธีแก้ปัญหาของฉันและแจ้งให้เราทราบว่าทำไมสิ่งนี้ถึงไม่ดีเท่ากับคำตอบก่อนหน้า

- (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
ฉันทำสิ่งที่คล้ายกับสิ่งนี้และนี่เป็นทางออกที่ดีที่สุดได้อย่างง่ายดาย ฉันได้ลองแฮ็คและไลบรารี่หลายอันแล้วและนี่ก็เป็นอันเดียวที่ใช้งานได้กับ iOS 9 ด้วย tableView ที่ไม่ครอบคลุมทั้งหน้าจอ รุ่งโรจน์!
skensell

6

วิธีหนึ่งที่ฉันทำสำเร็จคือต่อไปนี้

ลงทะเบียนคอนโทรลเลอร์มุมมองUIScrollViewDelegateของคุณUITableViewเพื่อเป็นตัวอย่างของคุณ

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

จากUIScrollViewDelegateวิธีการภายในคุณสามารถรับ contentOffset ใหม่และแปลUINavigationBarขึ้นหรือลงตามนั้น

การตั้งค่าอัลฟาของการชมย่อยสามารถทำได้โดยขึ้นอยู่กับค่าเกณฑ์และปัจจัยที่คุณสามารถตั้งค่าและคำนวณได้

หวังว่ามันจะช่วย!


พบโพสต์ที่คล้ายกันที่นี่
Diana Sule

ขอบคุณไดอาน่า! ฉันสงสัยว่าฉันอาจต้องใช้วิธีการ UIScrollViewDelegate แต่ดูเหมือนว่ามันอาจเกินความเป็นไปได้เล็กน้อย เมื่อฉันเข้าใจสิ่งนี้ฉันจะโพสต์มัน ไชโย!
El Mocoso

4

นอกจากคำตอบของ Iwburk ฉันได้เพิ่มสิ่งต่อไปนี้เพื่อแก้ไขปัญหาอัลฟาบนแถบนำทางที่ไม่ได้กำหนดเองและเพื่อรีเซ็ตแถบนำทางในวิธีการ 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];
}

มีวิธีอื่นนอกเหนือจากการวนลูปผ่าน subviews หรือไม่?
thisiscrazy4

จากสิ่งที่ฉันสามารถบอกได้ในแถบการนำทางที่ไม่ได้กำหนดเองจะมีการชมรวมทั้งหมด 4 ครั้ง: _UINavigationBarBackground, UINavigationItemView, UINavigationItemButtonView และ _UINavigationBarBackIndicatorView การวนซ้ำค่อนข้างเร็วและไม่มีผลต่อประสิทธิภาพการทำงานของแอพของฉัน
blueice

เนื่องจาก _UINavigationBarBackground ดูเหมือนจะเป็นมุมมองย่อยแรกเสมอคุณสามารถเข้าถึงส่วนที่เหลือได้โดยตรง: ((UIView *) self.navigationController.navigationBar.subviews [1]) alpha = alpha;
blueice

4

ฉันกำลังมองหาวิธีการแก้ปัญหาที่ได้รับอนุญาตสำหรับสไตล์และพฤติกรรมใด ๆ คุณจะสังเกตเห็นว่าพฤติกรรมการควบแน่นของแถบนั้นแตกต่างกันในแอพต่างๆ และแน่นอนว่ารูปลักษณ์ของแถบนั้นแตกต่างกันอย่างสิ้นเชิงระหว่างแอพ

ฉันสร้างวิธีแก้ไขปัญหานี้ด้วยhttps://github.com/bryankeller/BLKFlexibleHeightBar/

คุณสามารถกำหนดกฎพฤติกรรมของคุณเองเพื่อควบคุมวิธีการและเวลาที่แถบย่อและขยายและคุณสามารถกำหนดวิธีที่คุณต้องการให้ภาพรวมของแถบตอบสนองต่อการควบแน่นหรือการเติบโต

ลองดูที่โครงการของฉันถ้าคุณต้องการความยืดหยุ่นอย่างมากในการสร้างส่วนหัวของบาร์ที่คุณคิดได้


ฉันจะเพิ่มปุ่มลงใน customHeaderView นี้ได้อย่างไรซึ่งจะซ่อนเมื่อฉันเลื่อนขึ้นฉันไม่ต้องการปุ่มแบบคงที่มันเป็นไปได้หรือไม่ฉันพยายามสร้างปุ่มเดียวเป็นมุมมองย่อย แต่ไม่ได้รับการสัมผัสใด ๆ
abhimuralidharan

3

ฉันพยายามที่จะเลียนแบบพฤติกรรมนี้ในสถานการณ์ที่ฉันต้องการส่วนหัวที่กำหนดเองเกี่ยวกับ UITableView ฉันกลิ้งแถบ "การนำทาง" ของตัวเองเพราะสิ่งนี้อยู่ด้านล่างของสิ่งอื่น ๆ บนหน้าและฉันต้องการให้ส่วนหัวของส่วนตามพฤติกรรม "การเชื่อมต่อ" เริ่มต้น ฉันคิดว่าฉันพบวิธีที่ฉลาดและรวบรัดในการปรับ UITableView / UIScrollView พร้อมกับวัตถุอื่นในสไตล์ที่คล้ายกับที่เห็นใน Facebook / Instagram / Chrome / etc ปพลิเคชัน

ในไฟล์. xib ของฉันฉันได้โหลดส่วนประกอบของฉันลงในมุมมองอิสระแล้ว: http://imgur.com/0z9yebJ (ขออภัยไม่มีตัวแทนไปยังรูปภาพอินไลน์)

โปรดสังเกตว่าในแถบด้านข้างซ้ายจะมีการเรียงลำดับตารางด้านหลังมุมมองส่วนหัวหลัก คุณไม่สามารถบอกได้จากภาพหน้าจอ แต่มันก็มีตำแหน่ง y เช่นเดียวกับมุมมองส่วนหัวหลัก เนื่องจากมันขยายออกไปจากสายตาคุณสมบัติ contentInset บน UITableView ตั้งค่าเป็น 76 (ความสูงของมุมมองส่วนหัวหลัก)

ในการทำให้มุมมองส่วนหัวหลักเลื่อนขึ้นพร้อม ๆ กันด้วย UIScrollView ฉันใช้เมธอด scrollViewDidScroll ของ UIScrollViewDelegate เพื่อทำการคำนวณบางอย่างและเปลี่ยน contentInset ของ UIScrollView รวมถึงเฟรมของมุมมองส่วนหัวหลัก

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

คำสั่งแรกถ้าส่วนใหญ่เป็นการยกที่หนักหน่วง แต่ฉันต้องรวมอีกสองอันเพื่อจัดการกับสถานการณ์ที่ผู้ใช้กำลังลากอย่างแรงและค่า contentOffset เริ่มต้นที่ส่งไปยัง scrollViewDidScroll อยู่นอกช่วงแรกถ้าคำสั่งก่อน

ท้ายที่สุดมันก็ใช้ได้ดีสำหรับฉัน ฉันเกลียดการโหลดโปรเจ็กต์ของฉันด้วยคลาสย่อยที่ป่อง ฉันไม่สามารถพูดได้ว่านี่เป็นวิธีแก้ปัญหาที่ดีที่สุดหรือไม่ (ฉันลังเลที่จะใส่รหัสใด ๆ ใน scrollViewDidScroll เพราะมันถูกเรียกตลอดเวลา) แต่การใช้รหัสนั้นน้อยที่สุดที่ฉันเคยเห็น วิธีแก้ปัญหาสำหรับปัญหานี้และไม่เกี่ยวข้องกับการซ้อน UITableView ใน UIScrollView (Apple แนะนำกับสิ่งนี้ในเอกสารประกอบและเหตุการณ์การสัมผัสจบลงเล็กน้อยขี้ขลาดบน UITableView) หวังว่านี่จะช่วยใครซักคน!


3

HidingNavigationBarโครงการที่ยอดเยี่ยมที่ซ่อนแถบนำทางและแถบแท็บหากคุณต้องการ

HidingNavigationBar รองรับการซ่อน / แสดงองค์ประกอบมุมมองต่อไปนี้:

UINavigationBar

UINavigationBar และส่วนขยาย UIView

UINavigationBar และ UIToolbar

UINavigationBar และ UITabBar

https://github.com/tristanhimmelman/HidingNavigationBar


2

ฉันลองใช้งาน GTScrollNavigationBar แต่แอพของฉันต้องการให้ฉันแก้ไขข้อ จำกัด เลย์เอาต์อัตโนมัติ ฉันตัดสินใจที่จะยกตัวอย่างการติดตั้ง GitHub ในกรณีที่คนอื่นต้องทำด้วยเลย์เอาต์อัตโนมัติ ปัญหาอื่น ๆ ที่ฉันมีกับการใช้งานอื่น ๆ ส่วนใหญ่คือผู้คนไม่ได้กำหนดขอบเขตของมุมมองเลื่อนเพื่อหลีกเลี่ยงเอฟเฟกต์การเลื่อนพารัลแลกซ์ที่คุณสร้างในขณะที่คุณเลื่อนและปรับขนาด scrollview พร้อมกัน

ลองใช้JSCollapsingNavBarViewControllerหากคุณต้องการใช้เลย์เอาต์อัตโนมัติ ฉันได้รวมรุ่นสองรุ่นรุ่นหนึ่งมีแถบนำทางเท่านั้นและอีกรุ่นหนึ่งที่มีแถบย่อยด้านล่างแถบนำทางที่ยุบก่อนที่จะยุบแถบนำทาง


1

ฉันลองด้วยวิธีนี้ฉันหวังว่ามันจะช่วย เพียงใช้รหัสในวิธีมอบหมายและตั้งค่าเป็นมุมมอง / มุมมองย่อยที่ต้องการ

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

ส่วนขยายของคำตอบของ @Iwburk ... แทนที่จะเปลี่ยนต้นกำเนิดของแถบนำทางฉันต้องการขยาย / ลดขนาดของแถบนำทาง

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

มันยังใช้งานไม่ได้กับstoppedScrollingวิธีนี้ไม่ควรโพสต์การอัพเดทเมื่อฉันมีมัน


0

วิธีการทั้งหมดเหล่านี้ดูเหมือนจะซับซ้อนเกินไป ... ดังนั้นโดยธรรมชาติแล้วฉันสร้างขึ้นเอง:

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? คุณใช้ส่วนขยายของframeคุณสมบัติหรือไม่?
Ely

0

ฉันพบคำตอบทั้งหมดที่ให้ไว้ใน Objective-C นี่คือคำตอบของฉันใน Swift 3 นี่คือรหัสทั่วไปมากและสามารถใช้โดยตรง มันทำงานได้กับทั้ง UIScrollView และ 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
    }
}

ตรรกะของการตั้งค่าอัลฟาเป็นรายการนำทางถูกคัดลอกจาก @ WayneBurkettคำตอบและเขียนใหม่ใน Swift 3

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.