Manually log screen layouts in a native iOS app
By default, the Connect SDK automatically captures screen layouts when viewDidAppear is called on each view controller. For most apps this is sufficient, but there are cases where automatic capture doesn't reflect the actual state of the screen — for example, when a screen loads data asynchronously, uses animations, or displays views outside the main view hierarchy such as alerts or overlays.
This guide covers how to use the ConnectCustomEvent API to log screen layouts manually, either to supplement automatic capture or to replace it entirely.
NoteBefore you begin, make sure the Connect SDK is integrated into your app. For instructions, see Integrate the Connect SDK into a native iOS app (Swift) or Integrate the Connect SDK into a native iOS app (Objective-C).
When to use manual layout logging
Automatic layout logging captures the view controller at viewDidAppear time. This is not adequate when:
- The screen loads remote data after it appears — the layout captured at
viewDidAppearmay be empty or incomplete - UI animations need to complete before the layout is meaningful
- A
UITableVieworUICollectionViewneeds to reload its data before capture - Views outside the main view hierarchy are present — such as alerts, overlays, or views attached directly to the app window
- You need to associate a custom name with a view controller that serves multiple functions
In these cases, call the logging method at the point where the screen reaches its final state.
ImportantIf your view controller overrides
viewDidAppear, you must callsuper.viewDidAppear(animated)— otherwise automatic layout logging will not fire.
Correct:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Custom code
}- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Custom code
}Log a screen layout
After remote data loads
Call layout logging when your data fetch completes and the UI has been updated, rather than relying on viewDidAppear.
func requestCompleted(responseData: [AnyHashable: Any]) {
updateUI(responseData[productKeyKey])
hideActivityIndicator()
ConnectCustomEvent.sharedInstance().logScreenLayout(with: self)
}- (void)RESTRequestCompleted:(RESTRequest *)request
responseData:(NSDictionary *)responseData
response:(NSHTTPURLResponse *)response {
[self updateUI:[responseData objectForKey:[self productKeyKey]]];
[self hideActivityIndicator];
[[ConnectCustomEvent sharedInstance] logScreenLayoutWithViewController:self];
}After animations or table reloads
Use the andDelay parameter to wait for animations or a UITableView reload to complete before capturing. The delay is measured in seconds.
func requestCompleted(responseData: [AnyHashable: Any]) {
items = responseData[itemsKey]
itemsTable.reloadData()
hideActivityIndicator()
ConnectCustomEvent.sharedInstance().logScreenLayout(with: self, andDelay: 0.1)
}- (void)RESTRequestCompleted:(RESTRequest *)request
responseData:(NSDictionary *)responseData
response:(NSHTTPURLResponse *)response {
items = [responseData objectForKey:[self itemsKey]];
[self.itemsTable reloadData];
[self hideActivityIndicator];
[[ConnectCustomEvent sharedInstance] logScreenLayoutWithViewController:self
andDelay:0.1];
}On table row selection
Screen layout logging only captures views visible on screen at the time of the call. When a UITableView has more rows than fit on screen, call layout logging when a row is selected to ensure the captured layout matches the selected item.
func tableView(_ tableView: UITableView,
willSelectRowAt indexPath: IndexPath) -> IndexPath? {
ConnectCustomEvent.sharedInstance().logScreenLayout(with: self)
return indexPath
}- (NSIndexPath *)tableView:(UITableView *)tableView
willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[[ConnectCustomEvent sharedInstance] logScreenLayoutWithViewController:self];
return indexPath;
}With alerts and overlays
Views outside the main view hierarchy — such as UIAlertController — are not captured automatically. Pass them as related views to include them in the layout capture.
@IBAction func btnSubmitFormClick(_ sender: Any) {
let alert = UIAlertController(title: "Thank You!",
message: "We will be in touch with you soon.",
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default) { _ in
ConnectCustomEvent.sharedInstance().logScreenLayout(with: self)
})
present(alert, animated: true)
ConnectCustomEvent.sharedInstance().logScreenLayout(with: self,
andRelatedViews: [alert.view])
}- (IBAction)btnSubmitFormClick:(id)sender {
UIAlertController *alert =
[UIAlertController alertControllerWithTitle:@"Thank You!"
message:@"We will be in touch with you soon."
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"Ok"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[[ConnectCustomEvent sharedInstance] logScreenLayoutWithViewController:self];
}];
[alert addAction:ok];
[self presentViewController:alert animated:YES completion:nil];
[[ConnectCustomEvent sharedInstance] logScreenLayoutWithViewController:self
andRelatedViews:@[alert.view]];
}With a custom screen name
Use the andName parameter when a view controller serves multiple functions and you want to distinguish captures by name.
ConnectCustomEvent.sharedInstance().logScreenLayout(with: self, andName: "ProductDetail-Edit")[[ConnectCustomEvent sharedInstance] logScreenLayoutWithViewController:self
andName:@"ProductDetail-Edit"];With an image
Use logScreenLayoutWithImage when you want to log a static image as the background for a screen layout capture rather than the live view hierarchy. During replay, the image is displayed as a screen view or under Dynamic Update if the screen has already transitioned.
ConnectCustomEvent.sharedInstance().logScreenLayout(with: image)[[ConnectCustomEvent sharedInstance] logScreenLayoutWithImage:image];The method returns false if the image is nil or logging fails.
Related
Updated 4 days ago
