In my CarPlaySceneDelegate.swift, I have two tabs:
- The first tab uses a
CPListImageRowItemwith aCPListImageRowItemRowElement. The scroll direction is inverted, and the side button does not function correctly. - The second tab uses multiple
CPListItemobjects. There are no issues: scrolling works in the correct direction, and the side button behaves as expected.
Steps To Reproduce
- Launch the app.
- Connect to CarPlay.
- In the first tab, scroll up and down, then use the side button to navigate.
- In the second tab, scroll up and down, then use the side button to navigate.
- As observed, the scrolling behavior is different between the two tabs.
Code Example:
import CarPlay
import UIKit
class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegate {
var interfaceController: CPInterfaceController?
func templateApplicationScene(
_ templateApplicationScene: CPTemplateApplicationScene,
didConnect interfaceController: CPInterfaceController
) {
self.interfaceController = interfaceController
downloadImageAndSetupTemplates()
}
func templateApplicationScene(
_ templateApplicationScene: CPTemplateApplicationScene,
didDisconnectInterfaceController interfaceController: CPInterfaceController
) {
self.interfaceController = nil
}
private func downloadImageAndSetupTemplates() {
let urlString = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRcYUjd1FYkF04-8Vb7PKI1mGoF2quLPHKjvnR7V4ReZR8UjW-0NJ_kC7q13eISZGoTCLHaDPVbOthhH9QNq-YA0uuSUjfAoB3PPs1aXQ&s=10"
guard let url = URL(string: urlString) else {
setupTemplates(with: UIImage(systemName: "photo")!)
return
}
URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
let image: UIImage
if let data = data, let downloaded = UIImage(data: data) {
image = downloaded
} else {
image = UIImage(systemName: "photo")!
}
DispatchQueue.main.async {
self?.setupTemplates(with: image)
}
}.resume()
}
private func setupTemplates(with image: UIImage) {
// Tab 1 : un seul CPListImageRowItem avec 12 CPListImageRowItemRowElement
let elements: [CPListImageRowItemRowElement] = (1...12).map { index in
CPListImageRowItemRowElement(image: image, title: "test \(index)", subtitle: nil)
}
let rowItem = CPListImageRowItem(text: "Images", elements: elements, allowsMultipleLines: true)
rowItem.listImageRowHandler = { item, elementIndex, completion in
print("tapped element \(elementIndex)")
completion()
}
let tab1Section = CPListSection(items: [rowItem])
let tab1Template = CPListTemplate(title: "CPListImageRowItemRowElement", sections: [tab1Section])
// Tab 2 : 12 CPListItem simples
let tab2Items: [CPListItem] = (1...12).map { index in
let item = CPListItem(text: "Item \(index)", detailText: "Detail \(index)")
item.handler = { _, completion in
print("handler Tab 2")
completion()
}
return item
}
let tab2Section = CPListSection(items: tab2Items)
let tab2Template = CPListTemplate(title: "CPListItem", sections: [tab2Section])
// CPTabBarTemplate avec les deux tabs
let tabBar = CPTabBarTemplate(templates: [tab1Template, tab2Template])
interfaceController?.setRootTemplate(tabBar, animated: true)
}
}
Here is a quick video:
Hello Enguerrand,
CarPlay is intended to support a variety of car displays with different size and programming restrictions. In your sample code, CPListImageRowItem is hardcoded to displaying twelve elements. As the documentation for CPListImageRowItem states, at runtime you should consult the maximum number of images a row could display. Since the first tab section has a single row item with a large amount of elements, it might be causing some sort of overflow issue, breaking scrolling.
Please try amending it to check against CPMaximumNumberOfGridImages and create an array of row items rather than a single one:
var rowItems: [CPListImageRowItem] = []
for startIndex in stride(from: 0, to: allElements.count, by: CPMaximumNumberOfGridImages) {
let chunk = Array(allElements[startIndex..<min(startIndex + CPMaximumNumberOfGridImages, allElements.count)])
let rowItem = CPListImageRowItem(text: "Images", elements: chunk, allowsMultipleLines: true)
rowItem.listImageRowHandler = { item, elementIndex, completion in
print("tapped element \(elementIndex)")
completion()
}
rowItems.append(rowItem)
}
Similarly in your second tab, the CPListTemplate.maximumItemCount for maximum number of list items across all sections should be checked. Please see the CarPlay Developer Guide for more details.
Edit:
Sorry, I realized that you are on iOS 26, which adds the allowsMultipleLines parameter to the initializer for CPListImageRowItem. This issue is a known bug that we are tracking with your report FB22097223, but both the root cause and the fix is fairly similar.
Previously, the value of CPMaximumNumberOfGridImages was relatively small, but has now been increased to allow the option of a single row item having multiple rows' worth of image elements. Hence, the introduction of the allowsMultipleLines option.
The root cause is still an internal rendering issue that our engineers are aware of, but you can mitigate your issue by having an array of row items instead of one large row item, like in the above solution. The only difference is instead of using CPMaximumNumberOfGridImages for max elements per row, use a small number like 6 or 7. (It all depends on the size of the vehicle displays your app will be on, the number of expected image elements in your list, and your app design.) You could also set allowsMultipleLines to false to be extra-certain that the row item is not overly large.
Design tip: try sorting your element array so that the most important ones are displayed at the beginning of the list.
Hoping this helps,
Richard Yeh Developer Technical Support