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.isUserInteractionEnabled = false
view.backgroundColor = .clear
view.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
return view
}

View File

@ -11,8 +11,8 @@ import SwiftUI
struct ComposeReplyView: View {
let status: StatusMO
let stackPadding: CGFloat
let outerMinY: CGFloat
@State private var displayNameHeight: CGFloat?
@State private var contentHeight: CGFloat?
@ObservedObject private var preferences = Preferences.shared
@ -37,22 +37,24 @@ struct ComposeReplyView: View {
Spacer()
}
.background(GeometryReader { proxy in
Color.clear
.preference(key: DisplayNameHeightPrefKey.self, value: proxy.size.height)
.onPreferenceChange(DisplayNameHeightPrefKey.self) { newValue in
displayNameHeight = newValue
}
})
ComposeReplyContentView(status: status) { (newHeight) in
self.contentHeight = newHeight
ComposeReplyContentView(status: status) { newHeight in
contentHeight = newHeight
}
.offset(x: -4, y: -8)
.padding(.bottom, -8)
.frame(height: contentHeight ?? 0)
}
.frame(height: max(50, contentHeight ?? 0) + 8)
}
.padding(.bottom, -8)
}
private func replyAvatarImage(geometry: GeometryProxy) -> some View {
// using named coordinate spaces produces an incorrect scroll offset on iOS 13,
// so simply compare the geometry inside and outside the scroll view in the global coordinate space
let scrollOffset = outerMinY - geometry.frame(in: .global).minY
let scrollOffset = -geometry.frame(in: .named(ComposeView.coordinateSpaceOutsideOfScrollView)).minY
// add stackPadding so that the image is always at least stackPadding away from the top
var offset = scrollOffset + stackPadding
@ -61,7 +63,7 @@ struct ComposeReplyView: View {
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
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
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 {
// static var previews: some View {
// ComposeReplyView()

View File

@ -42,6 +42,8 @@ import Combine
}
struct ComposeView: View {
static let coordinateSpaceOutsideOfScrollView = "coordinateSpaceOutsideOfScrollView"
@ObservedObject var draft: Draft
@EnvironmentObject var mastodonController: MastodonController
@EnvironmentObject var uiState: ComposeUIState
@ -77,20 +79,12 @@ struct ComposeView: View {
}
var body: some View {
mostOfTheBody.toolbar {
ToolbarItem(placement: .cancellationAction) { cancelButton }
ToolbarItem(placement: .confirmationAction) { postButton }
}
}
var mostOfTheBody: some View {
ZStack(alignment: .top) {
GeometryReader { (outer) in
ScrollView(.vertical) {
mainStack(outerMinY: outer.frame(in: .global).minY)
}
.scrollDismissesKeyboardInteractivelyIfAvailable()
ScrollView(.vertical) {
mainStack
}
.coordinateSpace(name: ComposeView.coordinateSpaceOutsideOfScrollView)
.scrollDismissesKeyboardInteractivelyIfAvailable()
if let poster = poster {
// 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"))
)
}
.toolbar {
ToolbarItem(placement: .cancellationAction) { cancelButton }
ToolbarItem(placement: .confirmationAction) { postButton }
}
}
@ViewBuilder
@ -122,14 +120,13 @@ struct ComposeView: View {
.animation(.default, value: uiState.autocompleteState)
}
func mainStack(outerMinY: CGFloat) -> some View {
var mainStack: some View {
VStack(alignment: .leading, spacing: 8) {
if let id = draft.inReplyToID,
let status = mastodonController.persistentContainer.status(for: id) {
ComposeReplyView(
status: status,
stackPadding: stackPadding,
outerMinY: outerMinY
stackPadding: stackPadding
)
}