I want to add a custom button in the meeting option toolbar in my zoom app in ios. I see there are provisions to hide or show the toolbar or even specific buttons. But is there any way to add a custom button in the toolbar under more in IOS?

Description
I want to add a custom button in the meeting option toolbar in my zoom app in ios. I see there are provisions to hide or show the toolbar or even specific buttons. But is there any way to add a custom button in the toolbar under more in IOS ?
Which iOS Client SDK version?
12.2

Hey @acharya.sohini,

Thanks for using the dev forum!

We do not currently have an interface for this. For this level of customization, I would suggest using a custom meeting UI instead.

Thanks!
Michael

Thanks Michael_Condon for replying. Is there any swift option for custom meeting UI? I see an objective C code. Is there a swift guideline ?

Hey @acharya.sohini,

We do not yet have a Swift guide for implementing custom UI. However, all of the interfaces available in the SDK work with both Swift and Objective-C. For example, to turn on custom meeting UI in Objective-c you would call
[[MobileRTC Shared] getMeetingSettings].enableCustomUI = YES;
and in Swift you would call
MobileRTC.shared()?.getMeetingSettings.enableCustomUI = true

Thanks!
Michael

Have you find out the solution? I’m having the same demand. Thanks.

Hey @zacob,

You would need to implement a custom ui for this functionality.

Thanks!
Michael

1 Like

After enabling custom UI (set to true), how can I create my own calling UI (or add any UI controls to the default calling UI)? I dont see the guideline in document for iOS. Thanks @Michael_Condon

I found the guideline here. However; it’s hard to find out how to customize the UI, i can follow the guideline to onInitMeetingView to add my custom controller but how can i keep the original video layer, how to create controls and link to actions in my controller, it’s not clear in the document. Is there any sample code for custom UI @Michael_Condon? Thank you so much.

Hey @zacob,

We are in the process of creating better documentation for this area of the SDK. In the meantime, I can gladly help with your integration :slight_smile:

The way I have set my own Zoom SDK application up is like this.

  1. I enable custom meeting UI and I assign my main viewcontroller to be a MobileRTCCustomizedUIMeetingDelegate
  2. The MobileRTCCustomizedUIMeetingDelegate is my main viewcontroller, and it simply presents another viewcontroller when onInitMeetingView is triggered. This second viewcontroller is my customMeetingUIViewController that will actually show my meeting controls and video streams.
  3. I also assign my customMeetingUIViewController to be my MobileRTCMeetingServiceDelegate, so that it can listen to meeting events and react accordingly.

1:

guard let meetingService = MobileRTC.shared().getMeetingService() else { return }

// This is in my mainViewController
meetingService.customizedUImeetingDelegate = self

2:

extension ViewController: MobileRTCCustomizedUIMeetingDelegate {
    // Called when local user joins a meeting, and when the local user has been admitted to meeting from waiting room.
    func onInitMeetingView() {
        guard let customMeetingUIVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "CustomMeetingUIViewController") as? CustomMeetingUIViewController,
              let meetingService = MobileRTC.shared().getMeetingService() else { return }
        meetingService.delegate = customMeetingUIVC

        self.present(customMeetingUIVC, animated: false, completion: nil)
        self.customMeetingUIViewController = customMeetingUIVC
    }

    func onDestroyMeetingView() {
        MobileRTC.shared().getMeetingService()?.delegate = self
    }
}

3:

@objc class CustomMeetingUIViewController: UIViewController {
    // My views and properties are here
    
    // My button actions are here. Note, I made an SDK wrapper called SDKManager to keep things organized, but this isnt required.
    @IBAction func leaveMeetingButtonPressed(_ sender: Any) {
        SDKManager.leaveMeeting()
    }

    @IBAction func endMeetingButtonPressed(_ sender: Any) {
        SDKManager.endMeeting()
    }
    // ...

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        updateViews()
    }

    private func updateViews() {
    // View updates are handled here, this is basically psuedocode
        if let firstRemoteUser = SDKManager.remoteUserIDsInMeeting()?.first as? UInt, let remoteUserVideoView = SDKManager.remoteUserVideoView(userID: firstRemoteUser, videoAspect: MobileRTCVideoAspect_PanAndScan, frame: remoteUserView.frame) {
            DispatchQueue.main.async { [weak self] in
                self?.remoteUserView.addSubview(remoteUserVideoView)
            }
        }
        // ...
    }
}

extension CustomMeetingUIViewController: MobileRTCMeetingServiceDelegate {
  // This is where all of my meeting service delegate callbacks are handled. I removed a lot of code here just to simplify this post, but I think you get the idea. 

    // Called when joining a meeting and when being admitted from the waiting room.
    func onMeetingReady() {
    }

    func onJoinMeetingConfirmed() {
    }

    func onJBHWaiting(with cmd: JBHCmd) {
    }

    func onWaitingRoomStatusChange(_ needWaiting: Bool) {
        updateViews()
    }

    func onMeetingStateChange(_ state: MobileRTCMeetingState) {
        print("** meeting state \(state.name)")
        currentMeetingStateLabel.text = state.name
    }

    func onMeetingError(_ error: MobileRTCMeetError, message: String?) {
        mostRecentMeetErrorLabel.text = error.name
    }

    func onJoinMeetingInfo(_ info: MobileRTCJoinMeetingInfo, completion: @escaping (String, String, Bool) -> Void) {

    }
}

Thanks!
Michael

1 Like

Hi @Michael_Condon . Thank you for your response.
Yes, i went to the steps you listed out. I still dont see how you handle the video stream in your
CustomMeetingUIViewController
Could you show me your CustomMeetingUIViewController storyboard and your SDKManager and how you get the remoteUserVideoView and is video stream included in the remoteUserVideoView?

Thank you so much.

Hey @zacob,

My storyboard is a little complicated because it has lots of buttons and other views, but I basically have 2 UIViews that have a hardcoded size. One view is for rendering the local user’s view, and one is for rendering the remote user’s view.

    static func localUserVideoView(_ videoAspect: MobileRTCVideoAspect, frame: CGRect) -> MobileRTCVideoView? {
        guard let meetingService = MobileRTC.shared().getMeetingService() else { return nil }

        let localUserID = meetingService.activeUserID()
        let localUserVideoView = MobileRTCVideoView(frame: frame)
        localUserVideoView.setVideoAspect(videoAspect)
        let showAttendeeVideoSuccessful = localUserVideoView.showAttendeeVideo(withUserID: localUserID)

        if showAttendeeVideoSuccessful {
            print("Started showing local user video.")
        } else {
            print("Failed to show local user video.")
        }

        return localUserVideoView
    }

    static func remoteUserVideoView(userID: UInt, videoAspect: MobileRTCVideoAspect, frame: CGRect) -> MobileRTCVideoView? {
        let remoteUserVideoView = MobileRTCVideoView(frame: frame)
        remoteUserVideoView.setVideoAspect(videoAspect)
        let showAttendeeVideoSuccessful = remoteUserVideoView.showAttendeeVideo(withUserID: userID)

        if showAttendeeVideoSuccessful {
            print("Started showing remote user video.")
        } else {
            print("Failed to show remote user video.")
        }

        return remoteUserVideoView
    }

Once I run those functions I just add the returned view as a subview to the UIViews in the storyboard.

Thanks!
Michael

Thanks @Michael_Condon , got your idea.
And other gesture like move local video or minimize call will be handled by ourself right?

Thanks @Michael_Condon , i could customize UI as i want. I have one more question is how to get the ellapse time by userID? Or i need my own timer to calculate?

Hey @zacob,

Yep, that is correct.

Yes, this would have to be implemented manually.

Thanks!
Michael

1 Like

Thank you for your support @Michael_Condon !

1 Like

You are very welcome @zacob :slight_smile:

Please let us know if there is anything else that comes up!

Hi @Michael_Condon ,
It’s me again :slightly_smiling_face:
Is there any APIs from Zoom SDK to control audio output sources (speakers/headphones/airpod)? Or i need to use Apple AVAudioSession to do that?
Thanks.

Hey @zacob,

It’s nice to see you again :slight_smile:

If your device is already connected to multuple audio outputs, you should be able to call the switchMyAudioSource method from the MobileRTCMeetingService.

Thanks!
Michael

1 Like

Hi @Michael_Condon , looks. like it’s not my demand. I decided to handle by AVAudioSession to fit my requirement.
BTW, i will change to new topic for future questions to avoid ruining this one.
Thank you for your support.

Hey @zacob,

Sounds good! Let me know how else I can assist :slight_smile:

Thanks!
Michael