@@ -12,12 +12,13 @@ import UIKit
1212extension UIApplication {
1313 @objc final public class func configureLinearNetworkActivityIndicatorIfNeeded( ) {
1414#if !targetEnvironment(macCatalyst) && (swift(<5.9) || !os(visionOS))
15- if #available( iOS 11 . 0 , * ) {
15+ if UIDevice . current. userInterfaceIdiom != . pad {
16+ if #available( iOS 18 . 0 , * ) {
17+ configureLinearNetworkActivityIndicator ( )
18+ }
1619 // detect notch
1720 if let window = shared. windows. first, window. safeAreaInsets. bottom > 0.0 {
18- if UIDevice . current. userInterfaceIdiom != . pad {
19- configureLinearNetworkActivityIndicator ( )
20- }
21+ configureLinearNetworkActivityIndicator ( )
2122 }
2223 }
2324 #endif
@@ -26,26 +27,26 @@ extension UIApplication {
2627#if !targetEnvironment(macCatalyst) && (swift(<5.9) || !os(visionOS))
2728 class func configureLinearNetworkActivityIndicator( ) {
2829 DispatchQueue . once {
29- let originalSelector = #selector( setter: UIApplication . isNetworkActivityIndicatorVisible)
30- let swizzledSelector = #selector( ft_setNetworkActivityIndicatorVisible ( visible: ) )
31- let originalMethod = class_getInstanceMethod ( self , originalSelector)
32- let swizzledMethod = class_getInstanceMethod ( self , swizzledSelector)
33- method_exchangeImplementations ( originalMethod!, swizzledMethod!)
30+ swizzle ( original: #selector( setter: UIApplication . isNetworkActivityIndicatorVisible) ,
31+ swizzled: #selector( setter: ft_networkActivityIndicatorVisible) )
32+ swizzle ( original: #selector( getter: UIApplication . isNetworkActivityIndicatorVisible) ,
33+ swizzled: #selector( getter: ft_networkActivityIndicatorVisible) )
3434 }
3535 UIViewController . configureLinearNetworkActivityIndicator ( )
3636 }
3737
3838 private struct AssociatedKeys {
3939 static var indicatorWindowKey : UInt8 = 0
40+ static var indicatorVisibleKey : UInt8 = 0
4041 }
4142
4243 var indicatorWindow : UIWindow ? {
4344 get {
44- return objc_getAssociatedObject ( self , & AssociatedKeys. indicatorWindowKey) as? UIWindow
45+ objc_getAssociatedObject ( self , & AssociatedKeys. indicatorWindowKey) as? UIWindow
4546 }
4647
4748 set {
48- if let newValue = newValue {
49+ if let newValue {
4950 objc_setAssociatedObject (
5051 self ,
5152 & AssociatedKeys. indicatorWindowKey,
@@ -57,86 +58,107 @@ extension UIApplication {
5758 }
5859
5960
60- @objc func ft_setNetworkActivityIndicatorVisible( visible: Bool ) {
61- self . ft_setNetworkActivityIndicatorVisible ( visible: visible) // original implementation
62-
63- if visible {
64- if indicatorWindow == nil {
65- let frame = CGRect ( x: 0 , y: 0 , width: UIScreen . main. bounds. width, height: 12 )
66- indicatorWindow = UIWindow ( frame: frame)
67- indicatorWindow? . windowLevel = UIWindow . Level. statusBar + 1
68- indicatorWindow? . isUserInteractionEnabled = false
69-
70- // notched iPhones differ in corner radius and right notch width
71- // => lookup margin from right window edge, and width
72- let layout : [ ModelName : ( CGFloat , CGFloat ) ] = [
73- . iPhoneX1: ( 74 , 44 ) ,
74- . iPhoneX2: ( 74 , 44 ) ,
75- . iPhoneXs: ( 74 , 44 ) ,
76- . iPhoneXsMax1: ( 74 , 44 ) ,
77- . iPhoneXsMax2: ( 74 , 44 ) ,
78- . iPhoneXR: ( 70 , 40 ) ,
79- . iPhone11: ( 70 , 40 ) ,
80- . iPhone11Pro: ( 60 , 34 ) ,
81- . iPhone11ProMax: ( 74 , 44 ) ,
82- . iPhone12Mini: ( 60 , 30 ) ,
83- . iPhone12: ( 72 , 34 ) ,
84- . iPhone12Pro: ( 72 , 34 ) ,
85- . iPhone12ProMax: ( 80 , 42 ) ,
86- . iPhone13Mini: ( 60 , 30 ) ,
87- . iPhone13: ( 72 , 34 ) ,
88- . iPhone13Pro: ( 72 , 34 ) ,
89- . iPhone13ProMax: ( 80 , 42 ) ,
90- . iPhone14: ( 72 , 34 ) ,
91- . iPhone14Plus: ( 80 , 42 ) ,
92- . iPhone14Pro: ( 72 , 34 ) ,
93- . iPhone14ProMax: ( 80 , 42 ) ,
94- . iPhone15: ( 72 , 34 ) ,
95- . iPhone15Plus: ( 80 , 42 ) ,
96- . iPhone15Pro: ( 72 , 34 ) ,
97- . iPhone15ProMax: ( 80 , 42 ) ,
98- . iPhone16: ( 84 , 36 ) ,
99- . iPhone16Plus: ( 96 , 42 ) ,
100- . iPhone16Pro: ( 86 , 38 ) ,
101- . iPhone16ProMax: ( 96 , 42 ) ,
102- ]
103- let modelName = UIDevice . current. ftModelName
104- let config = modelName. flatMap { layout [ $0] } ?? ( 74 , 44 )
105-
106- let x = indicatorWindow!. frame. width - config. 0
107- let width = config. 1
108-
109- let indicator = FTLinearActivityIndicator ( frame: CGRect ( x: x, y: 7 , width: width, height: 4.5 ) )
110- indicator. isUserInteractionEnabled = false
111- indicator. hidesWhenStopped = false
112- indicator. startAnimating ( )
113- indicatorWindow? . addSubview ( indicator)
114- }
61+ @objc var ft_networkActivityIndicatorVisible : Bool {
62+ get {
63+ objc_getAssociatedObject ( self , & AssociatedKeys. indicatorVisibleKey) as? Bool ?? false
11564 }
116- guard let indicator = indicatorWindow? . subviews. first as? FTLinearActivityIndicator else { return }
117- if #available( iOS 13 . 0 , * ) {
118- indicator. tintColor = indicatorWindow? . windowScene? . statusBarManager? . statusBarStyle == . lightContent ? . white : . black
119- } else {
120- indicator. tintColor = statusBarStyle == . default ? UIColor . black : UIColor . white
121- }
122- if visible {
123- indicatorWindow? . isHidden = self . isStatusBarHidden
124- indicator. isHidden = false
125- indicator. alpha = 1
126- } else {
127- UIView . animate ( withDuration: 0.5 , animations: {
128- indicator. alpha = 0
129- } ) { ( finished) in
130- if ( finished) {
131- indicator. isHidden = !self . isNetworkActivityIndicatorVisible // might have changed in the meantime
132- self . indicatorWindow? . isHidden = !self . isNetworkActivityIndicatorVisible || self . isStatusBarHidden
65+ set {
66+ self . ft_networkActivityIndicatorVisible = newValue // original implementation
67+ objc_setAssociatedObject ( self , & AssociatedKeys. indicatorVisibleKey, newValue, objc_AssociationPolicy. OBJC_ASSOCIATION_ASSIGN)
68+
69+ if newValue {
70+ if indicatorWindow == nil {
71+ let frame = CGRect ( x: 0 , y: 0 , width: UIScreen . main. bounds. width, height: 12 )
72+ indicatorWindow = UIWindow ( frame: frame)
73+ indicatorWindow? . windowLevel = UIWindow . Level. statusBar + 1
74+ indicatorWindow? . isUserInteractionEnabled = false
75+ if #available( iOS 17 . 0 , * ) {
76+ indicatorWindow? . registerForTraitChanges ( [ UITraitUserInterfaceStyle . self] , handler: { ( newTC: UITraitEnvironment , previousTraitCollection: UITraitCollection ) in
77+ self . ftUpdateNetworkActivityIndicatorAppearance ( )
78+ } )
79+ }
80+ // notched iPhones differ in corner radius and right notch width
81+ // => lookup margin from right window edge, and width
82+ let layout : [ ModelName : ( CGFloat , CGFloat ) ] = [
83+ . iPhoneX1: ( 74 , 44 ) ,
84+ . iPhoneX2: ( 74 , 44 ) ,
85+ . iPhoneXs: ( 74 , 44 ) ,
86+ . iPhoneXsMax1: ( 74 , 44 ) ,
87+ . iPhoneXsMax2: ( 74 , 44 ) ,
88+ . iPhoneXR: ( 70 , 40 ) ,
89+ . iPhone11: ( 70 , 40 ) ,
90+ . iPhone11Pro: ( 60 , 34 ) ,
91+ . iPhone11ProMax: ( 74 , 44 ) ,
92+ . iPhone12Mini: ( 60 , 30 ) ,
93+ . iPhone12: ( 72 , 34 ) ,
94+ . iPhone12Pro: ( 72 , 34 ) ,
95+ . iPhone12ProMax: ( 80 , 42 ) ,
96+ . iPhone13Mini: ( 60 , 30 ) ,
97+ . iPhone13: ( 72 , 34 ) ,
98+ . iPhone13Pro: ( 72 , 34 ) ,
99+ . iPhone13ProMax: ( 80 , 42 ) ,
100+ . iPhone14: ( 72 , 34 ) ,
101+ . iPhone14Plus: ( 80 , 42 ) ,
102+ . iPhone14Pro: ( 72 , 34 ) ,
103+ . iPhone14ProMax: ( 80 , 42 ) ,
104+ . iPhone15: ( 72 , 34 ) ,
105+ . iPhone15Plus: ( 80 , 42 ) ,
106+ . iPhone15Pro: ( 72 , 34 ) ,
107+ . iPhone15ProMax: ( 80 , 42 ) ,
108+ . iPhone16: ( 84 , 36 ) ,
109+ . iPhone16Plus: ( 96 , 42 ) ,
110+ . iPhone16Pro: ( 86 , 38 ) ,
111+ . iPhone16ProMax: ( 96 , 42 ) ,
112+ . iPhoneSE2: ( 177 , 40 ) ,
113+ . iPhoneSE3: ( 177 , 40 ) ,
114+ ]
115+ let modelName = UIDevice . current. ftModelName
116+ let config = modelName. flatMap { layout [ $0] } ?? ( 74 , 44 )
117+
118+ let x = indicatorWindow!. frame. width - config. 0
119+ let width = config. 1
120+
121+ let indicator = FTLinearActivityIndicator ( frame: CGRect ( x: x, y: 7 , width: width, height: 4.5 ) )
122+ if [ . iPhoneSE2, . iPhoneSE3] . contains ( modelName) {
123+ indicator. frame = CGRect ( x: 172 , y: 1.5 , width: 31 , height: 3 )
124+ }
125+ indicator. isUserInteractionEnabled = false
126+ indicator. hidesWhenStopped = false
127+ indicator. startAnimating ( )
128+ indicatorWindow? . addSubview ( indicator)
129+ }
130+ }
131+ guard let indicator = indicatorWindow? . subviews. first as? FTLinearActivityIndicator else { return }
132+ if #available( iOS 13 . 0 , * ) {
133+ let style = indicatorWindow? . windowScene? . statusBarManager? . statusBarStyle ?? . default
134+ indicator. tintColor = switch style {
135+ case . lightContent: . white
136+ case . darkContent: . black
137+ default : indicatorWindow? . traitCollection. userInterfaceStyle == . dark ? . white : . black
138+ }
139+ } else {
140+ indicator. tintColor = statusBarStyle == . default ? UIColor . black : UIColor . white
141+ }
142+ if newValue {
143+ indicatorWindow? . isHidden = self . isStatusBarHidden
144+ indicator. isHidden = false
145+ indicator. alpha = 1
146+ } else {
147+ UIView . animate ( withDuration: 0.5 , animations: {
148+ indicator. alpha = 0
149+ } ) { ( finished) in
150+ if ( finished) {
151+ indicator. isHidden = !self . isNetworkActivityIndicatorVisible // might have changed in the meantime
152+ self . indicatorWindow? . isHidden = !self . isNetworkActivityIndicatorVisible || self . isStatusBarHidden
153+ }
133154 }
134155 }
135156 }
136157 }
137158
138159 func ftUpdateNetworkActivityIndicatorAppearance( ) {
139160 self . indicatorWindow? . isHidden = !self . isNetworkActivityIndicatorVisible || self . isStatusBarHidden
161+ isNetworkActivityIndicatorVisible = isNetworkActivityIndicatorVisible
140162 }
141163 #endif
142164}
@@ -146,11 +168,8 @@ extension UIApplication {
146168extension UIViewController {
147169 @objc final public class func configureLinearNetworkActivityIndicator( ) {
148170 DispatchQueue . once {
149- let originalSelector = #selector( setNeedsStatusBarAppearanceUpdate)
150- let swizzledSelector = #selector( ftSetNeedsStatusBarAppearanceUpdate)
151- let originalMethod = class_getInstanceMethod ( self , originalSelector)
152- let swizzledMethod = class_getInstanceMethod ( self , swizzledSelector)
153- method_exchangeImplementations ( originalMethod!, swizzledMethod!)
171+ swizzle ( original: #selector( setNeedsStatusBarAppearanceUpdate) ,
172+ swizzled: #selector( ftSetNeedsStatusBarAppearanceUpdate) )
154173 }
155174 }
156175
@@ -190,4 +209,13 @@ extension DispatchQueue {
190209 block ( )
191210 }
192211}
212+
213+ @available ( iOSApplicationExtension, unavailable)
214+ extension NSObject {
215+ fileprivate class func swizzle( original: Selector , swizzled: Selector ) {
216+ let originalMethod = class_getInstanceMethod ( self as AnyClass , original)
217+ let swizzledMethod = class_getInstanceMethod ( self as AnyClass , swizzled)
218+ method_exchangeImplementations ( originalMethod!, swizzledMethod!)
219+ }
220+ }
193221#endif
0 commit comments