Fix compose reply view not working after ContentTextView refactor, use named CoordinateSpace for calculating scroll offset in reply avatar view

This commit is contained in:
Shadowfacts 2022-09-17 20:46:57 -04:00
parent e9962997a6
commit c6c8f63e39
3 changed files with 35 additions and 28 deletions

View File

@ -23,6 +23,7 @@ struct ComposeReplyContentView: UIViewRepresentable {
view.setTextFrom(status: status) view.setTextFrom(status: status)
view.isUserInteractionEnabled = false view.isUserInteractionEnabled = false
view.backgroundColor = .clear view.backgroundColor = .clear
view.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
return view return view
} }

View File

@ -11,8 +11,8 @@ import SwiftUI
struct ComposeReplyView: View { struct ComposeReplyView: View {
let status: StatusMO let status: StatusMO
let stackPadding: CGFloat let stackPadding: CGFloat
let outerMinY: CGFloat
@State private var displayNameHeight: CGFloat?
@State private var contentHeight: CGFloat? @State private var contentHeight: CGFloat?
@ObservedObject private var preferences = Preferences.shared @ObservedObject private var preferences = Preferences.shared
@ -37,23 +37,25 @@ struct ComposeReplyView: View {
Spacer() Spacer()
} }
.background(GeometryReader { proxy in
ComposeReplyContentView(status: status) { (newHeight) in Color.clear
self.contentHeight = newHeight .preference(key: DisplayNameHeightPrefKey.self, value: proxy.size.height)
.onPreferenceChange(DisplayNameHeightPrefKey.self) { newValue in
displayNameHeight = newValue
}
})
ComposeReplyContentView(status: status) { newHeight in
contentHeight = newHeight
} }
.offset(x: -4, y: -8) .frame(height: contentHeight ?? 0)
.padding(.bottom, -8)
} }
.frame(height: max(50, contentHeight ?? 0) + 8)
} }
.padding(.bottom, -8)
} }
private func replyAvatarImage(geometry: GeometryProxy) -> some View { private func replyAvatarImage(geometry: GeometryProxy) -> some View {
// using named coordinate spaces produces an incorrect scroll offset on iOS 13, let scrollOffset = -geometry.frame(in: .named(ComposeView.coordinateSpaceOutsideOfScrollView)).minY
// so simply compare the geometry inside and outside the scroll view in the global coordinate space
let scrollOffset = outerMinY - geometry.frame(in: .global).minY
// add stackPadding so that the image is always at least stackPadding away from the top // add stackPadding so that the image is always at least stackPadding away from the top
var offset = scrollOffset + stackPadding var offset = scrollOffset + stackPadding
@ -61,7 +63,7 @@ struct ComposeReplyView: View {
offset = max(offset, 0) offset = max(offset, 0)
// subtract 50, because we care about where the bottom of the view is but the offset is relative to the top of the view // subtract 50, because we care about where the bottom of the view is but the offset is relative to the top of the view
let maxOffset = (contentHeight ?? 0) - 50 let maxOffset = (contentHeight ?? 0) + (displayNameHeight ?? 0) - 50
// once you scroll past the in-reply-to-content, the bottom of the avatar should be pinned to the bottom of the content // once you scroll past the in-reply-to-content, the bottom of the avatar should be pinned to the bottom of the content
offset = min(offset, maxOffset) offset = min(offset, maxOffset)
@ -74,6 +76,13 @@ struct ComposeReplyView: View {
} }
private struct DisplayNameHeightPrefKey: PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
//struct ComposeReplyView_Previews: PreviewProvider { //struct ComposeReplyView_Previews: PreviewProvider {
// static var previews: some View { // static var previews: some View {
// ComposeReplyView() // ComposeReplyView()

View File

@ -42,6 +42,8 @@ import Combine
} }
struct ComposeView: View { struct ComposeView: View {
static let coordinateSpaceOutsideOfScrollView = "coordinateSpaceOutsideOfScrollView"
@ObservedObject var draft: Draft @ObservedObject var draft: Draft
@EnvironmentObject var mastodonController: MastodonController @EnvironmentObject var mastodonController: MastodonController
@EnvironmentObject var uiState: ComposeUIState @EnvironmentObject var uiState: ComposeUIState
@ -77,20 +79,12 @@ struct ComposeView: View {
} }
var body: some View { var body: some View {
mostOfTheBody.toolbar {
ToolbarItem(placement: .cancellationAction) { cancelButton }
ToolbarItem(placement: .confirmationAction) { postButton }
}
}
var mostOfTheBody: some View {
ZStack(alignment: .top) { ZStack(alignment: .top) {
GeometryReader { (outer) in ScrollView(.vertical) {
ScrollView(.vertical) { mainStack
mainStack(outerMinY: outer.frame(in: .global).minY)
}
.scrollDismissesKeyboardInteractivelyIfAvailable()
} }
.coordinateSpace(name: ComposeView.coordinateSpaceOutsideOfScrollView)
.scrollDismissesKeyboardInteractivelyIfAvailable()
if let poster = poster { if let poster = poster {
// can't use SwiftUI.ProgressView because there's no UIProgressView.Style.bar equivalent, see FB8587149 // can't use SwiftUI.ProgressView because there's no UIProgressView.Style.bar equivalent, see FB8587149
@ -108,6 +102,10 @@ struct ComposeView: View {
dismissButton: .default(Text("OK")) dismissButton: .default(Text("OK"))
) )
} }
.toolbar {
ToolbarItem(placement: .cancellationAction) { cancelButton }
ToolbarItem(placement: .confirmationAction) { postButton }
}
} }
@ViewBuilder @ViewBuilder
@ -122,14 +120,13 @@ struct ComposeView: View {
.animation(.default, value: uiState.autocompleteState) .animation(.default, value: uiState.autocompleteState)
} }
func mainStack(outerMinY: CGFloat) -> some View { var mainStack: some View {
VStack(alignment: .leading, spacing: 8) { VStack(alignment: .leading, spacing: 8) {
if let id = draft.inReplyToID, if let id = draft.inReplyToID,
let status = mastodonController.persistentContainer.status(for: id) { let status = mastodonController.persistentContainer.status(for: id) {
ComposeReplyView( ComposeReplyView(
status: status, status: status,
stackPadding: stackPadding, stackPadding: stackPadding
outerMinY: outerMinY
) )
} }