Apple?Watch?App?Lifecycle應(yīng)用開發(fā)
Watch App Lifecycle
watchOS App 的生命周期比 iOS App 的生命周期要更復(fù)雜一些。watchOS App 可能會(huì)處于以下五種狀態(tài):
- Not running - 未運(yùn)行
- Inactive - 不活躍
- Active - 活躍
- Background - 后臺(tái)
- Suspended - 掛起
常見的狀態(tài)轉(zhuǎn)換
watchOS App 的五種狀態(tài)由下圖中的紫色框表示:
作為開發(fā)者,我們只會(huì)與其中三個(gè)狀態(tài)進(jìn)行交互:Inactive、Active、Background。 而在 Not running 和 Suspended 狀態(tài)下 App 未運(yùn)行。
啟動(dòng) App 到 Active 狀態(tài)
如果用戶尚未運(yùn)行 App 或系統(tǒng)從內(nèi)存中清除了 App,則 App 以 Not running 狀態(tài)開始。App 啟動(dòng)后,它會(huì)從 Not running 轉(zhuǎn)換到 Inactive 狀態(tài)。
處于 Inactive 狀態(tài)時(shí),App 仍然在前臺(tái)運(yùn)行,但不會(huì)響應(yīng)用戶的任何操作。但是 App 可能仍在執(zhí)行某些代碼。在此狀態(tài)下,App 并不是無法運(yùn)行。
App 幾乎立即將轉(zhuǎn)換到 Active 狀態(tài),這是在 Apple Watch 屏幕上運(yùn)行的 App 的正常模式。當(dāng)處于 Active 狀態(tài)時(shí),App 可以接收來自 Apple Watch 上的物理控件和用戶手勢(shì)的操作。
當(dāng)用戶啟動(dòng)我們的 App 但尚未運(yùn)行時(shí),watchOS 將執(zhí)行以下操作:
- 調(diào)用 applicationDidFinishLaunching() 并將 scenePhase 設(shè)置為 .inactive;
- 創(chuàng)建 App 的初始 scene 和 rootView;
- 調(diào)用 applicationWillEnterForeground();
- 調(diào)用 applicationDidBecomeActive() 并將 scenePhase 設(shè)置為 .active;
- App 將出現(xiàn)在屏幕上,watchOS 將調(diào)用 rootView 的 onAppear(perform:)。
注意:從 watchOS 7 及更高版本開始,在使用 SwiftUI 時(shí),scenePhase 環(huán)境變量的更新,要早于 WKExtensionDelegate 方法的調(diào)用。
App 到 Inactive 狀態(tài)
一旦用戶放下手臂,watchOS 將變?yōu)?Inactive 狀態(tài)。如前所述,該 App 仍在運(yùn)行并執(zhí)行我們的代碼。Inactive 狀態(tài)需要減少 App 對(duì) Apple Watch 電池的影響,我們應(yīng)暫?;蛉∠魏尾恍枰碾姵孛芗筒僮?。例如,我們可以禁止當(dāng)前正在運(yùn)行的動(dòng)畫的展示。
我們還需要考慮是否需要保存一些數(shù)據(jù),保存 Core Data Stack?向 UserDefaults 寫入內(nèi)容?其實(shí),一旦用戶再次抬起手臂,App 就會(huì)再次激活。因此,此時(shí)并不需要保存或保存太多內(nèi)容,否則可能會(huì)對(duì)電量產(chǎn)生較大壓力。
當(dāng)我們的 App 轉(zhuǎn)換到 Inactive 狀態(tài)時(shí),watchOS 會(huì)將 scenePhase 設(shè)置為 .inactive,然后調(diào)用 WKExtensionDelegate 的 applicationWillResignActive() 方法。
App 到 Background 狀態(tài)
在轉(zhuǎn)換到 Inactive 狀態(tài)兩分鐘后,或者當(dāng)用戶切換到另一個(gè) App 時(shí),我們的 App 將轉(zhuǎn)換到 Background 狀態(tài)。通過系統(tǒng)也可以直接將 App 啟動(dòng)到 Background 狀態(tài),如后 Background session 和 Background task。
在 Suspended 狀態(tài)之前,操作系統(tǒng)會(huì)為 Background 狀態(tài)的 App 提供一小段不確定的時(shí)間。如果我們的 App 從 Inactive 狀態(tài)轉(zhuǎn)換到 Background 狀態(tài),我們需要快速執(zhí)行必要的操作來處理 App。
我們可以使用 SwiftUI ScenePhase 或 WKExtensionDelegate 的 applicationDidEnterBackground 來確定我們的 App 何時(shí)到 Background 狀態(tài)。當(dāng)從 Inactive 狀態(tài)轉(zhuǎn)換到 Background 狀態(tài)時(shí),watchOS 會(huì)將scenePhase 設(shè)置為 .background,然后調(diào)用applicationDidEnterBackground()。如果 App 需要太多資源,watchOS 將暫停該 App。
返回表盤
在 watchOS 7 之前,我們可以在 App 轉(zhuǎn)換到 Background 后請(qǐng)求 8 分鐘。在 watchOS 7 及后續(xù)版本中,用戶可以通過 Apple Watch 上的 設(shè)置 ? 通用 ? 返回表盤 來進(jìn)行超時(shí)配置。用戶可以選擇三個(gè)選項(xiàng):“始終”、“2 分鐘后”或“1 小時(shí)后”。默認(rèn)情況下,所選設(shè)置會(huì)應(yīng)用到所有 App,但用戶也可以為每個(gè) App 選取自定時(shí)間。默認(rèn)值為兩分鐘。 根據(jù)功能需要,我們可能希望告訴用戶如何將 App 的設(shè)置更改為一小時(shí)。
額外的 Background 執(zhí)行時(shí)間
如果在轉(zhuǎn)換到 Background 時(shí),App 需要執(zhí)行的工作量比 watchOS 為我們的App 提供時(shí)間要長(zhǎng),那么我們需要重新考慮 App 進(jìn)行的操作,例如刪除網(wǎng)絡(luò)調(diào)用。如果我們已經(jīng)進(jìn)行了所有可以進(jìn)行的優(yōu)化,但仍然需要更多的處理時(shí)間,我們可以調(diào)用 ProcessInfo 類的 performExpiringActivity(withReason:using:) 方法。如果在 App 處于前臺(tái)時(shí)調(diào)用,將獲得 30 秒。如果在后臺(tái)調(diào)用,將獲得 10 秒。
系統(tǒng)將異步嘗試執(zhí)行我們提供給 using 參數(shù)的代碼塊,它將返回一個(gè)布爾值,讓我們知道 App 是否即將暫停。如果我們收到 false 值,那么我們可以繼續(xù),并盡快執(zhí)行我們的操作。如果我們收到一個(gè) true 值,系統(tǒng)不會(huì)給我們額外的時(shí)間,App 需要立即停止。
請(qǐng)注意,僅僅是系統(tǒng)允許我們開始額外的工作,并不意味著它會(huì)給我們足夠的時(shí)間來完成它。如果我們的代碼塊仍在運(yùn)行,并且操作系統(tǒng)需要暫停我們的 App ,那么我們的代碼塊將被使用 true 參數(shù)再次調(diào)用。我們的代碼應(yīng)能夠處理此取消請(qǐng)求。
例如,我們可以在每個(gè)操作之前檢查 watchOS 是否告訴我們停止工作。假設(shè)我們有一個(gè)布爾實(shí)例屬性 cancel,我們會(huì)執(zhí)行以下操作:
processInfo.performExpiringActivity( withReason: "求求你" ) { suspending in guard !suspending else { cancel = true return } guard !cancel else { return } try? managedObjectContext.save() guard !cancel else { return } userDefaults.set(someData(), forKey: "criticalData") }
在代碼中:
立即檢查我們是否被允許運(yùn)行 如果系統(tǒng)告訴你暫停,那么我們將取消屬性設(shè)置為 true。
在嘗試保存我們的 CoreDataModel 之前,請(qǐng)確保未設(shè)置 cancel。另一個(gè)線程可能已經(jīng)調(diào)用了相同的方法并請(qǐng)求掛起。
在保存到 UserDefaults 之前,請(qǐng)檢查操作系統(tǒng)是否告訴我們停止。
每次檢查取消可能看起來有點(diǎn)奇怪,但這樣做可以確保我們遵守操作系統(tǒng)的指示。 在示例中,我們只需在被告知時(shí)停止操作,而時(shí)間情況下,我們可能需要快速執(zhí)行其他操作來標(biāo)記我們無法完成的操作。
App 到 Active 狀態(tài)
如果用戶在 App 處于 Background 狀態(tài)時(shí)與其交互,watchOS 將通過以下過程將其轉(zhuǎn)換回 Active 狀態(tài):
- 以 .background 狀態(tài)重新啟動(dòng)應(yīng)用程序;
- 調(diào)用 applicationWillEnterForeground();
- 將 scenePhase 設(shè)置為 .active;
- 調(diào)用 applicationDidBecomeActive()。
我們可能會(huì)對(duì)用戶在后臺(tái)狀態(tài)下如何與應(yīng)用交互感到困惑。這是用戶使用了 App 提供的復(fù)雜功能。
App 到 Suspended 狀態(tài)
當(dāng)我們的 App 最終轉(zhuǎn)換到 Suspended 狀態(tài)時(shí),所有代碼執(zhí)行都會(huì)停止。 App 仍在內(nèi)存中,但不會(huì)處理事件。
當(dāng)我們的 App 處于 Background 狀態(tài)并且沒有任何待處理的任務(wù)要完成時(shí),系統(tǒng)會(huì)將我們的 App 轉(zhuǎn)換為 Suspended 狀態(tài)。
一旦我們的 App 進(jìn)入 Suspended 狀態(tài),它就有資格被清除。 如果操作系統(tǒng)需要更多內(nèi)存,它可能會(huì)在不通知的情況下從內(nèi)存中清除任何處于 Suspended 狀態(tài)的 App。
系統(tǒng)將盡最大努力不清除最近執(zhí)行的 App、Dock 中的任何 App 以及在當(dāng)前表盤上有復(fù)雜功能的任何 App。 如果系統(tǒng)必須清除上述 App 之一,它將在內(nèi)存可用時(shí)重新啟動(dòng)該 App。
始終顯示 Always on
在 watchOS 6 之前,當(dāng)用戶最近沒有與之交互時(shí),Apple Watch 會(huì)息屏。 Always On 改變了這一點(diǎn),使手表繼續(xù)顯示時(shí)間。但是,watchOS 會(huì)模糊當(dāng)前運(yùn)行的 App,并在顯示屏上顯示時(shí)間。
而現(xiàn)在,在默認(rèn)情況下,會(huì)顯示我們的 App 的用戶界面,而不是時(shí)間。只要它是最前面的 App 或運(yùn)行 Background session,watchOS 就不會(huì)模糊它。處于 Always On 時(shí),手表屏幕會(huì)變暗,并且 UI 更新速度會(huì)變慢,從而延長(zhǎng)電池使用時(shí)長(zhǎng)。
如果用戶與我們的 App 交互,系統(tǒng)將返回其 Active 狀態(tài)。 Always On 的一個(gè)顯著優(yōu)勢(shì)與日期和時(shí)間有關(guān)。如果 App 顯示計(jì)時(shí)器或相對(duì)日期等,則 UI 將繼續(xù)更新為正確的值。
如果你希望為我們的 App 禁用 Always On,只需在 Info.plist 中將 WKSupportsAlwaysOnDisplay 鍵設(shè)置為 false。用戶還可以通過“設(shè)置”?“顯示和亮度”?“始終顯示”來為某些 App 或整個(gè)設(shè)備禁用 Always on。
狀態(tài)變化示例
創(chuàng)建一個(gè)新項(xiàng)目:
新增 ExtensionDelegate.swift 文件:
import WatchKit final class ExtensionDelegate: NSObject, WKExtensionDelegate { func applicationDidFinishLaunching() { print( #function) } func applicationWillEnterForeground() { print( #function) } func applicationDidBecomeActive() { print( #function) } func applicationWillResignActive() { print( #function) } func applicationDidEnterBackground() { print( #function) } }
修改 LifecycleApp.swift 代碼:
import SwiftUI @main struct Lifecycle_Watch_AppApp: App { @Environment(.scenePhase) private var scenePhase @WKExtensionDelegateAdaptor(ExtensionDelegate.self) private var extensionDelegate var body: some Scene { WindowGroup { ContentView() } .onChange(of: scenePhase) { print("onChange: ($0)") } } }
我們可以在物理設(shè)備上運(yùn)行該項(xiàng)目以觀察狀態(tài)變化。當(dāng)我們抬起和放下手腕時(shí),會(huì)看到狀態(tài)在 Active 和 Inactive 之間變化。如果我們讓應(yīng)用程序處于 Inactive 狀態(tài)兩分鐘,它會(huì)切換到 Background 模式。
WKExtendedRuntimeSession
有四種特定的類型,可以讓我們的 App 保持運(yùn)行,甚至在后臺(tái)運(yùn)行。
Self care
專注于用戶情緒健康或健康的 App 將在前臺(tái)運(yùn)行,即使在手表屏幕未打開。 watchOS 將為 App 序提供 10 分鐘的 Session,該 Session 將持續(xù)到用戶切換到另一個(gè) App 或 App 使 Session 無效。
Mindfulness
冥想已越來越成為一種流行的,Mindfulness - 正念 App 將保持在前臺(tái)。不過,這是一個(gè)耗時(shí)的過程,所以 watchOS 會(huì)給 App 一個(gè) 1 小時(shí)的 Session。
Physical therapy
伸展等鍛煉非常適合物理治療課程。 與最后兩種 Session 類型不同,物理治療 Session 在后臺(tái)運(yùn)行。 后臺(tái) Session 將一直運(yùn)行,直到到時(shí)間限制或 App 使 Session 無效,即使用戶啟動(dòng)另一個(gè) App 也是如此。物理治療課程可長(zhǎng)達(dá) 1 小時(shí)。
Smart alarm
當(dāng)我們需要安排時(shí)間檢查用戶的心率和運(yùn)動(dòng)時(shí),智能提醒是一個(gè)不錯(cuò)的選擇。 App 將獲得一個(gè) 30 分鐘的 Sesion。
與其他三種會(huì)話類型不同,我們必須安排智提醒鐘在未來某一個(gè)時(shí)刻開始。 我們需要在接下來的 36 小時(shí)內(nèi)啟動(dòng)會(huì)話,并在我們的 App 處于 WKApplicationState.active 狀態(tài)時(shí)安排它。我們的Aoo 能會(huì)暫停或終止,但 Sesion 將繼續(xù)。
當(dāng)需要處理 Session 時(shí),watchOS 將調(diào)用 App 的 WKExtensionDelegate 的 handle(_:)。
注意:我們必須在 App 退出之前設(shè)置會(huì)話的委托,否則 Session 將終止。
一旦 Session 運(yùn)行,我們必須通過調(diào)用會(huì)話的 notifyUser(hapticType:repeatHandler:) 來觸發(fā)提醒。 如果我們忘記了,watchOS 將顯示警告并提議禁用 Session。
刷牙提醒 Demo - Dentisit
搭建項(xiàng)目框架
我們將實(shí)現(xiàn)一個(gè)刷牙時(shí),提醒用戶刷牙時(shí)間的 App Dentisit,首先創(chuàng)建項(xiàng)目:
修改 ContentView.swift,這樣能讓我們更好的看到 App 的狀態(tài):
struct ContentView: View { @Environment(.scenePhase) private var scenePhase var body: some View { Text("Hello, World!") .onChange(of: scenePhase) { print($0) } } }
新增 GetReadyView.swift,它將實(shí)現(xiàn)一個(gè)準(zhǔn)備視圖:
import SwiftUI struct GetReadyView: View { private let color: Color // 色環(huán)顏色 @State private var stage: Int // 倒計(jì)時(shí)秒數(shù),屏幕上展示的值 private let onComplete: (() -> Void)? // 倒計(jì)時(shí)完成后回調(diào) private let denominator: Double // 倒計(jì)時(shí)秒數(shù),保存總值,用來計(jì)算色環(huán) @State private var trim = 1.0 // 色環(huán)顯示比例 @State private var text = "Ready" // 色環(huán)中心文案 private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() init(color: Color = .green, stages: Int = 4, onComplete: (() -> Void)? = nil) { self.color = color self.onComplete = onComplete _stage = State(initialValue: stages) denominator = Double(stages) } var body: some View { ZStack { Color.black.ignoresSafeArea() // 背景色環(huán) Circle() .stroke(style: StrokeStyle(lineWidth: 10, lineCap: .round)) .foregroundColor(color.opacity(0.5)) // 色環(huán) Circle() .trim(from: 0, to: trim) .stroke(style: StrokeStyle(lineWidth: 10, lineCap: .round)) .foregroundColor(color) .rotationEffect(.degrees(-90)) .animation(.linear, value: trim) // 文案 Text(text) .font(.title) } .onReceive(timer) { _ in tick() } .background(.black) } private func tick() { stage -= 1 // 更新當(dāng)前時(shí)間 self.text = "(self.stage)" trim = Double(stage) / denominator // 更新色環(huán) guard stage > 0 else { timer.upstream.connect().cancel() WKInterfaceDevice.current().play(.success) // 播放音效 if let onComplete = onComplete { onComplete() } return } WKInterfaceDevice.current().play(.start) // 播放音效 } } struct GetReadyView_Previews: PreviewProvider { static var previews: some View { GetReadyView() } }
具體代碼內(nèi)容,已經(jīng)添加注釋以輔助閱讀,可以在 ContentView 中添加 GetReadyView() 來查看效果:
struct ContentView: View { @Environment(.scenePhase) private var scenePhase var body: some View { // Text("Hello, World!") GetReadyView() .onChange(of: scenePhase) { print($0) } } }
最終效果如下:
選擇 Session 類型
刷牙屬于 Self care 類型,按照下圖中的步驟添加新功能。 首先,從 Project Navigator 菜單中選擇 Dentisit。 然后選擇 Dentisit Watch App,選擇 Signing & Capabilities,然后按 + Capability 選項(xiàng)。 出現(xiàn)提示時(shí),從功能列表中選擇 Background Modes,并修改 Session Type:
添加 ContentModel
創(chuàng)建一個(gè)名為 ContentModel.swift 的新文件:
import SwiftUI final class ContentModel: NSObject, ObservableObject { @Published var roundsLeft = 0 @Published var endOfRound: Date? @Published var endOfBrushing: Date? private var timer: Timer! private var session: WKExtendedRuntimeSession! }
在代碼中,ContentModel 需要符合 ObservableObject 以便模型可以更新 ContentView。我們還需要繼承 NSObject,這是 WKExtendedRuntimeSessionDelegate 的要求。
前三個(gè)屬性用 @Published 包裝,我們將使用它們來跟蹤用戶還需要刷幾輪、刷多久。
最后,我們需要一種方法來知道時(shí)間到了,并控制會(huì)話。
用戶開始刷牙后,我們需要?jiǎng)?chuàng)建會(huì)話并更新表盤按鈕上顯示的文本。將此添加到 ContentModel:
func startBrushing() { session = WKExtendedRuntimeSession() session.delegate = self session.start() }
將以下代碼添加到文件末尾實(shí)現(xiàn) WKExtendedRuntimeSessionDelegate:
extension ContentModel: WKExtendedRuntimeSessionDelegate { func extendedRuntimeSessionDidStart( _ extendedRuntimeSession: WKExtendedRuntimeSession ) { } func extendedRuntimeSessionWillExpire( _ extendedRuntimeSession: WKExtendedRuntimeSession ) { } func extendedRuntimeSession( _ extendedRuntimeSession: WKExtendedRuntimeSession, didInvalidateWith reason: WKExtendedRuntimeSessionInvalidationReason, error: Error? ) { } }
遵守協(xié)議非常簡(jiǎn)單:
- 一旦 Session 開始運(yùn)行,系統(tǒng)就會(huì)調(diào)用 extendedRuntimeSessionDidStart(_:)。
- 如果 App 即將超過 Session的時(shí)間限制,watchOS 將在強(qiáng)制使會(huì)話過期之前調(diào)用 extendedRuntimeSessionWillExpire(_:)。
- 無論出于何種原因,當(dāng)會(huì)話完成時(shí),watchOS 都會(huì)調(diào)用 extendedRuntimeSession(_:didInvalidateWith:error:)。
繼續(xù)在 在extendedRuntimeSessionDidStart(_:) 中添加:
let secondsPerRound = 30.0 let now = Date.now roundsLeft = 4 endOfRound = now.addingTimeInterval(secondsPerRound) endOfBrushing = now.addingTimeInterval(secondsPerRound * 4) let device = WKInterfaceDevice.current() device.play(.start)
我們不關(guān)心實(shí)際的日期或時(shí)間:我們只需要特定的秒數(shù)。當(dāng) Session 開始時(shí),讓手表快速振動(dòng)是很好的用戶體驗(yàn)。
現(xiàn)在我們知道每輪刷牙需要多長(zhǎng)時(shí)間,繼續(xù)設(shè)置一個(gè)計(jì)時(shí)器。 添加以下代碼以完成該方法:
timer = Timer( fire: endOfRound!, interval: secondsPerRound, repeats: true ) { _ in self.roundsLeft -= 1 guard self.roundsLeft == 0 else { self.endOfRound = Date.now.addingTimeInterval(secondsPerRound) device.play(.success) return } extendedRuntimeSession.invalidate() device.play(.success) device.play(.success) } RunLoop.main.add(timer, forMode: .common)
我們生成一個(gè)計(jì)時(shí)器,該計(jì)時(shí)器在當(dāng)前刷牙 Round 結(jié)束時(shí)開始,并每隔 secondsPerRound 秒重復(fù)一次。如果仍有幾輪要執(zhí)行,則更新一輪結(jié)束的時(shí)間,以便更新視圖的顯示。 讓手表振動(dòng)讓用戶知道是時(shí)候切換到他們嘴巴的新部分了。如果最后一輪完成,我們可以進(jìn)行兩次振動(dòng)提醒用戶。最后,將計(jì)時(shí)器安排到 run loop 中。
extendedRuntimeSession(_:didInvalidateWith:error:) 是禁用計(jì)時(shí)器的地方:
func extendedRuntimeSession( _ extendedRuntimeSession: WKExtendedRuntimeSession, didInvalidateWith reason: WKExtendedRuntimeSessionInvalidationReason, error: Error? ) { timer.invalidate() timer = nil endOfRound = nil endOfBrushing = nil roundsLeft = 0 }
更新 UI
修改 ContentView:
import SwiftUI struct ContentView: View { @Environment(.scenePhase) private var scenePhase @ObservedObject private var model = ContentModel() @State var showGettingReady = false var body: some View { ZStack { VStack { Button { showGettingReady = true } label: { Text("Start brushing") } .disabled(model.roundsLeft != 0) .padding() if let endOfBrushing = model.endOfBrushing, let endOfRound = model.endOfRound { Text("Rounds Left: (model.roundsLeft - 1)") Text("Total time left: (endOfBrushing, style: .timer)") Text("This round time left: (endOfRound, style: .timer)") } } if showGettingReady { GetReadyView { showGettingReady = false model.startBrushing() } } else { EmptyView() } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
添加開始按鈕、時(shí)間展示,并運(yùn)行項(xiàng)目,我們的 Dentisit 就開始工作了:
附件
你可以在這里獲得文章項(xiàng)目:github.com/LLLLLayer/A…
以上就是Apple Watch App Lifecycle應(yīng)用開發(fā)的詳細(xì)內(nèi)容,更多關(guān)于Apple Watch App Lifecycle的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
IOS動(dòng)畫效果源代碼整理(粒子、雪花、火焰、河流、蒸汽)
本篇文章給大家整理的IOS的關(guān)于動(dòng)畫的效果代碼整理,很多效果非常的好看,有興趣的學(xué)下。2018-01-01iOS中的應(yīng)用啟動(dòng)原理以及嵌套模型開發(fā)示例詳解
這篇文章主要介紹了iOS中的應(yīng)用啟動(dòng)原理以及嵌套模型開發(fā)示例詳解,代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下2015-12-12使用ARM匯編破解iOS程序基礎(chǔ)知識(shí)分享
最近對(duì)iOS逆向工程很感興趣。但查到的資料中都涉及到有ARM匯編,但都只是很泛地用到,并沒有對(duì)iOS上的ARM匯編進(jìn)行比較詳細(xì)的講解。因此,經(jīng)過一系列的學(xué)習(xí)對(duì)iOS下的ARM有了一定的理解。在此打算用幾篇文字記錄下來, 限于本人水平有限,如有錯(cuò)誤請(qǐng)不吝賜教。2015-11-11iOS 檢測(cè)網(wǎng)絡(luò)狀態(tài)的兩種方法
一般有Reachability和AFNetworking監(jiān)測(cè)兩種方式,都是第三方的框架,下文逐一詳細(xì)給大家講解,感興趣的朋友一起看看吧2016-10-10詳解IOS開發(fā)之實(shí)現(xiàn)App消息推送(最新)
這篇文章主要介紹了詳解IOS開發(fā)之實(shí)現(xiàn)App消息推送(最新),具有一定的參考價(jià)值,有興趣的可以了解一下。2016-12-12禁止iPhone Safari video標(biāo)簽視頻自動(dòng)全屏的辦法
本篇文章給大家分析有沒有辦法禁止iPhone Safari video標(biāo)簽視頻自動(dòng)全屏,以下給出好多種情況分享,感興趣的朋友可以參考下2015-09-09解決iOS11圖片下拉放大出現(xiàn)信號(hào)欄白條的bug問題
這篇文章主要介紹了iOS11圖片下拉放大出現(xiàn)信號(hào)欄白條的bug問題,需要的朋友參考下吧2017-09-09