从 Unity 桥接到 Swift 时通知中心不调用

Notification Center not calling when bridging from Unity to Swift

我们目前有一个 Unity(基于 C#)应用程序需要我们检测 iOS 中的音量按钮按下情况。我们目前通过 Objective-C++ 桥桥接本机 iOS 代码,并且该桥接我们的 Swift 代码。

我的想法是订阅公开的 AVSystemController API 以在每次音量变化时告诉我们,但它似乎并没有在触发关联的通知时触发。我们这样设置:

VolumeDetector.mm

#import <Foundation/Foundation.h>
#import <MediaPlayer/MediaPlayer.h>
#import "unityswift-Swift.h"

@interface VolumeDetectorDelegateHandler: NSObject<VolumeDetectorDelegate>

@end

@implementation VolumeDetectorDelegateHandler

- (void)volumeDidChange {
    UnitySendMessage("UIManager", "TakePicPressed", "Volume Changed");
}

@end

extern "C" {
    void subscribeToVolumeButtons() {
        VolumeDetector *detector = [VolumeDetector new];
        VolumeDetectorDelegateHandler *handler = [VolumeDetectorDelegateHandler new];
        detector.delegate = handler;
        MPVolumeView *mpVolumeview = [MPVolumeView new];
        [mpVolumeview setShowsVolumeSlider:NO];
        detector.volumeView = mpVolumeview;
        [detector subscribeToVolumeButtons];
    }
}

VolumeDetector.swift

import Foundation
import UIKit
import MediaPlayer

@objc public protocol VolumeDetectorDelegate {
    func volumeDidChange()
}

public class VolumeDetector: NSObject {
    @objc public var delegate: VolumeDetectorDelegate?
    @objc public var volumeView: MPVolumeView?

    @objc func subscribeToVolumeButtons() {
        print("Subscribing to volume buttons");

        NotificationCenter.default.addObserver(self, 
                                            selector: #selector(volumeChanged(notification:)),
                                            name: NSNotification.Name(rawValue: "AVSystemController_SystemVolumeDidChangeNotification"),
                                            object: nil)
    }

    @objc func volumeChanged(notification: NSNotification) {
        print("Volume changed")
        if let userInfo = notification.userInfo {
            if let volumeChangeType = userInfo["AVSystemController_AudioVolumeChangeReasonNotificationParameter"] as? String {
                if volumeChangeType == "ExplicitVolumeChange" {
                    print("Explicit volume changed")
                    delegate?.volumeDidChange()
                }
            }
        }
    }
}

这种面向协议的设计适用于其他用例中的回调,但在这种情况下似乎没有调用 volumeChanged

我已经在原生 Xcode 项目中测试了这个确切的代码并且它有效..所以不用担心 'AVSystemController_SystemVolumeDidChangeNotification'.

有问题

解法:

正如 Rob Napier 指出的那样,我必须保持对委托处理程序的强引用,以便观察者可以在附近逗留。这是更新后的工作代码:

VolumeDetector.mm

#import <Foundation/Foundation.h>
#import <MediaPlayer/MediaPlayer.h>
#import "unityswift-Swift.h"

@interface VolumeDetectorDelegateHandler: NSObject<VolumeDetectorDelegate>
@property (strong, retain) VolumeDetector *detector;
@property (strong, retain) MPVolumeView *volumeView;
@end

static VolumeDetectorDelegateHandler *handler;

@implementation VolumeDetectorDelegateHandler

- (void) subscribeToVolButtons {
    _detector = [VolumeDetector new];
    _detector.delegate = handler;
    [_detector subscribeToVolumeButtons];
}

-(void)volumeDidChange {
    UnitySendMessage("UIManager", "TakePicPressed", "Volume Changed");
}

@end

extern "C" {
    void subscribeToVolumeButtons() {
        handler = [VolumeDetectorDelegateHandler new];
        [handler subscribeToVolButtons];
    }
}

VolumeDetector.swift

import Foundation
import UIKit
import MediaPlayer

@objc public protocol VolumeDetectorDelegate {
    func volumeDidChange()
}

public class VolumeDetector: NSObject {
    @objc public var delegate: VolumeDetectorDelegate?

    @objc func subscribeToVolumeButtons() {
        if let vc = UnityGetGLViewController() {
            let volumeView = MPVolumeView(frame: CGRect(x:-100, y:0, width:0, height:0))
            volumeView.showsRouteButton = false
            vc.view.addSubview(volumeView);

            NotificationCenter.default.addObserver(self,
                                                    selector: #selector(volumeChanged(notification:)),
                                                    name: NSNotification.Name(rawValue: "AVSystemController_SystemVolumeDidChangeNotification"),
                                                    object: nil)
        }
    }

    @objc func volumeChanged(notification: NSNotification) {
        if let userInfo = notification.userInfo {
            if let volumeChangeType = userInfo["AVSystemController_AudioVolumeChangeReasonNotificationParameter"] as? String {
                if volumeChangeType == "ExplicitVolumeChange" {
                    delegate?.volumeDidChange()
                }
            }
        }
    }
}

您在 subscribeToVolumeButtons() 中创建了一个检测器,它订阅了通知,然后您立即丢弃了该检测器。到那时,您应该期望它会自动取消订阅通知。如果您希望检测器留在周围并继续运行,您将需要一些对它有强烈参考的东西。

如果您将 deinit 添加到 VolumeDetector,我预计它会在 subscribeToVolumeButtons() returns.

后不久被触发