class CustomViewFactory: ViewFactory {
func makeOutgoingCallView(viewModel: CallViewModel) -> some View {
CustomOutgoingCallView(viewModel: viewModel)
}
}
View Customizations
Injecting Your Views
The SwiftUI SDK allows complete view swapping of some of its components. This means you can, for example, create your own (different) outgoing call view and inject it in the slot of the default one. For most of the views, the SDK doesn’t require anything else than the view to conform to the standard SwiftUI View
protocol and return a view from the body
variable. You don’t need to implement any other lifecycle related methods or additional protocol conformance.
How the View Swapping Works
All the views that allow slots that your implementations can replace are generic over those views. This means that view type erasure (AnyView) is not used. The views contain default implementations, and in general, you don’t have to deal with the generics part of the code. Using generics over type erasure allows SwiftUI to compute the diffing of the views faster and more accurately while boosting performance and correctness.
View Factory
To abstract away the creation of the views, a protocol called ViewFactory
is used in the SDK. This protocol defines the swappable views of the video experience. There are default implementations for all the views used in the SDK. If you want to customize a view, you will need to provide your own implementation of the ViewFactory
, but you will need to implement only the view you want to swap.
For example, if we want to change the outgoing call view, we will need to implement the makeOutgoingCallView(viewModel: CallViewModel) -> OutgoingCallViewType
in the ViewFactory
:
Next, when you attach the CallModifier
to your hosting view, you need to inject the newly created CustomViewFactory
. The SDK will use the views you have provided in your custom implementation, while it will default back to the ones from the SDK in the slots where you haven’t provided any implementation.
var body: some View {
YourHostingView()
.modifier(CallModifier(viewFactory: CustomViewFactory(), viewModel: viewModel))
}
Here are all the slots available for customization in the SwiftUI SDK.
Outgoing Call View
In order to swap the outgoing call view, we will need to implement the makeOutgoingCallView(viewModel: CallViewModel) -> some View
in the ViewFactory
:
class CustomViewFactory: ViewFactory {
func makeOutgoingCallView(viewModel: CallViewModel) -> some View {
CustomOutgoingCallView(viewModel: viewModel)
}
}
Incoming Call View
Similarly, the incoming call view can be replaced by implementing the makeIncomingCallView(viewModel: CallViewModel, callInfo: IncomingCall) -> some View
in the ViewFactory
:
public func makeIncomingCallView(viewModel: CallViewModel, callInfo: IncomingCall) -> some View {
CustomIncomingCallView(callInfo: callInfo, viewModel: viewModel)
}
Call View
When the call state change to .inCall
, the call view slot is shown. The default implementation provides several customizable parts, such as the video participants, the call controls (mute/unmute, hang up) and the top trailing view (which by default displays participants’ info).
In order to swap the default call view, you will need to implement the makeCallView(viewModel: CallViewModel) -> some View
:
public func makeCallView(viewModel: CallViewModel) -> some View {
CustomCallView(viewModel: viewModel)
}
Apart from the main call view, you can also swap its building blocks.
Call Controls View
The call controls view by default displays controls for hiding/showing the camera, muting/unmuting the microphone, changing the camera source (front/back) and hanging up. If you want to change these controls, you will need to implement the makeCallControlsView(viewModel: CallViewModel) -> some View
method:
func makeCallControlsView(viewModel: CallViewModel) -> some View {
CustomCallControlsView(viewModel: viewModel)
}
Video Participants View
The video participants view slot presents the grid of users that are in the call. If you want to provide a different variation of the participants display, you will need to implement the makeVideoParticipantsView
in the ViewFactory
:
public func makeVideoParticipantsView(
viewModel: CallViewModel,
availableFrame: CGRect,
onChangeTrackVisibility: @escaping @MainActor(CallParticipant, Bool) -> Void
) -> some View {
VideoParticipantsView(
viewFactory: self,
viewModel: viewModel,
availableFrame: availableFrame,
onChangeTrackVisibility: onChangeTrackVisibility
)
}
In the method, the following parameters are provided:
viewModel
- the viewModel that manages the call.availableFrame
- the available frame for the participants view.onChangeTrackVisibility
- callback when the track changes its visibility.
Video Participant View
If you want to customize one particular participant view, you can change it via the method makeVideoParticipantView
:
func makeVideoParticipantView(
participant: CallParticipant,
id: String,
availableFrame: CGRect,
contentMode: UIView.ContentMode,
customData: [String: RawJSON],
call: Call?
) -> some View {
VideoCallParticipantView(
participant: participant,
id: id,
availableFrame: availableFrame,
contentMode: contentMode,
customData: customData,
call: call
)
}
Additionally, you can change the modifier applied to the view, by implementing the makeVideoCallParticipantModifier
:
public func makeVideoCallParticipantModifier(
participant: CallParticipant,
call: Call?,
availableFrame: CGRect,
ratio: CGFloat,
showAllInfo: Bool
) -> some ViewModifier {
VideoCallParticipantModifier(
participant: participant,
call: call,
availableFrame: availableFrame,
ratio: ratio,
showAllInfo: showAllInfo
)
}
Top View
This is the view presented in the top area of the call view. By default, it displays a back button (to go in minimized mode) and a button that shows the list of participants. You can swap this view with your own implementation, by implementing the makeCallTopView
in the ViewFactory
:
public func makeCallTopView(viewModel: CallViewModel) -> some View {
CallTopView(viewModel: viewModel)
}