Customizing the look

This section describes the different ways you can customize the Talk SDK UI.

Prerequisites

The Talk SDK is integrated in your application as described in Getting started. All steps up to Making a call apply.

Before you start

Note the following:

  • SDK provides you with an out-of-the-box UI for the call configuration view and call view
  • You can customize text, colors, and fonts used in the out-of-the-box UI
  • You cannot configure the layout and placement of the UI elements on the views

If customizing the UI elements is important to you, consider building your own UI and using the SDK's API. See Using the SDK with your own UI.

Making a call using the out-of-the-box UI

The Making a call section in Getting started shows how to make a call with a preconfigured setup using the out-of-the-box UI. The benefit of that approach is that it's the most straight-forward to implement. However, it doesn't provide as many possibilities for customization as building your own UI.

You can create and present the SDK's call configuration screen. After the user provides all the required permissions, you can create and present the SDK's call screen.

Swift

private func presentCallConfiguration() {    guard let talk = talk else { return }
    let callConfigurationViewController = talk.makeCallConfigurationViewController(for: "digitalLineNickname")
    callConfigurationViewController.cancelHandler = { [weak self] in        self?.navigationController?.popViewController(animated: true)    }    callConfigurationViewController.startCallHandler = { [weak self] answer in        self?.navigationController?.popViewController(animated: false)        self?.presentCallScreen(recordingConsentAnswer: answer)    }
    navigationController?.pushViewController(callConfigurationViewController, animated: true)}
private func presentCallScreen(recordingConsentAnswer: RecordingConsentAnswer) {    guard let talk = talk else { return }
    let callData = TalkCallData(digitalLine: "digitalLineNickname", recordingConsentAnswer: recordingConsentAnswer)    let callViewController = talk.makeCallViewController(with: callData) { [weak self] _, callSummary in        if let error = callSummary.error {            // handle error        }
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {            self?.dismiss(animated: true, completion: nil)        }    }
    callViewController.modalPresentationStyle = .fullScreen    present(callViewController, animated: false, completion: nil)}

Objective-C

- (void)presentCallConfiguration{    UIViewController<CallConfigurationScreen> *callConfigurationViewController = [self.talk makeCallConfigurationViewControllerFor:@"digitalLineNickname"];
    __weak typeof(self) weakSelf = self;    [callConfigurationViewController setCancelHandler:^{        [[weakSelf navigationController] popViewControllerAnimated:YES];    }];
    [callConfigurationViewController setStartCallHandler:^(enum RecordingConsentAnswer answer) {        [[weakSelf navigationController] popViewControllerAnimated:NO];        [weakSelf presentCallScreenWithRecordingConsentAnswer:answer];    }];
    [[self navigationController] pushViewController:callConfigurationViewController animated:YES];}
- (void)presentCallScreenWithRecordingConsentAnswer:(RecordingConsentAnswer)answer{    ZDKTalkCallData *callData = [[ZDKTalkCallData alloc] initWithDigitalLine:@"digitalLineNickname"                                 				    recordingConsentAnswer:answer];    __weak typeof(self) weakSelf = self;    UIViewController<CallScreen> *callViewController = [self.talk makeCallViewControllerWith:callData                                                                        callDidFinishHandler:^(NSTimeInterval callDuration, NSError  * _Nullable error) {        if (error) {            // handle error        }
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{            [weakSelf dismissViewControllerAnimated:YES completion:nil];        });    }];
    callViewController.modalPresentationStyle = UIModalPresentationFullScreen;    [self presentViewController:callViewController animated:NO completion:nil];}

In the code sample above, the presentCallConfiguration() function performs the following tasks:

  • Creates a CallConfigurationScreen conforming view controller (using SDK's makeCallConfigurationViewController(for:) function)
  • Configures its cancelHandler and startCallHandler
  • Pushes it on to the navigation stack

When CallConfigurationScreen completes by calling its startCallHandler, the presentCallScreen(recordingConsentAnswer:) function is called. It performs the following tasks:

  • Creates a CallScreen conforming view controller using SDK's makeCallViewController(with:) function
  • Presents it modally

This is a simple example of how to manually create the SDK's screens and display them together. When you have access to the view controller instances, you can easily customize them.

Customizing the call configuration screen

The call configuration screen from the Talk SDK has the following responsibilities:

  • Request permission to access the microphone if not yet granted
  • Verify agent availability
  • Ask the user for recording consent

In this version of the SDK, getting recording consent is not available in the UI but is available in the API.

The permissions screen provides the user with additional context and reassurance as to why access to their microphone is required. You can customize the look and feel of this screen or omit it from your application.

The SDK provided call configuration view controller created with makeCallConfigurationViewController(for:) exposes two properties representing different pages of the call configuration screen. Each of those sub-screens gives access to UI controls that you can adjust to better fit the style of the application the SDK is integrated with.

Swift

let callConfigurationViewController = talk.makeCallConfigurationViewController(for: "digitalLineNickname")
let microphonePermissionScreen: MicrophonePermissionScreen = callConfigurationViewController.microphoneScreenlet recordingConsentScreen: RecordingConsentScreen = callConfigurationViewController.recordingConsentScreen

Objective-C

UIViewController<CallConfigurationScreen> *callConfigurationViewController = [self.talk makeCallConfigurationViewControllerFor:@"digitalLineNickname"];
id<MicrophonePermissionScreen> microphoneScreen = callConfigurationViewController.microphoneScreen;id<RecordingConsentScreen> recordingConsentScreen = callConfigurationViewController.recordingConsentScreen;

Customizing the look of the microphone permission screen

The MicrophonePermissionScreen lets you customize the following screen elements:

Swift

@objc public protocol MicrophonePermissionScreen {
    /// Background color of the screen container    var backgroundColor: UIColor? { get set }
    /// Label which shows main title. (ex: 'Allow microphone').    var titleLabel: UILabel! { get }
    /// Label which shows description message under the title label.    var messageLabel: UILabel! { get }
    /// Allow Button which will open alert with asking user about microphone permission. In case permission already `granted` will call. `requestPermissionCompletion`    var allowButton: UIButton! { get }
    /// Cancel Button which will cancel all the flow and call `cancelHandler`.    var cancelButton: UIButton! { get }}

Objective-C

@protocol MicrophonePermissionScreen
/// Background color of the screen container@property (nonatomic, strong) UIColor * _Nullable backgroundColor;
/// Label which shows main title. (ex: ‘Allow microphone’).@property (nonatomic, readonly, strong) UILabel * _Null_unspecified titleLabel;
/// Label which shows description message under the title label.@property (nonatomic, readonly, strong) UILabel * _Null_unspecified messageLabel;
/// Allow Button which will open alert with asking user about microphone permission. In case permission already <code>granted</code> will call. <code>requestPermissionCompletion</code>@property (nonatomic, readonly, strong) UIButton * _Null_unspecified allowButton;
/// Cancel Button which will cancel all the flow and call <code>cancelHandler</code>.@property (nonatomic, readonly, strong) UIButton * _Null_unspecified cancelButton;
@end

The RecordingConsentScreen lets you customize the following screen elements:

Swift

@objc public protocol RecordingConsentScreen {
    /// Background color of the screen container    var backgroundColor: UIColor? { get set }
    /// Label which shows main title. (ex: "Recording this call").    var titleLabel: UILabel! { get }
    /// Label which shows description message under the title label.    var messageLabel: UILabel! { get }
    /// Button which will call `startCallHandler` handler.    var startCallButton: UIButton! { get }
    /// Cancel Button which will cancel all the flow and call `cancelHandler`.    var cancelButton: UIButton! { get }
    /// The view which contains consent `UISwitch` and description label.    var consentSwitchView: UIView! { get }
    /// Switch which will reflect user's `RecordingConsentAnswer`.  Switch On will equal to `.optIn`, Switch Off - `.optOut`.    var consentSwitch: UISwitch! { get }
    /// Label which message regarding uiswitch. (ex: "I agree to this call being recorder")    var consentDescriptionLabel: UILabel! { get }
    /// Indicator which is shown when fetching RecordingConsent    var activityIndicatorView: UIActivityIndicatorView! { get }}

Objective-C

@protocol RecordingConsentScreen
/// Background color of the screen container@property (nonatomic, strong) UIColor * _Nullable backgroundColor;/// Label which shows main title. (ex: “Recording this call”).@property (nonatomic, readonly, strong) UILabel * _Null_unspecified titleLabel;/// Label which shows description message under the title label.@property (nonatomic, readonly, strong) UILabel * _Null_unspecified messageLabel;/// Button which will call <code>startCallHandler</code> handler.@property (nonatomic, readonly, strong) UIButton * _Null_unspecified startCallButton;/// Cancel Button which will cancel all the flow and call <code>cancelHandler</code>.@property (nonatomic, readonly, strong) UIButton * _Null_unspecified cancelButton;/// The view which contains consent <code>UISwitch</code> and description label.@property (nonatomic, readonly, strong) UIView * _Null_unspecified consentSwitchView;/// Switch which will reflect user’s <code>RecordingConsentAnswer</code>.  Switch On will equal to <code>.optIn</code>, Switch Off - <code>.optOut</code>.@property (nonatomic, readonly, strong) UISwitch * _Null_unspecified consentSwitch;/// Label which message regarding uiswitch. (ex: “I agree to this call being recorder”)@property (nonatomic, readonly, strong) UILabel * _Null_unspecified consentDescriptionLabel;/// Indicator which is shown when fetching RecordingConsent@property (nonatomic, readonly, strong) UIActivityIndicatorView * _Null_unspecified activityIndicatorView;
@end

Omitting the call configuration screen

If you don't want to use the SDK's call configuration screen, you can implement your own solution for the following tasks:

  • Checking the microphone permission
  • Checking digital line's status and recording consent configuration
  • If recording consent is required, gather the consent confirmation from the user

You can create the SDK's call screen directly with the presentCallScreen(recordingConsentAnswer:) function as presented in the second part of Making a call using the out-of-the-box UI.

For the details how to implement your own call configuration screen, see Implementing a custom call configuration screen.

Customizing the call screen

The call screen is less customizable than the permissions screen. You can't change the icons and colors used for action buttons. It's customized the same way as the permissions screen.

Swift

@objc public protocol CallScreen {
    /// Loading view with activity indicator.    var callLoadingView: UIView & CallLoadingView { get }
    /// Error view with retry and cancel buttons.    var callErrorView: UIView & CallErrorView { get }
    /// Timer view displaying call duration.    var callTimerView: UIView & CallTimerView { get }
    /// View containing call screen action buttons.    var callButtonsView: UIView & CallButtonsView { get }
    (...)}

Objective-C

@protocol CallScreen/// Loading view with activity indicator.@property (nonatomic, readonly, strong) UIView <CallLoadingView> * _Nonnull callLoadingView;/// Error view with retry and cancel buttons.@property (nonatomic, readonly, strong) UIView <CallErrorView> * _Nonnull callErrorView;/// Timer view displaying call duration.@property (nonatomic, readonly, strong) UIView <CallTimerView> * _Nonnull callTimerView;/// View containing call screen action buttons.@property (nonatomic, readonly, strong) UIView <CallButtonsView> * _Nonnull callButtonsView;
(...)@end

Customizing the look of the loading sub-view

The CallLoadingView represents the part of the call view that is displayed while the call is being connected. This sub-view consists of a label and an activity indicator.

Swift

@objc public protocol CallLoadingView {    var titleLabel: UILabel! { get }    var activityIndicator: UIActivityIndicatorView! { get }}

Objective-C

@protocol CallLoadingView@property (nonatomic, readonly, strong) UILabel * _Null_unspecified titleLabel;@property (nonatomic, readonly, strong) UIActivityIndicatorView * _Null_unspecified activityIndicator;@end

Customizing the look of the error sub-view

The CallErrorView represents the part of the call view that is displayed when an error occurs when connecting the call. This sub-view consists of a title and message labels and two buttons, one for canceling and a second to retry reconnecting the call.

Swift

@objc public protocol CallErrorView {    var titleLabel: UILabel! { get }    var messageLabel: UILabel! { get }
    /// Retry button. When user will tap it call will try to reconnect.    var retryButton: UIButton! { get }
    /// Cancel button. Tapping will close the Call Screen.    var cancelButton: UIButton! { get }}

Objective-C

@protocol CallErrorView@property (nonatomic, readonly, strong) UILabel * _Null_unspecified titleLabel;@property (nonatomic, readonly, strong) UILabel * _Null_unspecified messageLabel;/// Retry button. When user will tap it call will try to reconnect.@property (nonatomic, readonly, strong) UIButton * _Null_unspecified retryButton;/// Cancel button. Tapping will close the Call Screen.@property (nonatomic, readonly, strong) UIButton * _Null_unspecified cancelButton;@end

Customizing the look of the timer sub-view

The CallTimerView represents the part of the call view that is displayed while the call is connected. This sub-view consists of a title label and a timer label refreshed every second that shows the current call duration.

Swift

@objc public protocol CallTimerView {    var titleLabel: UILabel! { get }
    /// Label displaying call duration in seconds. Refreshed every second.    var timerLabel: UILabel! { get }}

Objective-C

@protocol CallTimerView@property (nonatomic, readonly, strong) UILabel * _Null_unspecified titleLabel;/// Label displaying call duration in seconds. Refreshed every second.@property (nonatomic, readonly, strong) UILabel * _Null_unspecified timerLabel;@end

Customizing the look of the buttons sub-view

The CallButtonsView represents the part of the call view that is displayed at the bottom of the call view and contains the main call action buttons. This sub-view consists of button-label pairs for:

  • Toggling audio output between speaker and headset
  • Hanging up the call
  • Toggling between muting and unmuting the microphone

Swift

@objc public protocol CallButtonsView {
    /// Button for changing the `AudioSource` between `headset` and `speaker`.    var speakerButton: UIButton! { get }    var speakerTitleLabel: UILabel! { get }
    /// Button for disconnecting the call.    var hangUpButton: UIButton! { get }    var hangUpTitleLabel: UILabel! { get }
    /// Button for muting/unmuting the microphone.    var muteButton: UIButton! { get }    var muteTitleLabel: UILabel! { get }}

Objective-C

@protocol CallButtonsView/// Button for changing the <code>AudioSource</code> between <code>headset</code> and <code>speaker</code>.@property (nonatomic, readonly, strong) UIButton * _Null_unspecified speakerButton;@property (nonatomic, readonly, strong) UILabel * _Null_unspecified speakerTitleLabel;/// Button for disconnecting the call.@property (nonatomic, readonly, strong) UIButton * _Null_unspecified hangUpButton;@property (nonatomic, readonly, strong) UILabel * _Null_unspecified hangUpTitleLabel;/// Button for muting/unmuting the microphone.@property (nonatomic, readonly, strong) UIButton * _Null_unspecified muteButton;@property (nonatomic, readonly, strong) UILabel * _Null_unspecified muteTitleLabel;@end