問題:在iOS9.0之前芳撒,我們往往自定義個UIWindow加在狀態(tài)欄上,就可以實現(xiàn)狀態(tài)欄點擊回到頂部的功能阶淘⊙梅裕可是在iOS9.0之后,一個運用跳轉(zhuǎn)到另一個運用溪窒,左上角會有返回其他運用的按鈕坤塞,這個按鈕被我們添加在頂部的view擋住了冯勉,導(dǎo)致系統(tǒng)的返回按鈕失效?這就說明我們之前的方案存在BUG摹芙,于是我問了很多人灼狰,都說不知道該如何處理。
如需要了解iOS9.0之前他們集成點擊狀態(tài)欄回到頂部,可以參考以下文章:
友情提示
-
注意:以下前面方法僅對之前已經(jīng)集成好點擊狀態(tài)欄回到頂部的擴展
- 最后面才是完整版的代碼模塊
首先浮禾,看看效果圖
github: MGDYZB
一交胚、RunTime獲取屬性
1.第一步: 利用KVC從UIApplication.sharedApplication()取得狀態(tài)欄statusBarView,利用運行時機制查看statusBarView所有的屬性名稱
var statusBarView: UIView?
let key = "statusBar"
let app = UIApplication.sharedApplication()
if app.respondsToSelector(NSSelectorFromString("statusBar")) {
statusBarView = app.valueForKey(key) as? UIView
}
// 1.利用運行時機制查看所有的屬性名稱
var count : UInt32 = 0
let ivars = class_copyIvarList(statusBarView!.classForCoder, &count)
for i in 0..<count {
let ivar = ivars[Int(i)]
let name = ivar_getName(ivar)
print(String(UTF8String: name))
}
- 結(jié)果打印結(jié)果:
**Optional("_statusBarServer")**
**Optional("_inProcessProvider")**
**Optional("_showsForeground")**
**Optional("_backgroundView")**
**Optional("_foregroundView")**
**Optional("_doubleHeightLabel")**
**Optional("_doubleHeightLabelContainer")**
**Optional("_currentDoubleHeightText")**
**Optional("_currentRawData")**
**Optional("_interruptedAnimationCompositeViews")**
**Optional("_newStyleBackgroundView")**
**Optional("_newStyleForegroundView")**
**Optional("_slidingStatusBar")**
**Optional("_requestedStyle")**
**Optional("_styleOverrides")**
**Optional("_styleAttributes")**
**Optional("_orientation")**
**Optional("_hidden")**
**Optional("_suppressesHiddenSideEffects")**
**Optional("_foreground")**
**Optional("_registered")**
**Optional("_reservesEmptyTimeRegion")**
**Optional("_waitingOnCallbackAfterChangingStyleOverridesLocally")**
**Optional("_suppressGlow")**
**Optional("_translucentBackgroundAlpha")**
**Optional("_showOnlyCenterItems")**
**Optional("_foregroundViewShouldIgnoreStatusBarDataDuringAnimation")**
**Optional("_localDataOverrides")**
**Optional("_tintColor")**
**Optional("_lastUsedBackgroundColor")**
**Optional("_nextTintTransition")**
**Optional("_overrideHeight")**
**Optional("_disableRasterizationReasons")**
**Optional("_persistentAnimationsEnabled")**
**Optional("_simulatesLegacyAppearance")**
**Optional("_serverUpdatesDisabled")**
**Optional("_homeItemsDisabled")**
**Optional("_statusBarWindow")**
**Optional("_styleDelegate")**
**Optional("_foregroundColor")**
**Optional("_legibilityStyle")**
找到了我們想要的屬性“_foregroundView”
2.第二步:取得key盈电,根據(jù)KVC取得_foregroundView的view蝴簇,進行遍歷
let children = statusBarView!.valueForKeyPath("foregroundView")!.subviews
for child in children! {
print("\\\\\\\\(child)")
}
-
結(jié)果:
/*
如果來到這里
if child.isKindOfClass(NSClassFromString("UIStatusBarBreadcrumbItemView")!) {}方法
說明是從其他運用跳轉(zhuǎn)過來的,需要用一個屬性保存這個child
*/
for child in children! {
if child.isKindOfClass(NSClassFromString("UIStatusBarServiceItemView")!) {
}
if child.isKindOfClass(NSClassFromString("UIStatusBarServiceItemView")!) {
}
if child.isKindOfClass(NSClassFromString("UIStatusBarBreadcrumbItemView")!) {
// 來到這里匆帚,是從其他運用跳轉(zhuǎn)過來的
// 用一個屬性保存這個child
}
}
Tip:以上所有的代碼都在func applicationDidBecomeActive(application: UIApplication) {}執(zhí)行熬词。 有人想問為什么?因為運用每次啟動都要判斷是否是從其他地方跳轉(zhuǎn)吸重,來到這個方法進行判斷是最合適的(這個方法是在程序成為進如入前臺就會調(diào)用)互拾,可以參考AppDelegate完整代碼示例:
二、事件傳遞
我們需要對點擊進行事件傳遞嚎幸,尋找最合適的view去處理這個事件
第一步:我們需要獲取手指點擊的點颜矿,如何獲取呢?
這需要自定義一個UIButton嫉晶,對touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {}進行重寫或衡,通過UITouch獲取觸摸的點,需要用一個touchP屬性記錄當(dāng)前觸摸點
// MGButton.swift
// MGDYZB
// Created by ming on 16/10/31.
// Copyright ? 2016年 ming. All rights reserved.
import UIKit
class MGButton: UIButton {
var touchP: CGPoint = CGPoint.zero
override internal func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesBegan(touches, withEvent: event)
let touch = touches.first
touchP = (touch?.locationInView(touch?.view))!
}
}
第二步:我們需要對這個btn的點擊事件方法進行處理
其實獲取到target就是進入
child.isKindOfClass(NSClassFromString("UIStatusBarBreadcrumbItemView")!)
記錄的child车遂,action就是下圖的"userDidActivateButton:"
@objc func scrollTopWindowclick(btn: MGButton) {
NSLog("點擊了最頂部...")
if let window = UIApplication.sharedApplication().keyWindow {
if (MGScrollTopWindow.shareInstance.backStatusView != nil) { // 從其他運用跳轉(zhuǎn)該運用
if btn.touchP.y < 20 && btn.touchP.x > 6 && btn.touchP.x < MGScrollTopWindow.shareInstance.backStatusView!.width { // 如果觸摸點在系統(tǒng)的返回運用按鈕上面
// 1.包裝Selector之前獲取到的“userDidActivateButton:”方法
let sel = NSSelectorFromString("userDidActivateButton:")
// 2.找到系統(tǒng)的返回運用按鈕target封断,由之前打印可知就是之前記錄的child
MGScrollTopWindow.shareInstance.backStatusView!.performSelector(sel)
}else { // 觸摸點不在系統(tǒng)的返回運用按鈕上面
self.seekAllScrollViewInView(window)
}
}else { // 由主界面進入運用
// 遞歸 這樣就可以獲得所有的View,進行判斷舶担,回到頂部
self.seekAllScrollViewInView(window)
}
}
}
-
Tip:可以參考MGScrollTopWindow完整代碼示例:
AppDelegate完整代碼示例:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// NSThread.sleepForTimeInterval(1.0) //延遲啟動程序
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window!.rootViewController = tabBarVC;
window!.makeKeyAndVisible()
// 點擊狀態(tài)欄滾動到頂部
dispatch_after(1, dispatch_get_main_queue()) { () -> Void in
MGScrollTopWindow.shareInstance.show()
}
}
func applicationDidBecomeActive(application: UIApplication) {
var statusBarView: UIView? // 記錄狀態(tài)欄
var isYunYong = false // 記錄是否從其他運用跳轉(zhuǎn)
var tempView: UIView? // 臨時記錄從其他運用跳轉(zhuǎn)系統(tǒng)左上角返回其他運用的view
let key = "statusBar"
let app = UIApplication.sharedApplication()
if app.respondsToSelector(NSSelectorFromString("statusBar")) {
statusBarView = app.valueForKey(key) as? UIView
}
// 1.利用運行時機制查看所有的屬性名稱
var count : UInt32 = 0
let ivars = class_copyIvarList(statusBarView!.classForCoder, &count)
for i in 0..<count {
let ivar = ivars[Int(i)]
let name = ivar_getName(ivar)
print(String(UTF8String: name))
}
// 2.遍歷
let children = statusBarView!.valueForKeyPath("foregroundView")!.subviews
for child in children! {
if child.isKindOfClass(NSClassFromString("UIStatusBarServiceItemView")!) {
}
if child.isKindOfClass(NSClassFromString("UIStatusBarServiceItemView")!) {
}
if child.isKindOfClass(NSClassFromString("UIStatusBarBreadcrumbItemView")!) {
isYunYong = true
tempView = child
}
}
statusBarView?.addSubview(MGScrollTopWindow.scrollToWindow)
statusBarView?.bringSubviewToFront(MGScrollTopWindow.scrollToWindow)
if isYunYong { // 其他運用跳轉(zhuǎn)過來坡疼,記得賦值
MGScrollTopWindow.shareInstance.backStatusView = tempView
} else { // 記得要清空,否則會崩潰
MGScrollTopWindow.shareInstance.backStatusView = nil
}
}
MGScrollTopWindow完整代碼示例:
// MGScrollTopWindow.swift
// MGDYZB
// Created by ming on 16/10/27.
// Copyright ? 2016年 ming. All rights reserved.
import UIKit
class MGScrollTopWindow: NSObject{
// 單例:因為這個東西從程序運行一直要有衣陶,所以設(shè)計成單例
static let shareInstance: MGScrollTopWindow = MGScrollTopWindow()
// 記錄由一個運用跳轉(zhuǎn)到另一個運用左上角那個系統(tǒng)返回的view柄瑰,是可選類型,可有可無
var backStatusView: UIView?
// 狀態(tài)欄要添加的按鈕剪况,可設(shè)計成“單例”或是“屬性”
static let scrollToWindow: MGButton = {
let btn = MGButton(frame: UIApplication.sharedApplication().statusBarFrame)
btn.backgroundColor = UIColor.clearColor()
btn.hidden = true
return btn
}()
override init() {
super.init()
MGScrollTopWindow.scrollToWindow.addTarget(self, action: Selector("scrollTopWindowclick:"), forControlEvents: UIControlEvents.TouchUpInside)
}
deinit {
print("MGScrollTopWindow--deinit")
}
@objc func scrollTopWindowclick(btn: MGButton) {
NSLog("點擊了最頂部...")
if let window = UIApplication.sharedApplication().keyWindow {
if (MGScrollTopWindow.shareInstance.backStatusView != nil) {
if btn.touchP.y < 20 && btn.touchP.x > 6 && btn.touchP.x < MGScrollTopWindow.shareInstance.backStatusView!.width {
let sel = NSSelectorFromString("userDidActivateButton:")
MGScrollTopWindow.shareInstance.backStatusView!.performSelector(sel)
}else {
self.seekAllScrollViewInView(window)
}
}else {
self.seekAllScrollViewInView(window)
}
}
}
}
// MARK: - 獲取系統(tǒng)的狀態(tài)欄的view教沾,也就是statusBarView
extension MGScrollTopWindow {
func statusBarView() -> UIView {
var statusBar: UIView?
let object = UIApplication.sharedApplication()
if object.respondsToSelector(NSSelectorFromString(key)) {
statusBar = object.valueForKey(key) as? UIView
}
return statusBar!
}
}
// MARK: - show AND hidden
/*
控制要不要點擊狀態(tài)欄回到頂部這個功能
*/
extension MGScrollTopWindow {
func show() {
MGScrollTopWindow.scrollToWindow.hidden = false
}
func hide() {
MGScrollTopWindow.scrollToWindow.hidden = true
}
}
// MARK: - 遞歸尋找ScrollView
extension MGScrollTopWindow {
func seekAllScrollViewInView(view: UIView) {
// 遞歸 這樣就可以獲得所有的View
for subView in view.subviews {
self.seekAllScrollViewInView(subView)
}
// 是否是ScrollView 不是,直接返回
// print("The class is: \\\\\\\\(view.classForCoder)")
guard view.isKindOfClass(UIScrollView.classForCoder()) else {
return
}
let scrollView = view as! UIScrollView
let isShowInWindow = scrollView.intersectsOtherView(nil) && scrollView.window == UIApplication.sharedApplication().keyWindow
if isShowInWindow {
// 是ScrollView滾動到最前面(包括內(nèi)邊距)
var offset = scrollView.contentOffset as CGPoint
offset.y = -scrollView.contentInset.top
scrollView.setContentOffset(offset, animated: true)
}
/*
guard let window = UIApplication.sharedApplication().keyWindow else { return }
// 窗口的window的bounds
let windowBounds = window.convertRect(window.frame,toView: nil)
// 判斷ScrollView是否跟窗口有重疊 沒有重疊译断,直接返回
// 得到subview在窗口中得frame 把subview.superview轉(zhuǎn)到window frame上 nil = window
let newFrame = scrollView.superview!.convertRect(scrollView.frame,toView: nil)
// scrollView.hidden && && scrollView.window == UIApplication.sharedApplication().keyWindow
let isShowInWindow = CGRectIntersectsRect(newFrame, windowBounds) && scrollView.window == UIApplication.sharedApplication().keyWindow
if isShowInWindow {
// 是ScrollView滾動到最前面(包括內(nèi)邊距)
scrollView.scrollRectToVisible(CGRectMake(scrollView.frame.origin.x, 0, 1, 1), animated:true)
}
*/
}
}
如果覺得不錯請到github點贊授翻,感激不盡。轉(zhuǎn)載請注明出處
-
github
項目 | 簡介 |
---|---|
MGDS_Swif | 逗視視頻直播 |
MGMiaoBo | 喵播視頻直播 |
MGDYZB | 斗魚視頻直播 |
MGDemo | n多小功能合集 |
MGBaisi | 高度仿寫百思 |
MGSinaWeibo | 高度仿寫Sina |
MGLoveFreshBeen | 一款電商App |
MGWeChat | 小部分實現(xiàn)微信功能 |
MGTrasitionPractice | 自定義轉(zhuǎn)場練習(xí) |
DBFMDemo | 豆瓣電臺 |
MGPlayer | 一個播放視頻的Demo |
MGCollectionView | 環(huán)形圖片排布以及花瓣形排布 |
MGPuBuLiuDemo | 瀑布流--商品展 |
MGSlideViewDemo | 一個簡單點的側(cè)滑效果,仿QQ側(cè)滑 |
MyResume | 一個展示自己個人簡歷的Demo |
GoodBookDemo | 好書 |