iOS9.0之后他们集成点击状态栏回到顶部
2016-11-01 13:55
447 查看
问题:在iOS9.0之前,我们往往自定义个UIWindow加在状态栏上,就可以实现状态栏点击回到顶部的功能。可是在iOS9.0之后,一个运用跳转到另一个运用,左上角会有返回其他运用的按钮,这个按钮被我们添加在顶部的view挡住了,导致系统的返回按钮失效?这就说明我们之前的方案存在BUG,于是我问了很多人,都说不知道该如何处理。
如需要了解iOS9.0之前他们集成点击状态栏回到顶部,可以参考以下文章:
推荐: http://www.jb51.net/article/92011.htm
了解:http://www.jianshu.com/p/e18356fccbc9
友情提示
#####注意:以下方法仅对之前已经集成好点击状态栏回到顶部的扩展
首先,看看效果图
github: MGDYZB
1.第一步: 利用KVC从UIApplication.sharedApplication()取得状态栏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)) }
结果打印结果:
**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,根据KVC取得_foregroundView的view,进行遍历
let children = statusBarView!.valueForKeyPath("fo 4000 regroundView")!.subviews for child in children! { print("\(child)") }
结果:
/* 如果来到这里 if child.isKindOfClass(NSClassFromString("UIStatusBarBreadcrumbItemView")!) {}方法 说明是从其他运用跳转过来的,需要用一个属性保存这个child */ for child in children! { if child.isKindOfClass(NSClassFromString("UIStatusBarServiceItemView")!) { } if child.isKindOfClass(NSClassFromString("UIStatusBarServiceItemView")!) { } if child.isKindOfClass(NSClassFromString("UIStatusBarBreadcrumbItemView")!) { // 来到这里,是从其他运用跳转过来的 // 用一个属性保存这个child } }
Tip:以上所有的代码都在func applicationDidBecomeActive(application: UIApplication) {}执行,可以参考AppDelegate完整代码示例:
二、事件传递
我们需要对点击进行事件传递,寻找最合适的view去处理这个事件第一步:我们需要获取手指点击的点,如何获取呢?
这需要自定义一个UIButton,对touchesBegan(touches: Set, withEvent event: UIEvent?) {}进行重写,通过UITouch获取触摸的点,需要用一个touchP属性记录当前触摸点
// 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(NSClassF romString("UIStatusBarBreadcrumbItemView")!)**记录的child,action就是下图的”userDidActivateButton:”
@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 { // 如果触摸点在系统的返回运用按钮上面 // 1.包装Selector之前获取到的“userDidActivateButton:”方法 let sel = NSSelectorFromString("userDidActivateButton:") // 2.找到系统的返回运用按钮target,由之前打印可知就是之前记录的child MGScrollTopWindow.shareInstance.backStatusView!.performSelector(sel) }else { // 触摸点不在系统的返回运用按钮上面 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() // 点击状态栏滚动到顶部 dispatch_after(1, dispatch_get_main_queue()) { () -> Void in MGScrollTopWindow.shareInstance.show() } } func applicationDidBecomeActive(application: UIApplication) { var statusBarView: UIView? // 记录状态栏 var isYunYong = false // 记录是否从其他运用跳转 var tempView: UIView? // 临时记录从其他运用跳转系统左上角返回其他运用的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 { // 其他运用跳转过来,记得赋值 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{ // 单例:因为这个东西从程序运行一直要有,所以设计成单例 static let shareInstance: MGScrollTopWindow = MGScrollTopWindow() // 记录由一个运用跳转到另一个运用左上角那个系统返回的view,是可选类型,可有可无 var backStatusView: UIView? // 状态栏要添加的按钮,可设计成“单例”或是“属性” 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: - 获取系统的状态栏的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 /* 控制要不要点击状态栏回到顶部这个功能 */ 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滚动到最前面(包括内边距) 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转到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滚动到最前面(包括内边距) scrollView.scrollRectToVisible(CGRectMake(scrollView.frame.origin.x, 0, 1, 1), animated:true) } */ } }
如果觉得不错请到github点赞,感激不尽。转载请注明出处
项目原码MGDYZB
相关文章推荐
- android如何实现类似ios点击状态栏回到顶部功能
- iOS实现点击状态栏自动回到顶部效果详解
- iOS中点击状态栏让滑动视图回到顶部
- iOS 点击状态栏回到顶部
- iOS点击状态栏回到顶部(一个控制器中包含多个scrollview,系统自带的回到顶部失效)
- Android实现ios点击状态栏回到顶部效果(直接转的网址)
- iOS 9.0之后调用支付宝或者微信支付,点击右上角返回按钮会当前APP后订单状态的判断
- iOS 点击状态栏回滚scrollView顶部
- 解决“多个UITableView的时候不能点击状态栏回到顶部”问题
- iOS点击屏幕使tableview、scrollview回到顶部
- 解决“同个UIView里面有多个UITableView的时候不能点击状态栏回到顶部”问题
- 点击状态栏回到顶部的功能失效的解决办法
- ios 点击返回顶部效果的实现,类似单击状态栏效果
- 重复点击主界面(TabBar)按钮刷新界面--点击状态栏回到顶部
- 关于tableView点击状态栏列表回到顶部的说明
- iOS点击回到顶部
- 关于点击状态栏回到顶部的问题
- ios设置点击状态栏返回到顶部
- 点击状态栏回到顶部
- iOS不得姐项目--TabBar的重复点击实现当前模块刷新;状态栏点击实现当前模块回滚到最顶部