1

The zIndex property on a view inside a ForEach inside List does not seem to work whereas if you have zIndex inside a ForEach inside a ScrollView, it does.

Does not work for me:

List {
    ForEach { index
       View()
          .zIndex(index == 1 ? 1 : 0)
    }
}

Works:

VStack {
    ScrollView {
        ForEach { index
           View()
              .zIndex(index == 1 ? 1 : 0)
        }
    }
}

Does anyone have an idea why?

Brejuro
  • 3,421
  • 8
  • 34
  • 61
  • What is the `View` type you are using? – Sam Apr 09 '20 at 03:05
  • 1
    Also, what do you mean by `Does not work`? Does it not compile? crash? or just not have the expected behavior? – Sam Apr 09 '20 at 03:06
  • @Sam Sorry, to be clear the zIndex does not take effect, the "row" I'm forcing to the top zIndex does not actually go to the top. `View` is some custom view I have created – Brejuro Apr 09 '20 at 03:09
  • My guess would be a bug in SwiftUI — maybe try sticking `Group`s everywhere and see if it has an effect? – Sam Apr 09 '20 at 03:12
  • No luck with the `Group`s :( – Brejuro Apr 09 '20 at 03:22
  • It is not clear what you try to do, because List layouts row views by Y coordinate (w/o overlap), however zIndex affects Z coordinate. Would you provide more real example? – Asperi Apr 09 '20 at 05:10

1 Answers1

1

It seems zIndex is not working inside List in SwiftUI (even SwiftUI 2). One workaround could be to introspect the UITableViewCell and change its zPosition of the layer, Like the following;

 var body: some View {
    List {
        Text("First Row")
            .background(Color.green)
            .padding()
            .listRowZIndex(1)
        Text("Second Row")
            .background(Color.red)
            .padding()
            .listRowZIndex(0)
            .offset(x: 0, y: -30)
    }
 }

Then create the ViewModifier ;

struct ListRowZIndex: ViewModifier {
    
    @State var zIndex : CGFloat = 0.0
        
    func body(content: Content) -> some View {
        content.background(ListRowZindexHelperView(zIndex: zIndex))
    }
}

extension View {
    func listRowZIndex(_ index: CGFloat = 0.0) -> some View {
            self.modifier(ListRowZIndex(zIndex: index))
    }
}

Required implementation;

struct ListRowZindexHelperView: UIViewRepresentable {
    let zIndex : CGFloat
    let proxy = ListRowZindexHelperProxy(cellWrapper: UITableViewCellWrapper(cell: UITableViewCell()))
    
    func makeUIView(context: Context) -> UIView {
        return UIView()
    }

    func updateUIView(_ uiView: UIView, context: Context) {
        proxy.catchUITableViewCell(for: uiView)
        proxy.chnageZIndex(zIndex)
    }
}


class ListRowZindexHelperProxy {

    var uiTableViewCellWrapper: UITableViewCellWrapper
    
    init(cellWrapper: UITableViewCellWrapper) {
        uiTableViewCellWrapper = cellWrapper
    }

    func catchUITableViewCell(for view: UIView) {
        if let cell = view.enclosingUITableViewCell() {
            uiTableViewCellWrapper.uiTableViewCell = cell
        }
    }

    func chnageZIndex(_ zIndex: CGFloat = 0.0) {
        uiTableViewCellWrapper.uiTableViewCell.layer.zPosition = zIndex
    }
}

class UITableViewCellWrapper {
    var uiTableViewCell : UITableViewCell
    
    init(cell: UITableViewCell) {
        uiTableViewCell = cell
    }
}

extension UIView {
    func enclosingUITableViewCell() -> UITableViewCell? {
        var next: UIView? = self
        repeat {
            next = next?.superview
            if let asUITableViewCell = next as? UITableViewCell {
                return asUITableViewCell
            }
        } while next != nil
        return nil
    }
}

Result; Preview

The Second row goes behind the first row since first row has a higher zIndex now

Answer is inspired from this answer

  • Ah, the "elegance" of SwiftUI! The above is not working for me in Xcode 13.4.1, failing to walk up the view hierarchy saying that the superview of the added background view is nil – Alexandre G Jul 11 '22 at 11:07