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:
parent
e9962997a6
commit
c6c8f63e39
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,22 +37,24 @@ struct ComposeReplyView: View {
|
||||||
|
|
||||||
Spacer()
|
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
|
ComposeReplyContentView(status: status) { newHeight in
|
||||||
self.contentHeight = newHeight
|
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()
|
||||||
|
|
|
@ -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(outerMinY: outer.frame(in: .global).minY)
|
mainStack
|
||||||
}
|
}
|
||||||
|
.coordinateSpace(name: ComposeView.coordinateSpaceOutsideOfScrollView)
|
||||||
.scrollDismissesKeyboardInteractivelyIfAvailable()
|
.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
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue