Fix very bad performance when laying out Compose reply view
Using a non-scrolling UITextView wrapped in SwiftUI combined with the old hack of fixing its layout by passing the view controller's width down to the wrapped view caused very slow layouts, resulting in significant lag when typing into the main text view of the compose screen.
This commit is contained in:
parent
9dce94c014
commit
262aadf807
|
@ -11,24 +11,18 @@ import Combine
|
||||||
|
|
||||||
struct ComposeContainerView: View {
|
struct ComposeContainerView: View {
|
||||||
let mastodonController: MastodonController
|
let mastodonController: MastodonController
|
||||||
let vcWidthSubject: PassthroughSubject<CGFloat, Never>
|
|
||||||
@ObservedObject var uiState: ComposeUIState
|
@ObservedObject var uiState: ComposeUIState
|
||||||
|
|
||||||
init(
|
init(
|
||||||
mastodonController: MastodonController,
|
mastodonController: MastodonController,
|
||||||
vcWidthSubject: PassthroughSubject<CGFloat, Never>,
|
|
||||||
uiState: ComposeUIState
|
uiState: ComposeUIState
|
||||||
) {
|
) {
|
||||||
self.mastodonController = mastodonController
|
self.mastodonController = mastodonController
|
||||||
self.vcWidthSubject = vcWidthSubject
|
|
||||||
self.uiState = uiState
|
self.uiState = uiState
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ComposeView(
|
ComposeView(draft: uiState.draft)
|
||||||
draft: uiState.draft,
|
|
||||||
vcWidthSubject: vcWidthSubject
|
|
||||||
)
|
|
||||||
.environmentObject(mastodonController)
|
.environmentObject(mastodonController)
|
||||||
.environmentObject(uiState)
|
.environmentObject(uiState)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,6 @@ class ComposeHostingController: UIHostingController<ComposeContainerView> {
|
||||||
let mastodonController: MastodonController
|
let mastodonController: MastodonController
|
||||||
|
|
||||||
let uiState: ComposeUIState
|
let uiState: ComposeUIState
|
||||||
// storing the width in the UI state and having SwiftUI listen to it via @ObservedObject doesn't work
|
|
||||||
// it ends up spinning forever
|
|
||||||
let widthSubject = PassthroughSubject<CGFloat, Never>()
|
|
||||||
|
|
||||||
var draft: Draft { uiState.draft }
|
var draft: Draft { uiState.draft }
|
||||||
|
|
||||||
|
@ -42,7 +39,6 @@ class ComposeHostingController: UIHostingController<ComposeContainerView> {
|
||||||
// to use as the UIHostingController type parameter
|
// to use as the UIHostingController type parameter
|
||||||
let container = ComposeContainerView(
|
let container = ComposeContainerView(
|
||||||
mastodonController: mastodonController,
|
mastodonController: mastodonController,
|
||||||
vcWidthSubject: widthSubject,
|
|
||||||
uiState: uiState
|
uiState: uiState
|
||||||
)
|
)
|
||||||
super.init(rootView: container)
|
super.init(rootView: container)
|
||||||
|
@ -82,12 +78,6 @@ class ComposeHostingController: UIHostingController<ComposeContainerView> {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidLayoutSubviews() {
|
|
||||||
super.viewDidLayoutSubviews()
|
|
||||||
|
|
||||||
widthSubject.send(view.bounds.width)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
|
|
|
@ -12,37 +12,33 @@ struct ComposeReplyContentView: UIViewRepresentable {
|
||||||
typealias UIViewType = ComposeReplyContentTextView
|
typealias UIViewType = ComposeReplyContentTextView
|
||||||
|
|
||||||
let status: StatusMO
|
let status: StatusMO
|
||||||
let maxWidth: CGFloat
|
|
||||||
|
|
||||||
@EnvironmentObject var mastodonController: MastodonController
|
@EnvironmentObject var mastodonController: MastodonController
|
||||||
|
|
||||||
|
let heightChanged: (CGFloat) -> Void
|
||||||
|
|
||||||
func makeUIView(context: Context) -> ComposeReplyContentTextView {
|
func makeUIView(context: Context) -> ComposeReplyContentTextView {
|
||||||
let view = ComposeReplyContentTextView()
|
let view = ComposeReplyContentTextView()
|
||||||
view.overrideMastodonController = mastodonController
|
view.overrideMastodonController = mastodonController
|
||||||
view.setTextFrom(status: status)
|
view.setTextFrom(status: status)
|
||||||
view.isScrollEnabled = false
|
|
||||||
view.isUserInteractionEnabled = false
|
view.isUserInteractionEnabled = false
|
||||||
view.backgroundColor = .clear
|
view.backgroundColor = .clear
|
||||||
view.maxWidth = maxWidth
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUIView(_ uiView: ComposeReplyContentTextView, context: Context) {
|
func updateUIView(_ uiView: ComposeReplyContentTextView, context: Context) {
|
||||||
uiView.constraint.constant = maxWidth
|
uiView.heightChanged = heightChanged
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ComposeReplyContentTextView: StatusContentTextView {
|
class ComposeReplyContentTextView: StatusContentTextView {
|
||||||
|
var heightChanged: ((CGFloat) -> Void)?
|
||||||
|
|
||||||
var maxWidth: CGFloat!
|
override func layoutSubviews() {
|
||||||
var constraint: NSLayoutConstraint!
|
super.layoutSubviews()
|
||||||
|
|
||||||
override func didMoveToSuperview() {
|
heightChanged?(contentSize.height)
|
||||||
super.didMoveToSuperview()
|
|
||||||
|
|
||||||
translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
constraint = widthAnchor.constraint(lessThanOrEqualToConstant: maxWidth)
|
|
||||||
constraint.isActive = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,10 @@ import SwiftUI
|
||||||
|
|
||||||
struct ComposeReplyView: View {
|
struct ComposeReplyView: View {
|
||||||
let status: StatusMO
|
let status: StatusMO
|
||||||
let maxWidth: CGFloat
|
|
||||||
let stackPadding: CGFloat
|
let stackPadding: CGFloat
|
||||||
|
|
||||||
|
@State private var contentHeight: CGFloat?
|
||||||
|
|
||||||
private let horizSpacing: CGFloat = 8
|
private let horizSpacing: CGFloat = 8
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
@ -35,7 +36,10 @@ struct ComposeReplyView: View {
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|
||||||
ComposeReplyContentView(status: status, maxWidth: maxWidth - 50 - horizSpacing + 4)
|
ComposeReplyContentView(status: status) { (newHeight) in
|
||||||
|
self.contentHeight = newHeight
|
||||||
|
}
|
||||||
|
.frame(height: contentHeight)
|
||||||
.offset(x: -4, y: -8)
|
.offset(x: -4, y: -8)
|
||||||
.padding(.bottom, -8)
|
.padding(.bottom, -8)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,10 +12,8 @@ import Combine
|
||||||
|
|
||||||
struct ComposeView: View {
|
struct ComposeView: View {
|
||||||
@ObservedObject var draft: Draft
|
@ObservedObject var draft: Draft
|
||||||
let vcWidthSubject: PassthroughSubject<CGFloat, Never>
|
|
||||||
|
|
||||||
@EnvironmentObject var mastodonController: MastodonController
|
@EnvironmentObject var mastodonController: MastodonController
|
||||||
@State var viewControllerWidth: CGFloat = 0
|
|
||||||
@EnvironmentObject var uiState: ComposeUIState
|
@EnvironmentObject var uiState: ComposeUIState
|
||||||
@State var isPosting = false
|
@State var isPosting = false
|
||||||
@State var postProgress: Double = 0
|
@State var postProgress: Double = 0
|
||||||
|
@ -25,12 +23,8 @@ struct ComposeView: View {
|
||||||
|
|
||||||
private let stackPadding: CGFloat = 8
|
private let stackPadding: CGFloat = 8
|
||||||
|
|
||||||
init(
|
init(draft: Draft) {
|
||||||
draft: Draft,
|
|
||||||
vcWidthSubject: PassthroughSubject<CGFloat, Never>
|
|
||||||
) {
|
|
||||||
self.draft = draft
|
self.draft = draft
|
||||||
self.vcWidthSubject = vcWidthSubject
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var charactersRemaining: Int {
|
var charactersRemaining: Int {
|
||||||
|
@ -73,7 +67,6 @@ struct ComposeView: View {
|
||||||
.coordinateSpace(name: "outer")
|
.coordinateSpace(name: "outer")
|
||||||
.onAppear(perform: self.didAppear)
|
.onAppear(perform: self.didAppear)
|
||||||
.navigationBarTitle("Compose")
|
.navigationBarTitle("Compose")
|
||||||
.onReceive(vcWidthSubject) { self.viewControllerWidth = $0 }
|
|
||||||
.actionSheet(isPresented: $uiState.isShowingSaveDraftSheet, content: self.saveAndCloseSheet)
|
.actionSheet(isPresented: $uiState.isShowingSaveDraftSheet, content: self.saveAndCloseSheet)
|
||||||
.alert(isPresented: $isShowingPostErrorAlert) {
|
.alert(isPresented: $isShowingPostErrorAlert) {
|
||||||
Alert(
|
Alert(
|
||||||
|
@ -90,7 +83,6 @@ struct ComposeView: View {
|
||||||
let status = mastodonController.persistentContainer.status(for: id) {
|
let status = mastodonController.persistentContainer.status(for: id) {
|
||||||
ComposeReplyView(
|
ComposeReplyView(
|
||||||
status: status,
|
status: status,
|
||||||
maxWidth: viewControllerWidth - (2 * stackPadding),
|
|
||||||
stackPadding: stackPadding
|
stackPadding: stackPadding
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue