Watch App Lifecycle

watchOS App 的生命周期比 iOS App 的生命周期要更复杂一些。watchOS App 可能会处于以下五种状态:

  • Not running - 未运行
  • Inactive - 不活跃
  • Active - 活跃
  • Background - 后台
  • Suspended - 挂起

常见的状态转换

watchOS App 的五种状态由下图中的紫色框表示:

作为开发者,我们只会与其中三个状态进行交互:Inactive、Active、Background。 而在 Not running 和 Suspended 状态下 App 未运行。

启动 App 到 Active 状态

如果用户尚未运行 App 或系统从内存中清除了 App,则 App 以 Not running 状态开始。App 启动后,它会从 Not running 转换到 Inactive 状态。

处于 Inactive 状态时,App 仍然在前台运行,但不会响应用户的任何操作。但是 App 可能仍在执行某些代码。在此状态下,App 并不是无法运行。

App 几乎立即将转换到 Active 状态,这是在 Apple Watch 屏幕上运行的 App 的正常模式。当处于 Active 状态时,App 可以接收来自 Apple Watch 上的物理控件和用户手势的操作。

当用户启动我们的 App 但尚未运行时,watchOS 将执行以下操作:

  • 调用 applicationDidFinishLaunching() 并将 scenePhase 设置为 .inactive;
  • 创建 App 的初始 scene 和 rootView;
  • 调用 applicationWillEnterForeground();
  • 调用 applicationDidBecomeActive() 并将 scenePhase 设置为 .active;
  • App 将出现在屏幕上,watchOS 将调用 rootView 的 onAppear(perform:)。

注意:从 watchOS 7 及更高版本开始,在使用 SwiftUI 时,scenePhase 环境变量的更新,要早于 WKExtensionDelegate 方法的调用。

App 到 Inactive 状态

一旦用户放下手臂,watchOS 将变为 Inactive 状态。如前所述,该 App 仍在运行并执行我们的代码。Inactive 状态需要减少 App 对 Apple Watch 电池的影响,我们应暂停或取消任何不需要的电池密集型操作。例如,我们可以禁止当前正在运行的动画的展示。

我们还需要考虑是否需要保存一些数据,保存 Core Data Stack?向 UserDefaults 写入内容?其实,一旦用户再次抬起手臂,App 就会再次激活。因此,此时并不需要保存或保存太多内容,否则可能会对电量产生较大压力。

当我们的 App 转换到 Inactive 状态时,watchOS 会将 scenePhase 设置为 .inactive,然后调用 WKExtensionDelegate 的 applicationWillResignActive() 方法。

App 到 Background 状态

在转换到 Inactive 状态两分钟后,或者当用户切换到另一个 App 时,我们的 App 将转换到 Background 状态。通过系统也可以直接将 App 启动到 Background 状态,如后 Background session 和 Background task。

在 Suspended 状态之前,操作系统会为 Background 状态的 App 提供一小段不确定的时间。如果我们的 App 从 Inactive 状态转换到 Background 状态,我们需要快速执行必要的操作来处理 App。

我们可以使用 SwiftUI ScenePhase 或 WKExtensionDelegate 的 applicationDidEnterBackground 来确定我们的 App 何时到 Background 状态。当从 Inactive 状态转换到 Background 状态时,watchOS 会将scenePhase 设置为 .background,然后调用applicationDidEnterBackground()。如果 App 需要太多资源,watchOS 将暂停该 App。

返回表盘

在 watchOS 7 之前,我们可以在 App 转换到 Background 后请求 8 分钟。在 watchOS 7 及后续版本中,用户可以通过 Apple Watch 上的 设置 ▸ 通用 ▸ 返回表盘 来进行超时配置。用户可以选择三个选项:“始终”、“2 分钟后”或“1 小时后”。默认情况下,所选设置会应用到所有 App,但用户也可以为每个 App 选取自定时间。默认值为两分钟。 根据功能需要,我们可能希望告诉用户如何将 App 的设置更改为一小时。

额外的 Background 执行时间

如果在转换到 Background 时,App 需要执行的工作量比 watchOS 为我们的App 提供时间要长,那么我们需要重新考虑 App 进行的操作,例如删除网络调用。如果我们已经进行了所有可以进行的优化,但仍然需要更多的处理时间,我们可以调用 ProcessInfo 类的 performExpiringActivity(withReason:using:) 方法。如果在 App 处于前台时调用,将获得 30 秒。如果在后台调用,将获得 10 秒。

系统将异步尝试执行我们提供给 using 参数的代码块,它将返回一个布尔值,让我们知道 App 是否即将暂停。如果我们收到 false 值,那么我们可以继续,并尽快执行我们的操作。如果我们收到一个 true 值,系统不会给我们额外的时间,App 需要立即停止。

请注意,仅仅是系统允许我们开始额外的工作,并不意味着它会给我们足够的时间来完成它。如果我们的代码块仍在运行,并且操作系统需要暂停我们的 App ,那么我们的代码块将被使用 true 参数再次调用。我们的代码应能够处理此取消请求。

例如,我们可以在每个操作之前检查 watchOS 是否告诉我们停止工作。假设我们有一个布尔实例属性 cancel,我们会执行以下操作:

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")
}

在代码中:

  • 立即检查我们是否被允许运行 如果系统告诉你暂停,那么我们将取消属性设置为 true。

  • 在尝试保存我们的 CoreDataModel 之前,请确保未设置 cancel。另一个线程可能已经调用了相同的方法并请求挂起。

  • 在保存到 UserDefaults 之前,请检查操作系统是否告诉我们停止。

每次检查取消可能看起来有点奇怪,但这样做可以确保我们遵守操作系统的指示。 在示例中,我们只需在被告知时停止操作,而时间情况下,我们可能需要快速执行其他操作来标记我们无法完成的操作。

App 到 Active 状态

如果用户在 App 处于 Background 状态时与其交互,watchOS 将通过以下过程将其转换回 Active 状态:

  • 以 .background 状态重新启动应用程序;
  • 调用 applicationWillEnterForeground();
  • 将 scenePhase 设置为 .active;
  • 调用 applicationDidBecomeActive()。

我们可能会对用户在后台状态下如何与应用交互感到困惑。这是用户使用了 App 提供的复杂功能。

App 到 Suspended 状态

当我们的 App 最终转换到 Suspended 状态时,所有代码执行都会停止。 App 仍在内存中,但不会处理事件。

当我们的 App 处于 Background 状态并且没有任何待处理的任务要完成时,系统会将我们的 App 转换为 Suspended 状态。

一旦我们的 App 进入 Suspended 状态,它就有资格被清除。 如果操作系统需要更多内存,它可能会在不通知的情况下从内存中清除任何处于 Suspended 状态的 App。

系统将尽最大努力不清除最近执行的 App、Dock 中的任何 App 以及在当前表盘上有复杂功能的任何 App。 如果系统必须清除上述 App 之一,它将在内存可用时重新启动该 App。

始终显示 Always on

在 watchOS 6 之前,当用户最近没有与之交互时,Apple Watch 会息屏。 Always On 改变了这一点,使手表继续显示时间。但是,watchOS 会模糊当前运行的 App,并在显示屏上显示时间。

而现在,在默认情况下,会显示我们的 App 的用户界面,而不是时间。只要它是最前面的 App 或运行 Background session,watchOS 就不会模糊它。处于 Always On 时,手表屏幕会变暗,并且 UI 更新速度会变慢,从而延长电池使用时长。

如果用户与我们的 App 交互,系统将返回其 Active 状态。 Always On 的一个显着优势与日期和时间有关。如果 App 显示计时器或相对日期等,则 UI 将继续更新为正确的值。

如果你希望为我们的 App 禁用 Always On,只需在 Info.plist 中将 WKSupportsAlwaysOnDisplay 键设置为 false。用户还可以通过“设置”▸“显示和亮度”▸“始终显示”来为某些 App 或整个设备禁用 Always on。

状态变化示例

创建一个新项目:

新增 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)")
        }
    }
}

我们可以在物理设备上运行该项目以观察状态变化。当我们抬起和放下手腕时,会看到状态在 Active 和 Inactive 之间变化。如果我们让应用程序处于 Inactive 状态两分钟,它会切换到 Background 模式。

WKExtendedRuntimeSession

有四种特定的类型,可以让我们的 App 保持运行,甚至在后台运行。

Self care

专注于用户情绪健康或健康的 App 将在前台运行,即使在手表屏幕未打开。 watchOS 将为 App 序提供 10 分钟的 Session,该 Session 将持续到用户切换到另一个 App 或 App 使 Session 无效。

Mindfulness

冥想已越来越成为一种流行的,Mindfulness - 正念 App 将保持在前台。不过,这是一个耗时的过程,所以 watchOS 会给 App 一个 1 小时的 Session。

Physical therapy

伸展等锻炼非常适合物理治疗课程。 与最后两种 Session 类型不同,物理治疗 Session 在后台运行。 后台 Session 将一直运行,直到到时间限制或 App 使 Session 无效,即使用户启动另一个 App 也是如此。物理治疗课程可长达 1 小时。

Smart alarm

当我们需要安排时间检查用户的心率和运动时,智能提醒是一个不错的选择。 App 将获得一个 30 分钟的 Sesion。

与其他三种会话类型不同,我们必须安排智提醒钟在未来某一个时刻开始。 我们需要在接下来的 36 小时内启动会话,并在我们的 App 处于 WKApplicationState.active 状态时安排它。我们的Aoo 能会暂停或终止,但 Sesion 将继续。

当需要处理 Session 时,watchOS 将调用 App 的 WKExtensionDelegate 的 handle(_:)。

注意:我们必须在 App 退出之前设置会话的委托,否则 Session 将终止。

一旦 Session 运行,我们必须通过调用会话的 notifyUser(hapticType:repeatHandler:) 来触发提醒。 如果我们忘记了,watchOS 将显示警告并提议禁用 Session。

刷牙提醒 Demo - Dentisit

搭建项目框架

我们将实现一个刷牙时,提醒用户刷牙时间的 App Dentisit,首先创建项目:

修改 ContentView.swift,这样能让我们更好的看到 App 的状态:

struct ContentView: View {
    @Environment(.scenePhase) private var scenePhase
    var body: some View {
        Text("Hello, World!")
        .onChange(of: scenePhase) { print($0) }
    }
}

新增 GetReadyView.swift,它将实现一个准备视图:

import SwiftUI
struct GetReadyView: View {
  private let color: Color // 色环颜色
  @State private var stage: Int // 倒计时秒数,屏幕上展示的值
  private let onComplete: (() -> Void)? // 倒计时完成后回调
  private let denominator: Double // 倒计时秒数,保存总值,用来计算色环
  @State private var trim = 1.0 // 色环显示比例
  @State private var text = "Ready" // 色环中心文案
  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()
      // 背景色环
      Circle()
        .stroke(style: StrokeStyle(lineWidth: 10, lineCap: .round))
        .foregroundColor(color.opacity(0.5))
      // 色环
      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 // 更新当前时间
    self.text = "(self.stage)"
    trim = Double(stage) / denominator // 更新色环
    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()
  }
}

具体代码内容,已经添加注释以辅助阅读,可以在 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 选项。 出现提示时,从功能列表中选择 Background Modes,并修改 Session Type:

添加 ContentModel

创建一个名为 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 的要求。

前三个属性用 @Published 包装,我们将使用它们来跟踪用户还需要刷几轮、刷多久。

最后,我们需要一种方法来知道时间到了,并控制会话。

用户开始刷牙后,我们需要创建会话并更新表盘按钮上显示的文本。将此添加到 ContentModel:

func startBrushing() {
    session = WKExtendedRuntimeSession()
    session.delegate = self
    session.start()
}

将以下代码添加到文件末尾实现 WKExtendedRuntimeSessionDelegate:

extension ContentModel: WKExtendedRuntimeSessionDelegate {
    func extendedRuntimeSessionDidStart(
        _ extendedRuntimeSession: WKExtendedRuntimeSession
    ) {
    }
    func extendedRuntimeSessionWillExpire(
        _ extendedRuntimeSession: WKExtendedRuntimeSession
    ) {
    }
    func extendedRuntimeSession(
        _ extendedRuntimeSession: WKExtendedRuntimeSession,
        didInvalidateWith reason: WKExtendedRuntimeSessionInvalidationReason,
        error: Error?
    ) {
    }
}

遵守协议非常简单:

  • 一旦 Session 开始运行,系统就会调用 extendedRuntimeSessionDidStart(_:)。
  • 如果 App 即将超过 Session的时间限制,watchOS 将在强制使会话过期之前调用 extendedRuntimeSessionWillExpire(_:)。
  • 无论出于何种原因,当会话完成时,watchOS 都会调用 extendedRuntimeSession(_:didInvalidateWith:error:)。

继续在 在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)

我们不关心实际的日期或时间:我们只需要特定的秒数。当 Session 开始时,让手表快速振动是很好的用户体验。

现在我们知道每轮刷牙需要多长时间,继续设置一个计时器。 添加以下代码以完成该方法:

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)

我们生成一个计时器,该计时器在当前刷牙 Round 结束时开始,并每隔 secondsPerRound 秒重复一次。如果仍有几轮要执行,则更新一轮结束的时间,以便更新视图的显示。 让手表振动让用户知道是时候切换到他们嘴巴的新部分了。如果最后一轮完成,我们可以进行两次振动提醒用户。最后,将计时器安排到 run loop 中。

extendedRuntimeSession(_:didInvalidateWith:error:) 是禁用计时器的地方:

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()
    }
}

添加开始按钮、时间展示,并运行项目,我们的 Dentisit 就开始工作了:

附件

你可以在这里获得文章项目:github.com/LLLLLayer/A…

以上就是Apple Watch App Lifecycle应用开发的详细内容,更多关于Apple Watch App Lifecycle的资料请关注Devmax其它相关文章!

Apple Watch App Lifecycle应用开发的更多相关文章

  1. 详解如何通过H5(浏览器/WebView/其他)唤起本地app

    这篇文章主要介绍了详解如何通过H5(浏览器/WebView/其他)唤起本地app的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. H5混合开发app如何升级的方法

    本篇文章主要介绍了H5混合开发app如何升级的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  3. html5唤起app的方法

    这篇文章主要介绍了html5唤起app的方法的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  4. xcode – 上传到App Store时进行身份验证

    只需为现有安装/文件夹创建备份,这很重要,因为在(新)安装期间,Transporter将删除以前的安装:现在运行以下命令来更新Transporter:希望这有助于某人.

  5. App store拒绝应用程序在iOs 10上支持IPV6网络

    我收到苹果公司的app拒绝邮件,下面是我们在连接到IPv6网络的Wi-Fi上运行iOS10.0.2的iPad和iPhone上查看了应用中的一个或多个错误.具体来说,应用程序在启动时仍保留在启动屏根据他们的要求,我已经在我的Mac上创建了NAT64网络,并为iPhone5S设备10.0.2os版本共享了互联网,App工作正常,但苹果称其不与IPv6合作任何人都可以确认我需要检查其他什么吗?

  6. ios – 我如何从iPhone中提取IPA以从App Store下载以便我可以在IPA中查看资产?

    我最喜欢的应用程序之一已从应用程序商店中删除,我想因为它没有在太长时间内更新.我有一台旧设备,但没有下载到我的新手机上.如何获得IPA以便我可以查看应用程序包并查看应用程序中的资产?

  7. ios – – [Not A Type _cfTypeID]:发送到解除分配的实例的消息

    我正在使用代码为图像提供不同的效果,如对比度,色调,饱和度等;并使用了appleglImageProcessing代码,我从我的视图跳转到glimgeProcessing,并将结果图像保存到appDelegate文件中的uiimage属性.从Eagle视图返回后,我使用viewDidAppear函数将我的图像视图更改为更新的图像我的代码是我的日志响应是尝试将图像设置为我的imageView时出现问

  8. iOS扩展:是否需要增加其捆绑版本(CFBundleVersion)?

    我是否必须在我的扩展程序的Info.plist中增加CFBundLeversion以确保它覆盖现有的?或者,如果在主应用程序的Info.plist中这样做就足够了?

  9. ios – navigator.app undefined

    谢谢你的帮助.干杯,米格尔解决方法“navigator.app”对象仅适用于Android.幸运的是,在即将发布的PhoneGap2.3.0版本中你可以做到:做你想做的事.

  10. 验证Xcode安装时,“/Applications/Xcode.app:密封资源丢失或无效”错误

    你认为我需要重新安装Xcode以防万一?

随机推荐

  1. iOS实现拖拽View跟随手指浮动效果

    这篇文章主要为大家详细介绍了iOS实现拖拽View跟随手指浮动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  2. iOS – genstrings:无法连接到输出目录en.lproj

    使用我桌面上的项目文件夹,我启动终端输入:cd然后将我的项目文件夹拖到终端,它给了我路径.然后我将这行代码粘贴到终端中找.-name*.m|xargsgenstrings-oen.lproj我在终端中收到此错误消息:genstrings:无法连接到输出目录en.lproj它多次打印这行,然后说我的项目是一个目录的路径?没有.strings文件.对我做错了什么的想法?

  3. iOS 7 UIButtonBarItem图像没有色调

    如何确保按钮图标采用全局色调?解决方法只是想将其转换为根注释,以便为“回答”复选标记提供更好的上下文,并提供更好的格式.我能想出这个!

  4. ios – 在自定义相机层的AVFoundation中自动对焦和自动曝光

    为AVFoundation定制图层相机创建精确的自动对焦和曝光的最佳方法是什么?

  5. ios – Xcode找不到Alamofire,错误:没有这样的模块’Alamofire’

    我正在尝试按照github(https://github.com/Alamofire/Alamofire#cocoapods)指令将Alamofire包含在我的Swift项目中.我创建了一个新项目,导航到项目目录并运行此命令sudogeminstallcocoapods.然后我面临以下错误:搜索后我设法通过运行此命令安装cocoapodssudogeminstall-n/usr/local/bin

  6. ios – 在没有iPhone6s或更新的情况下测试ARKit

    我在决定下载Xcode9之前.我想玩新的框架–ARKit.我知道要用ARKit运行app我需要一个带有A9芯片或更新版本的设备.不幸的是我有一个较旧的.我的问题是已经下载了新Xcode的人.在我的情况下有可能运行ARKit应用程序吗?那个或其他任何模拟器?任何想法或我将不得不购买新设备?解决方法任何iOS11设备都可以使用ARKit,但是具有高质量AR体验的全球跟踪功能需要使用A9或更高版本处理器的设备.使用iOS11测试版更新您的设备是必要的.

  7. 将iOS应用移植到Android

    我们制作了一个具有2000个目标c类的退出大型iOS应用程序.我想知道有一个最佳实践指南将其移植到Android?此外,由于我们的应用程序大量使用UINavigation和UIView控制器,我想知道在Android上有类似的模型和实现.谢谢到目前为止,guenter解决方法老实说,我认为你正在计划的只是制作难以维护的糟糕代码.我意识到这听起来像很多工作,但从长远来看它会更容易,我只是将应用程序的概念“移植”到android并从头开始编写.

  8. ios – 在Swift中覆盖Objective C类方法

    我是Swift的初学者,我正在尝试在Swift项目中使用JSONModel.我想从JSONModel覆盖方法keyMapper,但我没有找到如何覆盖模型类中的Objective-C类方法.该方法的签名是:我怎样才能做到这一点?解决方法您可以像覆盖实例方法一样执行此操作,但使用class关键字除外:

  9. ios – 在WKWebView中获取链接URL

    我想在WKWebView中获取tapped链接的url.链接采用自定义格式,可触发应用中的某些操作.例如HTTP://我的网站/帮助#深层链接对讲.我这样使用KVO:这在第一次点击链接时效果很好.但是,如果我连续两次点击相同的链接,它将不报告链接点击.是否有解决方法来解决这个问题,以便我可以检测每个点击并获取链接?任何关于这个的指针都会很棒!解决方法像这样更改addobserver在observeValue函数中,您可以获得两个值

  10. ios – 在Swift的UIView中找到UILabel

    我正在尝试在我的UIViewControllers的超级视图中找到我的UILabels.这是我的代码:这是在Objective-C中推荐的方式,但是在Swift中我只得到UIViews和CALayer.我肯定在提供给这个方法的视图中有UILabel.我错过了什么?我的UIViewController中的调用:解决方法使用函数式编程概念可以更轻松地实现这一目标.

返回
顶部