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:
Shadowfacts 2020-09-06 22:47:02 -04:00
parent 9dce94c014
commit 262aadf807
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
5 changed files with 16 additions and 40 deletions

View File

@ -11,24 +11,18 @@ import Combine
struct ComposeContainerView: View {
let mastodonController: MastodonController
let vcWidthSubject: PassthroughSubject<CGFloat, Never>
@ObservedObject var uiState: ComposeUIState
init(
mastodonController: MastodonController,
vcWidthSubject: PassthroughSubject<CGFloat, Never>,
uiState: ComposeUIState
) {
self.mastodonController = mastodonController
self.vcWidthSubject = vcWidthSubject
self.uiState = uiState
}
var body: some View {
ComposeView(
draft: uiState.draft,
vcWidthSubject: vcWidthSubject
)
ComposeView(draft: uiState.draft)
.environmentObject(mastodonController)
.environmentObject(uiState)
}

View File

@ -16,9 +16,6 @@ class ComposeHostingController: UIHostingController<ComposeContainerView> {
let mastodonController: MastodonController
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 }
@ -42,7 +39,6 @@ class ComposeHostingController: UIHostingController<ComposeContainerView> {
// to use as the UIHostingController type parameter
let container = ComposeContainerView(
mastodonController: mastodonController,
vcWidthSubject: widthSubject,
uiState: uiState
)
super.init(rootView: container)
@ -82,12 +78,6 @@ class ComposeHostingController: UIHostingController<ComposeContainerView> {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
widthSubject.send(view.bounds.width)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)

View File

@ -12,37 +12,33 @@ struct ComposeReplyContentView: UIViewRepresentable {
typealias UIViewType = ComposeReplyContentTextView
let status: StatusMO
let maxWidth: CGFloat
@EnvironmentObject var mastodonController: MastodonController
let heightChanged: (CGFloat) -> Void
func makeUIView(context: Context) -> ComposeReplyContentTextView {
let view = ComposeReplyContentTextView()
view.overrideMastodonController = mastodonController
view.setTextFrom(status: status)
view.isScrollEnabled = false
view.isUserInteractionEnabled = false
view.backgroundColor = .clear
view.maxWidth = maxWidth
return view
}
func updateUIView(_ uiView: ComposeReplyContentTextView, context: Context) {
uiView.constraint.constant = maxWidth
uiView.heightChanged = heightChanged
}
}
class ComposeReplyContentTextView: StatusContentTextView {
var heightChanged: ((CGFloat) -> Void)?
var maxWidth: CGFloat!
var constraint: NSLayoutConstraint!
override func layoutSubviews() {
super.layoutSubviews()
override func didMoveToSuperview() {
super.didMoveToSuperview()
translatesAutoresizingMaskIntoConstraints = false
constraint = widthAnchor.constraint(lessThanOrEqualToConstant: maxWidth)
constraint.isActive = true
heightChanged?(contentSize.height)
}
}

View File

@ -10,9 +10,10 @@ import SwiftUI
struct ComposeReplyView: View {
let status: StatusMO
let maxWidth: CGFloat
let stackPadding: CGFloat
@State private var contentHeight: CGFloat?
private let horizSpacing: CGFloat = 8
var body: some View {
@ -35,7 +36,10 @@ struct ComposeReplyView: View {
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)
.padding(.bottom, -8)
}

View File

@ -12,10 +12,8 @@ import Combine
struct ComposeView: View {
@ObservedObject var draft: Draft
let vcWidthSubject: PassthroughSubject<CGFloat, Never>
@EnvironmentObject var mastodonController: MastodonController
@State var viewControllerWidth: CGFloat = 0
@EnvironmentObject var uiState: ComposeUIState
@State var isPosting = false
@State var postProgress: Double = 0
@ -25,12 +23,8 @@ struct ComposeView: View {
private let stackPadding: CGFloat = 8
init(
draft: Draft,
vcWidthSubject: PassthroughSubject<CGFloat, Never>
) {
init(draft: Draft) {
self.draft = draft
self.vcWidthSubject = vcWidthSubject
}
var charactersRemaining: Int {
@ -73,7 +67,6 @@ struct ComposeView: View {
.coordinateSpace(name: "outer")
.onAppear(perform: self.didAppear)
.navigationBarTitle("Compose")
.onReceive(vcWidthSubject) { self.viewControllerWidth = $0 }
.actionSheet(isPresented: $uiState.isShowingSaveDraftSheet, content: self.saveAndCloseSheet)
.alert(isPresented: $isShowingPostErrorAlert) {
Alert(
@ -90,7 +83,6 @@ struct ComposeView: View {
let status = mastodonController.persistentContainer.status(for: id) {
ComposeReplyView(
status: status,
maxWidth: viewControllerWidth - (2 * stackPadding),
stackPadding: stackPadding
)
}