iOS UI Kit — Customization
Customize the iOS UI Kit via EnxSetting.shared — a shared singleton that controls
join options, UI appearance, enabled features, screen sharing, and live streaming. Configure
settings before presenting EnxVideoViewClass so they take effect when the
session loads.
These settings control how participants enter the room — what the join button says, which camera is active by default, whether a preview screen appears, and whether audio or video starts muted.
Custom Join Button Label
Override the default "Join" label on the entry button with any string:
EnxSetting.shared.setJoinText("Go Live")
Default Camera
Choose which camera is active when the session starts. Useful for apps where rear-camera use cases (e.g., field inspection, demonstrations) are primary:
EnxSetting.shared.setCameraPosition(true) // Front camera (default)
EnxSetting.shared.setCameraPosition(false) // Rear camera
Camera Preview Before Joining
Show a pre-join preview screen so participants can check their video and audio before entering the room. Disable it for faster direct-join flows:
EnxSetting.shared.isPreScreening(true) // Show preview screen
EnxSetting.shared.isPreScreening(false) // Skip directly into room
Audio-Only Call
When enabled, all video capture and rendering is suppressed and only audio is transmitted. All video-related UI elements are hidden automatically:
EnxSetting.shared.isAudioOnlyCall(true) // Audio only
EnxSetting.shared.isAudioOnlyCall(false) // Audio + Video (default)
Join with Video Muted
Enter the room with the camera off. The participant can enable their camera after joining:
EnxSetting.shared.joinAsVideoMute(true) // Enter with camera off
EnxSetting.shared.joinAsVideoMute(false) // Enter with camera on (default)
Join with Audio Muted
Enter the room with the microphone muted. Useful for large webinars where participants should be silent by default:
EnxSetting.shared.joinAsAudioMute(true) // Enter muted
EnxSetting.shared.joinAsAudioMute(false) // Enter with mic on (default)
Auto-Start RTMP Streaming on Join
Automatically begin RTMP live streaming the moment the moderator connects. Provide both the RTMP ingest URL and the CDN playback URL:
EnxSetting.shared.startLiveStreaming(true)
EnxSetting.shared.liveStreaming([
"rtmpDetails": ["rtmpUrl": "YOUR_RTMP_URL"],
"urlDetails": ["url": "YOUR_CDN_URL"]
])
When a session is used for a 1-to-1 voice call — where neither participant publishes video — the default video grid is replaced by a purpose-built calling screen. It shows the remote participant's name and initials avatar over a gradient background, with overlay buttons for microphone toggle and exit.
Two properties on EnxSetting.shared control this mode. Both must be set before
the session connects — they have no effect once the session is active.
Enable the Audio View Overlay
Set isAudioViewMode to true to activate the audio call UI. This
replaces the standard video grid with the calling screen whenever no video is being published:
EnxSetting.shared.isAudioViewMode = true
Customise the Audio View Appearance
Assign an EnxAudioViewConfig instance to audioViewConfig to define
the gradient background colours, the remote participant's name text colour, and the initials
avatar colours:
EnxSetting.shared.audioViewConfig = EnxAudioViewConfig(
gradientLayer1: UIColor(red: 0.04, green: 0.22, blue: 0.20, alpha: 1), // top gradient colour
gradientLayer2: UIColor(red: 0.01, green: 0.08, blue: 0.07, alpha: 1), // bottom gradient colour
nameTextColor: .white, // remote participant name
sortNameTextColor: .white, // initials text colour
sortNameBGColor: UIColor(red: 0.07, green: 0.55, blue: 0.44, alpha: 1) // initials avatar background
)
| Parameter | Type | Description |
|---|---|---|
gradientLayer1 | UIColor | Top colour of the background gradient. |
gradientLayer2 | UIColor | Bottom colour of the background gradient. |
nameTextColor | UIColor | Colour of the remote participant's display name. |
sortNameTextColor | UIColor | Colour of the initials text inside the avatar circle. |
sortNameBGColor | UIColor | Background colour of the avatar/initials circle. |
Both isAudioViewMode and audioViewConfig must be configured before
EnxVideoViewClass connects to the room. Changes applied after the session starts
have no effect.
Bottom Bar Background Color
Change the background color of the control bar at the bottom of the video view to match your app's brand:
EnxSetting.shared.updateBottomOptionView(withColor: .blue.withAlphaComponent(1.0))
You can define a maximum of 7 buttons in the Bottom Bar. Custom buttons use your own action targets. Buttons assigned pre-defined tags are handled automatically by the kit and do not require additional wiring.
Participant List Actions
Control which actions appear when a moderator long-presses a participant in the list.
Pass an array of ParticipantListOptions raw values:
EnxSetting.shared.configureParticipantList(
to: [
ParticipantListOptions.audio.rawValue, // Hard mute audio
ParticipantListOptions.video.rawValue, // Hard mute video
ParticipantListOptions.chat.rawValue, // Open private chat
ParticipantListOptions.disconnect.rawValue // Force disconnect
]
)
Use these APIs to control exactly which controls appear in the kit's UI. You can enable features by tag (quick) or by supplying fully configured model objects with custom icons.
Enable Controls by Tag
Pass an array of EnxRequiredEventsOption raw values to show only the controls
your app needs:
EnxSetting.shared.configureRequiredEventList(
withList: [
EnxRequiredEventsOption.audio.rawValue,
EnxRequiredEventsOption.video.rawValue,
EnxRequiredEventsOption.cameraSwitch.rawValue
]
)
Enable Controls with Custom Icons
For full brand control, supply EnxRequiredEventsOptionModel objects that pair
each control with your own normal and selected-state images:
let requiredAudio = EnxRequiredEventsOptionModel(
name: "Audio",
isSelected: false,
optionTag: .audio,
isSwith: false,
eventImageNormal: UIImage(named: "audio-on"),
eventImageSelected: UIImage(named: "audio-off")
)
let requiredVideo = EnxRequiredEventsOptionModel(
name: "Video",
isSelected: false,
optionTag: .video,
isSwith: false,
eventImageNormal: UIImage(named: "video-on"),
eventImageSelected: UIImage(named: "video-off")
)
let requiredCamera = EnxRequiredEventsOptionModel(
name: "Camera",
isSelected: false,
optionTag: .cameraSwitch,
isSwith: false,
eventImageNormal: UIImage(named: "camera-switch"),
eventImageSelected: UIImage(named: "camera-switch")
)
EnxSetting.shared.configureRequiredEventsOptionList(
withList: [requiredAudio, requiredVideo, requiredCamera]
)
iOS screen sharing uses a ReplayKit broadcast extension — a separate app extension target in your Xcode project that captures the screen and feeds frames to the kit. The extension communicates with the main app via an App Group.
Initialize the Screen Share Client
Create an EnxShareClass instance inside your broadcast extension's
SampleHandler. Pass the screen share token, your App Group name, a client
identifier, and a delegate for lifecycle callbacks:
EnxShareClass(
screenShare: "YOUR_SCREEN_SHARE_TOKEN",
withAppGroupName: "group.com.company.AppName",
withUserKey: "CLIENT_ID",
delegate: self
)
Process Frames for Sharing
Inside your broadcast extension's processSampleBuffer method, forward each
pixel buffer to the kit:
enxShare.processFrame(buffer: outPixelBuffer!, withTimeStamp: time)
Set Screen Share Layout
Control how the shared screen is presented to remote participants. The
"presenter" layout pins the shared screen as the dominant tile:
EnxSetting.shared.setOption(forScreenShare: ["layout": "presenter"])
Set App Group and User Key
Register the App Group name and client identifier so the main app and broadcast extension can communicate:
EnxSetting.shared.setAppGroupsName(
groupname: "group.com.company.AppName",
withUserKey: "CLIENT_ID"
)
Stop Screen Sharing
enxShare.stopStreenShare()
Moderators can start and stop RTMP streaming during an active session. The stream is broadcast to any RTMP-compatible ingest endpoint (e.g., YouTube Live, Facebook Live, a custom media server).
Start Streaming
To start with explicit stream details at runtime, pass an information dictionary:
EnxSetting.shared.startStreaming(information: [
"rtmpDetails": ["rtmpUrl": "YOUR_RTMP_URL"],
"urlDetails": ["url": "YOUR_CDN_URL"]
])
To start using the stream details that were configured at join time (via
EnxSetting.shared.liveStreaming()), pass nil:
EnxSetting.shared.startStreaming(information: nil)
Stop Streaming
EnxSetting.shared.stopStreaming()
Show / Hide Live Indicator
Toggle the on-screen "Live" badge that appears while RTMP streaming is active:
EnxSetting.shared.isShow(GoLiveIndicator: true) // Show badge
EnxSetting.shared.isShow(GoLiveIndicator: false) // Hide badge
Send Custom Data
Send arbitrary structured data to all participants — used for polls, Q&A responses, or any custom in-session signalling:
EnxSetting.shared.sendUserData(enxSendUserData)
Receive Custom Data
Implement this delegate method to handle incoming custom data from other participants:
func didUserDataReceived(_ userData: [String: Any]) {
// Handle incoming poll responses, Q&A answers, or custom events
print("Received user data: \(userData)")
}
Close the Chat Panel
EnxSetting.shared.closeChatPage()
Observe Panel Open / Close Events
The kit notifies your delegate whenever a slide-over panel opens or closes. Use this to adjust your own UI — for example, hiding floating controls when a panel is visible:
func didPageSlide(_ pageName: EnxPageSlideEventName, isShow: Bool) {
// pageName: EnxChat | EnxPolling | EnxQnA | EnxScreenShare
// isShow: true = panel opened, false = panel closed
}
Room and User Information
Query current session state at any time using these accessors on EnxSetting:
EnxSetting.getParticipantList() // All connected participants
EnxSetting.shared.getRoomMode() // "group" or "lecture"
EnxSetting.shared.getCurrentRole() // "moderator" or "participant"
EnxSetting.shared.getClientID() // Self client ID
EnxSetting.shared.getRoom() // Room instance (EnxRoom)
EnxSetting.shared.getiSKnockRoom() // true if knock/lobby is enabled
EnxSetting.shared.getisUserAwaited() // true if a user is waiting in lobby
EnxSetting.shared.getisUserInFloorReq() // true if user has requested floor