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,181
  • 6
  • 31
  • 55
  • 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

0

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