iosswiftuiscrollviewpopoverios26

iOS 26: Scroll indicator falls outside .popover content


When showing a popover:

struct ValueRowView<Content: View, PopoverContent: View> : View
{
    let label: String
    let value: Content
    @State private var showPopover: Bool = false
    let popoverContent: PopoverContent?

    init(label: String, value: Content, @ViewBuilder popoverContent: () -> PopoverContent? = { nil })
    {
        self.label = label
        self.value = value
        self.popoverContent = popoverContent()
    }

    var body: some View
    {
        HStack
        {
            VStack(alignment: .leading)
            {
                Text(label)
                    .font(.footnote)
                    .foregroundColor(.gray)

                value
            }

            Spacer()

            if let popoverContent
            {
                Button(action:
                {
                    showPopover.toggle()
                })
                {
                    Image(systemName: "info.circle")
                        .foregroundColor(.accentColor)
                }
                .popover(isPresented: $showPopover, attachmentAnchor: .point(.center))
                {
                    ScrollView //TODO: Shows scroll indicator in popover arrow; iOS 26 bug?
                    {
                        popoverContent
                            .fixedSize(horizontal: false, vertical: true)
                            .padding()
                            .presentationCompactAdaptation(.popover)
                    }
                    .frame(width: UIDevice.current.userInterfaceIdiom == .phone ? 270 : 400)
                }
            }
        }
    }
}

extension ValueRowView where PopoverContent == EmptyView
{
    init(label: String, value: Content)
    {
        self.label = label
        self.value = value
        self.popoverContent = nil
    }
}

The scroll indicator isn't visible next to the scrolled content. Instead it runs outside the popover and shows up on the popover arrow/triangle (see image).

How to prevent this?

enter image description here


Solution

  • I would agree that this looks like a small bug.

    I think what may be happening is that the scroll indicator is being shown in the trailing safe area inset. In your example, the pointer arrow is on the right side and it seems that this is also using the safe area inset. With the arrow on the right, the rest of the area for the trailing inset is masked out. So the only part of the scroll indicator that you see is the part that runs through the arrow.

    Here are two possible workarounds:

    1. Add nominal trailing padding to the ScrollView, to break contact with the safe area inset:
    .popover(isPresented: $showPopover, attachmentAnchor: .point(.center)) {
        ScrollView {
            popoverContent
                .fixedSize(horizontal: false, vertical: true)
                .padding()
                .presentationCompactAdaptation(.popover)
        }
        .padding(.trailing, 1) // 👈 here
        .frame(width: UIDevice.current.userInterfaceIdiom == .phone ? 270 : 400)
    }
    
    1. Apply .contentMargins for .scrollIndicators to the ScrollView:
    .popover(isPresented: $showPopover, attachmentAnchor: .point(.center)) {
        ScrollView {
            popoverContent
                .fixedSize(horizontal: false, vertical: true)
                .padding()
                .presentationCompactAdaptation(.popover)
        }
        .contentMargins(16, for: .scrollIndicators) // 👈 here
        .frame(width: UIDevice.current.userInterfaceIdiom == .phone ? 270 : 400)
    }
    

    Both workarounds work similarly:

    Animation

    Of course, another workaround is to move the info button to the leading side of the content. This causes the pointer arrow to switch sides, so the trailing inset is not masked out and the scroll indicator remains visible.