Controlling volume of Apple Watch
Asked Answered
V

3

5

I'm trying to control the volume of the Apple Watch itself in code from SwiftUI.

I'm streaming audio using the AVPlayer.

Is there an API to set the Volume of the Watch or use to Digital Crown to control the volume without

  • Setting the volume property on the AVPlayer. This just set the volume relative to the system volume. So if the system is muted it does not increase the volume.
  • Using the WKInterfaceVolumeControl. This does the job, but it can not be sized does and takes a lot of space on the small screen.
Valentine answered 24/10, 2019 at 10:57 Comment(3)
Any luck getting this to work?Redhot
I ended up adding a WKInterfaceVolumeControl through WKInterfaceObjectRepresentable to the View hierarchy and settings its opacity to 0. You have to make sure that the volume control keeps the focus. I added a repeating timer where I set the focus, just to be sure. Then then observe AVAudioSession.sharedInstance().observe(\.outputVolume) and update a smaller view in code. This works ok... On my older watch there is quite a lack between the volume changing and the app getting the volume update. But I think it is the only way to control the volume without the big control.Valentine
hi @Valentine Could you please share more details and example with little code as well.Crosshatch
V
9

The workaround I ended up with was this:

  1. Wrap the WKInterfaceVolumeControl to use it in SwiftUI
struct VolumeView: WKInterfaceObjectRepresentable {
    typealias WKInterfaceObjectType = WKInterfaceVolumeControl


    func makeWKInterfaceObject(context: Self.Context) -> WKInterfaceVolumeControl {
        let view = WKInterfaceVolumeControl(origin: .local)
        Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak view] timer in
            if let view = view {
                view.focus()
            } else {
                timer.invalidate()
            }
        }
        DispatchQueue.main.async {
            view.focus()
        }
        return view
    }
    func updateWKInterfaceObject(_ wkInterfaceObject: WKInterfaceVolumeControl, context: WKInterfaceObjectRepresentableContext<VolumeView>) {
    }
}
  1. Add the VolumeView to the view hierarchy with opacity = 0.
        .background(VolumeView().opacity(0))
  1. Listen to volume updates from the system.
volumeObserver = AVAudioSession.sharedInstance().observe(\.outputVolume) { session, _ in
            print("Output volume: \(session.outputVolume)")
            self.volume = Double(session.outputVolume)
        }

With that you can update some other view, but keep in mind that especially on older what's the update does not always happen (immediately).

Valentine answered 11/12, 2019 at 9:23 Comment(1)
This gave me the right direction, thanks! For me it worked, the insert a WKInterfaceVolumeControl into my 'Player' Interface Controller and set its 'Alpha' to '0'. Currently I didn't found 'focus' problems.Sexless
D
1

leoMehlig's answer only works for me if I add a bit of delay and resign the focus before focusing for the first time.

So, speaking in code, I changed this:

DispatchQueue.main.async {
    view.focus()
}

to this:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
    view.resignFocus()
    view.focus()
}
Doan answered 8/10, 2020 at 14:9 Comment(0)
S
0

leoMehlig's answer works for me as is, but I noticed the embedded WKInterfaceVolumeControl was losing focus when I would tap anywhere on the watch screen.

This was because I had a .focusable(true) modifier in the view hierarchy, so any view being tapped further up in the hierarchy would keep the focus.

I had added the .focusable(true) when I was trying to bind AVPlayer.volume to the digital crown rotation, it was no longer needed when using WKInterfaceVolumeControl

Sandra answered 14/9, 2023 at 17:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.