超簡單!!! iOS設定狀態列、導航欄按鈕、標題、顏色、透明度,偏移等
2021-06-24由 遊戲時裝分享 發表于 農業
導航欄一般寫什麼
1. 要實現以下這些效果都非常簡單
2. 廢話不多說,先看看實現效果
3. 下面告訴你我為什麼說實現這些效果非常簡單
比如說要實現螞蟻森林的導航欄效果(有以下幾個需求):
剛進入導航欄透明、兩邊按鈕和文字都是白色、狀態列也是白色
向上滾動後導航欄背景由透明逐漸變成白色
當超過某一點後,標題變成
黑色
、狀態列變成
黑色
、兩邊按鈕變成
藍色
實現步驟:
3。1。 實現剛進入導航欄透明、兩邊按鈕和文字都是白色、狀態列也是白色
override func viewDidLoad()
{
super。viewDidLoad()
// 設定導航欄顏色為白色
navBarBarTintColor = 。white
// 設定剛進入頁面時透明度為0
navBarBackgroundAlpha = 0
}
3。2。 實現剩下兩個需求
func scrollViewDidScroll(_ scrollView: UIScrollView){ let offsetY = scrollView
。contentOffset。y
if
(offsetY > NAVBAR_COLORCHANGE_POINT) { let alpha = (offsetY - NAVBAR_COLORCHANGE_POINT) / CGFloat(kNavBarBottom)
// 向上滾動後導航欄背景由透明逐漸變成白色
navBarBackgroundAlpha = alpha
if
(alpha >
0。5
){
// 當超過某一點後,兩邊按鈕變成藍色
navBarTintColor = UIColor(red:
0
,green:
0。478431
,blue:
1
,alpha:
1。0
)
// 標題變成黑色
navBarTitleColor =
。black
// 狀態列變成黑色
statusBarStyle =
。default
}
else
{
// 當沒有超過某點,上面屬性還原
navBarTintColor =
。white
navBarTitleColor =
。white
statusBarStyle =
。lightContent
}}
else
{navBarBackgroundAlpha =
0
navBarTintColor =
。white
navBarTitleColor =
。white
statusBarStyle =
。lightContent
}}
3。3。 發現沒有,改變相關屬性只要一句程式碼就完全搞定了!!!
// 一行程式碼搞定導航欄顏色
navBarBarTintColor = 。white
// 一行程式碼搞定導航欄透明度
navBarBackgroundAlpha =
alpha
// 一行程式碼搞定導航欄兩邊按鈕顏色navBarTintColor = UIColor(
red
:
0
,
green
:
0。478431
,
blue
:
1
,
alpha
:
1。0
)
// 一行程式碼搞定導航欄上標題顏色
navBarTitleColor = 。black
// 一行程式碼搞定狀態列是 default 還是 lightContent
statusBarStyle = 。
default
3。4。 說了這麼多,看看幾句程式碼能否實現我們需要的效果吧
3。5。 有人可能會問:這只是在一個介面裡面,但是涉及到push、pop、右滑手勢怎麼辦呢?
答:沒關係,我已經給你處理好了,你不用寫一句程式碼!!!那麼看看效果吧
4. 好了,說了這麼多接下來看看如何實現的吧
4。1 實現導航欄透明漸變就很簡單了,網上一找一大堆,大部分都是透過加一層的方法來實現(在這裡就是加一個view到navigationBar上)
// set navigationBar barTintColor
fileprivate
func
wr_setBackgroundColor(color:UIColor)
{
if
(backgroundView ==
nil
){
// add a image(nil color) to _UIBarBackground make it clear
setBackgroundImage(
UIImage
(),
for
:。
default
)backgroundView =
UIView
(frame:
CGRect
(x:
0
,y:
0
,width:
Int
(bounds。width), height:
64
))
// _UIBarBackground is first subView for navigationBar
subviews。first?。insertSubview(backgroundView ??
UIView
(), at:
0
)} backgroundView?。backgroundColor = color}
// set _UIBarBackground alpha (_UIBarBackground subviews alpha <=>fileprivate
func
wr_setBackgroundAlpha(alpha:CGFloat)
{
let
barBackgroundView = subviews[
0
]barBackgroundView。alpha = alpha}
4。2 你以為就這樣結束了嗎,來看看下面的問題,當由透明的導航欄右滑到不透明的導航欄看看會出現什麼情況?
4。3 處理右滑返回手勢問題
我們都知道,導航欄是屬於導航控制器的,一個導航欄不可能出現兩個顏色,那麼右滑突兀怎麼解決呢?兩個方法,一個方法是自定義導航欄(後面會說),另一個方法是改變導航欄顏色,我們假設當前控制器為fromVC,返回的控制器為toVC,如果可以實現從 fromVC 右滑到 toVC 導航欄顏色漸變那麼問題就解決了!但是導航欄只有一個顏色啊~~~怎麼辦?
同樣,可以透過加一層的方法來解決。我們可以記錄一下 fromVC消失前對應的導航欄顏色 和 toVC 當前的導航欄顏色,然後根據右滑進度percentComplete,來計算漸變色,這樣問題就解決了!
記錄ViewController對應導航欄的顏色和透明度
// navigationBar barTintColorvar
navBarBarTintColor:
UIColor
{
get
{
guard
let
barTintColor = objc_getAssociatedObject(
self
,&
AssociatedKeys
。navBarBarTintColor)
as
?
UIColor
else
{
return
UIColor
。defaultNavBarBarTintColor }
return
barTintColor }
set
{objc_setAssociatedObject(
self
,&
AssociatedKeys
。navBarBarTintColor, newValue, 。
OBJC_ASSOCIATION_RETAIN_NONATOMIC
)
if
customNavBar。isKind(of:
UINavigationBar
。
self
){
let
navBar = customNavBar
as
!
UINavigationBar
navBar。wr_setBackgroundColor(color: newValue) }
else
{
if
pushToCurrentVCFinished ==
true
&& pushToNextVCFinished ==
false
{navigationController?。setNeedsNavigationBarUpdate(barTintColor: newValue) } } }}
// navigationBar _UIBarBackground alphavar
navBarBackgroundAlpha:
CGFloat
{
get
{
guard
let
barBackgroundAlpha = objc_getAssociatedObject(
self
,&
AssociatedKeys
。navBarBackgroundAlpha)
as
?
CGFloat
else
{
return
1。0
}
return
barBackgroundAlpha }
set
{objc_setAssociatedObject(
self
,&
AssociatedKeys
。navBarBackgroundAlpha, newValue, 。
OBJC_ASSOCIATION_RETAIN_NONATOMIC
)
if
customNavBar。isKind(of:
UINavigationBar
。
self
){
let
navBar = customNavBar
as
!
UINavigationBar
navBar。wr_setBackgroundAlpha(alpha: newValue) }
else
{
if
pushToCurrentVCFinished ==
true
&& pushToNextVCFinished ==
false
{navigationController?。setNeedsNavigationBarUpdate(barBackgroundAlpha: newValue) } } }}
交換系統方法 _updateInteractiveTransition(監控右滑返回手勢的進度)
// swizzling system method: _updateInteractiveTransition
func
wr_updateInteractiveTransition(
_
percentComplete: CGFloat)
{
guard
let
topViewController = topViewController,
let
coordinator = topViewController。transitionCoordinator
else
{wr_updateInteractiveTransition(percentComplete)
return
}
let
fromVC = coordinator。viewController(forKey: 。from)
let
toVC = coordinator。viewController(forKey: 。to) updateNavigationBar(fromVC: fromVC, toVC: toVC, progress: percentComplete) wr_updateInteractiveTransition(percentComplete)
}
根據 fromVC 與 toVC 的導航欄顏色 配合 返回手勢進度計算漸變色
// Calculate the middle Color with translation percentclass fileprivate func middleColor(
from
Color: UIColor,
to
Color: UIColor, percent: CGFloat) -> UIColor{ // get current color RGBA var
from
Red: CGFloat =
0
var
from
Green: CGFloat =
0
var
from
Blue: CGFloat =
0
var
from
Alpha: CGFloat =
0
from
Color。getRed(&
from
Red, green: &
from
Green, blue: &
from
Blue, alpha: &
from
Alpha) // get
to
color RGBA var
to
Red: CGFloat =
0
var
to
Green: CGFloat =
0
var
to
Blue: CGFloat =
0
var
to
Alpha: CGFloat =
0
to
Color。getRed(&
to
Red, green: &
to
Green, blue: &
to
Blue, alpha: &
to
Alpha) // calculate middle color RGBA let newRed =
from
Red + (
to
Red -
from
Red) * percent let newGreen =
from
Green + (
to
Green -
from
Green) * percent let newBlue =
from
Blue + (
to
Blue -
from
Blue) * percent let newAlpha =
from
Alpha + (
to
Alpha -
from
Alpha) * percent return UIColor(red: newRed, green: newGreen, blue: newBlue, alpha: newAlpha)}
改變導航欄顏色和透明度
fileprivate func updateNavigationBar(
from
VC: UIViewController?,
to
VC: UIViewController?, progress: CGFloat){ // change navBarBarTintColor let
from
BarTintColor =
from
VC?。navBarBarTintColor ?? 。
default
NavBarBarTintColor let
to
BarTintColor =
to
VC?。navBarBarTintColor ?? 。
default
NavBarBarTintColor let newBarTintColor = UIColor。middleColor(
from
Color:
from
BarTintColor,
to
Color:
to
BarTintColor, percent: progress)
set
NeedsNavigationBarUpdate(barTintColor: newBarTintColor) // change navBarTintColor let
from
TintColor =
from
VC?。navBarTintColor ?? 。
default
NavBarTintColor let
to
TintColor =
to
VC?。navBarTintColor ?? 。
default
NavBarTintColor let newTintColor = UIColor。middleColor(
from
Color:
from
TintColor,
to
Color:
to
TintColor, percent: progress)
set
NeedsNavigationBarUpdate(tintColor: newTintColor) // change navBarTitleColor let
from
TitleColor =
from
VC?。navBarTitleColor ?? 。
default
NavBarTitleColor let
to
TitleColor =
to
VC?。navBarTitleColor ?? 。
default
NavBarTitleColor let newTitleColor = UIColor。middleColor(
from
Color:
from
TitleColor,
to
Color:
to
TitleColor, percent: progress)
set
NeedsNavigationBarUpdate(titleColor: newTitleColor) // change navBar _UIBarBackground alpha let
from
BarBackgroundAlpha =
from
VC?。navBarBackgroundAlpha ?? UIColor。
default
BackgroundAlpha let
to
BarBackgroundAlpha =
to
VC?。navBarBackgroundAlpha ?? UIColor。
default
BackgroundAlpha let newBarBackgroundAlpha = UIColor。middleAlpha(
from
Alpha:
from
BarBackgroundAlpha,
to
Alpha:
to
BarBackgroundAlpha, percent: progress)
set
NeedsNavigationBarUpdate(barBackgroundAlpha: newBarBackgroundAlpha)}
好了!來看看處理後的效果吧,是不是好多了呢~
4。4 別高興的太早,還有其他問題。在右滑返回手勢的過程中,導航欄顏色和透明度會根據手勢變化而變化。但是一旦鬆手,系統會自動完成或取消返回操作。導致透明度停留在最後的那個狀態。
4。5 咱們來處理右滑返回手勢中斷的問題吧~
透過遵守UINavigationBarDelegate協議,實現
optional public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool // same as push methods 方法來監聽右滑返回手勢中斷的情況
public
func
navigationBar(
_
navigationBar: UINavigationBar, shouldPop item: UINavigationItem)
->
Bool
{
if
let
topVC = topViewController,
let
coor = topVC。transitionCoordinator, coor。initiallyInteractive {
if
#available(iOS
10。0
,*) { coor。notifyWhenInteractionChanges({ (context)
in
self
。dealInteractionChanges(context) }) }
else
{coor。notifyWhenInteractionEnds({ (context)
in
self
。dealInteractionChanges(context) }) }
return
true
}
let
itemCount = navigationBar。items?。
count
??
0
let
n= viewControllers。
count
>= itemCount ?
2
:
1
let
popToVC = viewControllers[viewControllers。
count
-n] popToViewController(popToVC, animated:
true
)
return
true
}
// deal the gesture of return break offprivate
func
dealInteractionChanges(
_
context: UIViewControllerTransitionCoordinatorContext)
{
let
animations: (
UITransitionContextViewControllerKey
)-> () = {
let
curColor = context。viewController(forKey: $
0
)?。navBarBarTintColor ??
UIColor
。defaultNavBarBarTintColor
let
curAlpha = context。viewController(forKey: $
0
)?。navBarBackgroundAlpha ??
UIColor
。defaultBackgroundAlpha
self
。setNeedsNavigationBarUpdate(barTintColor: curColor)
self
。setNeedsNavigationBarUpdate(barBackgroundAlpha: curAlpha) }
// after that, cancel the gesture of return
if
context。isCancelled {
let
cancelDuration:
TimeInterval
=context。transitionDuration *
Double
(context。percentComplete)
UIView
。animate(withDuration: cancelDuration) { animations(。from) } }
else
{
// after that, finish the gesture of return
let
finishDuration:
TimeInterval
=context。transitionDuration *
Double
(
1
-context。percentComplete)
UIView
。animate(withDuration: finishDuration) { animations(。to) } }}
處理後:
4。5 同樣,push和pop也要處理一下 ~
但是push和pop我們拿不到進度怎麼辦呢?處理辦法是,透過交換方法,自己實現給push和pop新增進度。
// MARK: swizzling push
struct
pushProperties
{fileprivate
static
let
pushDuration =
0。13
fileprivate
static
var
displayCount =
0
fileprivate
static
var
pushProgress:
CGFloat
{
let
all:
CGFloat
=
CGFloat
(
60。0
*pushDuration)
let
current =
min
(all,
CGFloat
(displayCount))
return
current / all }}
// swizzling system method: pushViewController
func
wr_pushViewController(
_
viewController: UIViewController, animated: Bool)
{
var
displayLink:
CADisplayLink
?=
CADisplayLink
(target:
self
,selector: #selector(pushNeedDisplay)) displayLink?。add(to:
RunLoop
。main, forMode: 。defaultRunLoopMode)
CATransaction
。setCompletionBlock { displayLink?。invalidate() displayLink =
nil
pushProperties。displayCount =
0
viewController。pushToCurrentVCFinished =
true
};
CATransaction
。setAnimationDuration(pushProperties。pushDuration)
CATransaction
。begin() wr_pushViewController(viewController, animated: animated)
CATransaction
。commit()}
計算push進度,並且根據進度更新導航欄顏色和透明度
//
change
navigationBar barTintColor smooth before push
to
current VC finished
or
before
pop
to
current VC finishedfunc pushNeedDisplay(){ guard
let
topViewController = topViewController,
let
coordinator = topViewController。transitionCoordinator
else
{
return
}pushProperties。displayCount +=
1
let
pushProgress = pushProperties。pushProgress //
(
“第(pushProperties。displayCount)次push的進度:(pushProgress)”
)
let
fromVC = coordinator。viewController(forKey: 。from)
let
toVC = coordinator。viewController(forKey: 。
to
)updateNavigationBar(fromVC: fromVC, toVC: toVC, progres
s:
pushProgress)}
pop的設定方法也一樣,具體請檢視程式碼
4。6 以上都是改變導航欄的顏色和透明度,同樣改變導航欄的按鈕顏色和標題顏色,以及狀態列狀態都和改變顏色一樣,每個ViewController記錄一下。需要改變的時候,ViewController 改變一下屬性就ok了,非常方便!
那麼接下來看一下其他demo的動態效果圖吧~~~
新浪微博個人中心
qq空間
知乎日報
螞蟻森林
5. 好了,來說說前面提過的自定義導航欄吧
5。1 至於怎麼自定義導航欄我就不說了,來說說如果是自定義導航欄,那怎麼才能像之前一樣一句程式碼改變導航欄屬性。
經過封裝,自定義導航欄只需要多寫一行程式碼!!!
// 自定義導航欄必須設定這個屬性!!!!!!customNavBar = navBar
如果把這行程式碼放在基類控制器中,那麼其他所有繼承基類控制器都可以一句程式碼修改導航欄屬性~~~
看看自定義導航欄的效果吧,是不是也很棒
6. 最後看一下移動導航欄的效果
實現程式碼
/// 設定導航欄在垂直方向上平移多少距離
func
wr_setTranslationY(translationY:CGFloat)
{transform =
CGAffineTransform
。
init
(translationX:
0
,y: translationY)} ——-
到這裡就結束��,具體程式碼請前往:
——-
參考資料:
——-
歡迎關注我的微博: