Last active
November 17, 2020 17:35
-
-
Save anandabits/5337407d8e6f69abe3044c2c6e326beb to your computer and use it in GitHub Desktop.
SwiftUI List overlay clipping
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// NOTE: behavior is exhibited in the 14.1 simulator and previews in Xcode 12.1 | |
struct OverlayRowClipping: View { | |
let rowData = (1...50).map { "row \($0)" } | |
@State var alertIsOpen = false | |
var body: some View { | |
List { | |
Section(header: Text("The Header")) { | |
ForEach(rowData, id: \.self) { item in | |
HStack { | |
Button(action: { alertIsOpen = true }) { | |
HStack { | |
Text(item) | |
Spacer() | |
} | |
// required to make the entire row respond to touch | |
.background(Color.white) | |
} | |
.border(Color.black) | |
.buttonStyle(PlainButtonStyle()) | |
HelpButton() | |
.border(Color.black) | |
} | |
} | |
} | |
} | |
.alert(isPresented: $alertIsOpen) { | |
.init(title: Text("Main row button was tapped")) | |
} | |
.listStyle(GroupedListStyle()) | |
.navigationBarTitle("Overlay Clipping", displayMode: .inline) | |
} | |
} | |
struct HelpButton: View { | |
@State var isOpen = false | |
var body: some View { | |
Button("Help") { | |
withAnimation { isOpen.toggle() } | |
} | |
// With the default (or no) button style the overlay is not clipped | |
// but touch handling is incorrect. If you tap on the left side of the | |
// Help button (roughly the H) the main row button responds to the touch. | |
// Also, the row background is highlighted when the button is tapped | |
// which is not desired in my use case. Further, I noticed that the | |
// highlighting behavior happens for **all** buttons in a row, including | |
// buttons with PlainButtonStyle, whenever **any** button in the row has | |
// the default style. This kind of nonlocal behavior seems problematic. | |
// | |
// When switching to PlainButtonStyle, touch handling and row highlighting | |
// behaves as desired, but the overlay is clipped in different ways | |
// depending on which help button is tapped. When the overlay | |
// is scrolled offscreen and back onscreen the redraw is sometimes different | |
// than the original draw. Scrolling a row off the top of the screen and | |
// back onscreen seems to "fix" the clipping the next time the overlay is | |
// displayed. Scrolling a row off the bottom of the screen and back on | |
// does the opposite, "breaking" a row where clipping was not happening. | |
.buttonStyle(PlainButtonStyle()) | |
.overlay(overlay) | |
} | |
@ViewBuilder var overlay: some View { | |
if isOpen { | |
GeometryReader { geometry in | |
Color.red | |
.frame(width: 100, height: 100) | |
.offset(x: geometry.size.width - 100, y: -100) | |
} | |
} else { | |
EmptyView() | |
} | |
} | |
} | |
struct OverlayRowClipping_Previews: PreviewProvider { | |
static var previews: some View { | |
OverlayRowClipping() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment