iOS 隱藏導航欄下的1px黑色線條(runtime)

最近老板有個需求,需要把項目中所有的線條都給去掉,包括導航欄上的那根黑線,本來這個需求也很正常.可是我們的項目時間比較久遠了,所以有的控制器繼承了基類,而有的則沒有,難道我需要在每個控制器里面都寫一遍隱藏的代碼么?特別是在一個需求多變的公司,假如哪次老板說需要這條線了,那我得重來一次么.不過我不不太喜歡寫重復性的代碼,所以就開始著手解決這件事了.

第一種:

由于1px的黑色線條是個黑色陰影圖片,所以我們可以通過設置這張圖片去讓它隱藏或者更換顏色.

①全局設置

//兩句話需要配合使用才有效
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:@"white.jpg"] forBarPosition:UIBarPositionAny barMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setShadowImage:[UIImage new]];

這是一種比較官方的一種寫法,不過這種方法會修改navigationBar的translucent(半透明)屬性,這個屬性會影響到我們的界面布局.
接下來我通過一個demo去驗證它,我創(chuàng)建了兩個ViewController,分別對應兩個UINavigationController.FirstVC采用了StoryBoard生成的界面,而ThirdVC則是通過代碼生成的,當我們在AppDelegate.m中使用了上述方法后:

通過SB生成的導航控制器

通過代碼生成的導航控制器

UI層級

從圖片我們可以發(fā)現,使用了修改圖片來達到隱藏線條的目的是完全可行的.但是,在SB中可能不會有太 大影響,不過在非SB的布局中,我們發(fā)現view的渲染是從導航欄的底部開始的,也是說View的Y點為64,所以說在該視圖上的控件的Frame也會改變.如果說我們在該界面有一個隱藏和顯示導航欄的操作,出現的后果則是:

//導航欄顯示下的Frame
self.view.frame = CGRectMake(0, 64, 375, 603);
//導航欄隱藏下的Frame
self.view.frame = CGRectMake(0, 0, 375, 667);

不過既然是問題,那就有解決的辦法,我們可以通過設置translucentYES就可以解決了,這時候View的布局會充滿全屏.

②單頁面設置

-(void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"white.jpg"] forBarMetrics:UIBarMetricsDefault];
    [self.navigationController.navigationBar setShadowImage:[UIImage new]];
}
-(void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"white.jpg"] forBarMetrics:UIBarMetricsDefault];
    [self.navigationController.navigationBar setShadowImage:nil];
}

第二種:

由于第一種的方法讓我項目中的VC出現了各種各樣的問題,而我又不想一個個去改,所以又有了接下來的辦法.我們可以通過遍歷導航欄的子控件,尋找到這個黑色線條,在合適的時候隱藏或者顯示,這時候我們就不會改變UINavigationBartranslucent了,也不會影響我們視圖的布局.

①單頁面設置

viewWillAppear:或者viewWillDisappear:中調用hideNavigationBarBottomLine:方法就可以完成對線條的隱藏或顯示.

-(void)hideNavigationBarBottomLine:(BOOL)hidden{
    //iOS10及以上的隱藏方法
    if ([UIDevice currentDevice].systemVersion.floatValue >= 10.0) {
        if ([self.navigationController.navigationBar respondsToSelector:@selector(setBackgroundImage:forBarMetrics:)]) {
            NSArray *list=self.navigationController.navigationBar.subviews;
            for (id obj in list) {
                //10.0的系統(tǒng)字段不一樣
                UIView *view =   (UIView*)obj;
                for (id obj2 in view.subviews) {
                    if ([obj2 isKindOfClass:[UIImageView class]]) {
                        UIImageView *image =  (UIImageView*)obj2;
                        if (image.frame.size.height <= 1) {
                            image.hidden = hidden;
                        }
                    }
                    
                    if ([obj2 isKindOfClass:[UIView class]]) {
                        UIView *view2 = (UIView *)obj2;
                        if (view2.frame.size.height <= 1) {
                            view2.hidden = hidden;
                        }
                    }
                }
            }
            
        }
        
        return;
    }
    //iOS10以下的隱藏方法
    if ([self.navigationController.navigationBar respondsToSelector:@selector(setBackgroundImage:forBarMetrics:)]){
        NSArray *list = self.navigationController.navigationBar.subviews;
        for (id obj in list) {
            if ([obj isKindOfClass:[UIImageView class]]) {
                UIImageView *imageView = (UIImageView *)obj;
                NSArray *list2 = imageView.subviews;
                for (id obj2 in list2) {
                    if ([obj2 isKindOfClass:[UIImageView class]]) {
                        UIImageView *imageView2 = (UIImageView *)obj2;
                        if (imageView2.frame.size.height <= 1) {
                            imageView2.hidden = hidden;
                        }
                    }
                    if ([obj2 isKindOfClass:[UIView class]]) {
                        UIView *view2 = (UIView *)obj2;
                        if (view2.frame.size.height <= 1) {
                            view2.hidden = hidden;
                        }
                    }
                }
            }
        }
    }
}

②最終方案(全局設置)

but,之前提過,因為不想寫重復性的代碼,所以才來折騰的,當時就想,如果項目中所有的類都繼承了基類該多好啊,在基類的viewWillAppear:或者viewWillDisappear:中改一次就好了.想到這里,我突然想起來有一個方法可以每次都能調用這些方法,那就是runtime.我們可以寫一個UIViewController的類別,通過runtime我們可以獲取到UIViewController生命周期方法,在viewDidLoad中,我們只用寫一次代碼就可以了,后續(xù)的視圖控制器只要在生命周期中,都會到類別中去掉用隱藏的方法,方便又快捷,不要寫大量重復性的代碼.并且哪次老板需要顯示線條的時候,只要把這個類別移除就可以了,不用涉及到項目中其他類的操作.對于oc運行時特性不了解的自行百度,具體的實現思路如下:

通過重寫NSObject類都有的Load方法,使用runtime替換系統(tǒng)的viewDidLoad方法為自己的hide_viewDidLoad方法.

+(void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        
        SEL originalSelector1 = @selector(viewDidLoad);
        SEL swizzledSelector1 = @selector(hide_viewDidLoad);
        
        Method originalMethod1 = class_getInstanceMethod(class, originalSelector1);
        Method swizzledMethod1 = class_getInstanceMethod(class, swizzledSelector1);
        
        BOOL didAddMethod =
        class_addMethod(class,
                        originalSelector1,
                        method_getImplementation(swizzledMethod1),
                        method_getTypeEncoding(swizzledMethod1));
        
        if (didAddMethod) {
            class_replaceMethod(class,
                                swizzledSelector1,
                                method_getImplementation(originalMethod1),
                                method_getTypeEncoding(originalMethod1));
            
        } else {
            method_exchangeImplementations(originalMethod1, swizzledMethod1);
        }
    });
}

在替換的方法中寫我們相應的需求:

-(void)hide_viewDidLoad {
    [self hide_viewDidLoad];
    
    if (self.navigationController) {
        [self hideNavigationBarBottomLine:YES];
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,802評論 25 709
  • *7月8日上午 N:Block :跟一個函數塊差不多,會對里面所有的內容的引用計數+1,想要解決就用__block...
    炙冰閱讀 2,715評論 1 14
  • 在中國古代陶瓷史中,有一個一直被傳說和迷霧籠罩著的古窯,這就是五代十國時期出現的官窯——柴窯。但不同于其他官窯的是...
    丑小蟲閱讀 1,820評論 3 7
  • AR EasyAR iOS的SDK功能: 匹配場景圖片,在匹配成功的圖片上加載圖像,3D模型,視頻; EasyAR...
    白菜松閱讀 4,032評論 1 6
  • 嫁錯一個女,白養(yǎng)二十年。 01 林意涵是臺大四年級日文系的學生,為了提高日文水平,她經常上日本的臉書,還交到了一個...
    江湖覓道閱讀 524評論 0 0

友情鏈接更多精彩內容