fix: snap to replied-to UX lag issue

This commit is contained in:
CMK 2021-07-06 16:38:14 +08:00
parent add3d7d32c
commit d61a9b315f
4 changed files with 70 additions and 28 deletions

View File

@ -860,20 +860,45 @@ extension ComposeViewController {
let repliedToCellFrame = viewModel.repliedToCellFrame.value
guard repliedToCellFrame != .zero else { return }
let throttle = viewModel.repliedToCellFrame.value.height - scrollView.adjustedContentInset.top
// print("\(throttle) - \(scrollView.contentOffset.y)")
// try to find some patterns:
// print("""
// repliedToCellFrame: \(viewModel.repliedToCellFrame.value.height)
// scrollView.contentOffset.y: \(scrollView.contentOffset.y)
// scrollView.contentSize.height: \(scrollView.contentSize.height)
// scrollView.frame: \(scrollView.frame)
// scrollView.adjustedContentInset.top: \(scrollView.adjustedContentInset.top)
// scrollView.adjustedContentInset.bottom: \(scrollView.adjustedContentInset.bottom)
// """)
switch viewModel.collectionViewState.value {
case .fold:
if scrollView.contentOffset.y < throttle {
os_log("%{public}s[%{public}ld], %{public}s: fold", ((#file as NSString).lastPathComponent), #line, #function)
guard velocity.y < 0 else { return }
let offsetY = scrollView.contentOffset.y + scrollView.adjustedContentInset.top
if offsetY < -44 {
tableView.contentInset.top = 0
targetContentOffset.pointee = CGPoint(x: 0, y: -scrollView.adjustedContentInset.top)
viewModel.collectionViewState.value = .expand
}
os_log("%{public}s[%{public}ld], %{public}s: fold", ((#file as NSString).lastPathComponent), #line, #function)
case .expand:
if scrollView.contentOffset.y > -44 {
os_log("%{public}s[%{public}ld], %{public}s: expand", ((#file as NSString).lastPathComponent), #line, #function)
guard velocity.y > 0 else { return }
// check if top across
let topOffset = (scrollView.contentOffset.y + scrollView.adjustedContentInset.top) - repliedToCellFrame.height
// check if bottom bounce
let bottomOffsetY = scrollView.contentOffset.y + (scrollView.frame.height - scrollView.adjustedContentInset.bottom)
let bottomOffset = bottomOffsetY - scrollView.contentSize.height
if topOffset > 44 {
// do not interrupt user scrolling
viewModel.collectionViewState.value = .fold
} else if bottomOffset > 44 {
tableView.contentInset.top = -repliedToCellFrame.height
targetContentOffset.pointee = CGPoint(x: 0, y: -repliedToCellFrame.height)
viewModel.collectionViewState.value = .fold
os_log("%{public}s[%{public}ld], %{public}s: expand", ((#file as NSString).lastPathComponent), #line, #function)
}
}
}
@ -910,7 +935,8 @@ extension ComposeViewController: UICollectionViewDelegate {
extension ComposeViewController: UIAdaptivePresentationControllerDelegate {
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
return traitCollection.userInterfaceIdiom == .pad ? .formSheet : .automatic
return .fullScreen
//return traitCollection.userInterfaceIdiom == .pad ? .formSheet : .automatic
}
func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {

View File

@ -253,6 +253,8 @@ extension ComposeViewModel: UITableViewDataSource {
cell.statusContentWarningEditorView.alpha = 0
UIView.animate(withDuration: 0.33, delay: 0, options: [.curveEaseOut]) {
cell.statusContentWarningEditorView.alpha = 1
tableView.beginUpdates()
tableView.endUpdates()
} completion: { _ in
// do nothing
}

View File

@ -29,7 +29,7 @@ final class ComposeViewModel: NSObject {
let selectedStatusVisibility: CurrentValueSubject<ComposeToolbarView.VisibilitySelectionType, Never>
let activeAuthentication: CurrentValueSubject<MastodonAuthentication?, Never>
let activeAuthenticationBox: CurrentValueSubject<AuthenticationService.MastodonAuthenticationBox?, Never>
let traitCollectionDidChangePublisher = CurrentValueSubject<Void, Never>(Void()) // use CurrentValueSubject to make intial event emit
let traitCollectionDidChangePublisher = CurrentValueSubject<Void, Never>(Void()) // use CurrentValueSubject to make initial event emit
let repliedToCellFrame = CurrentValueSubject<CGRect, Never>(.zero)
let autoCompleteRetryLayoutTimes = CurrentValueSubject<Int, Never>(0)
let autoCompleteInfo = CurrentValueSubject<ComposeViewController.AutoCompleteInfo?, Never>(nil)

View File

@ -57,29 +57,43 @@ extension StatusContentWarningEditorView {
containerBackgroundView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 1024),
containerBackgroundView.bottomAnchor.constraint(equalTo: bottomAnchor),
])
iconImageView.translatesAutoresizingMaskIntoConstraints = false
addSubview(iconImageView)
let containerStackView = UIStackView()
containerStackView.axis = .horizontal
containerStackView.translatesAutoresizingMaskIntoConstraints = false
addSubview(containerStackView)
NSLayoutConstraint.activate([
iconImageView.centerYAnchor.constraint(equalTo: centerYAnchor),
iconImageView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
iconImageView.widthAnchor.constraint(equalToConstant: StatusView.avatarImageSize.width).priority(.defaultHigh), // center alignment to avatar
])
iconImageView.setContentHuggingPriority(.required - 2, for: .horizontal)
textView.translatesAutoresizingMaskIntoConstraints = false
addSubview(textView)
NSLayoutConstraint.activate([
textView.centerYAnchor.constraint(equalTo: centerYAnchor),
textView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: 6).priority(.required - 1),
textView.leadingAnchor.constraint(equalTo: iconImageView.trailingAnchor, constant: StatusView.avatarToLabelSpacing - 4), // align to name label. minus magic 4pt to remove addition inset
textView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
bottomAnchor.constraint(greaterThanOrEqualTo: textView.bottomAnchor, constant: 6).priority(.required - 1),
//textView.heightAnchor.constraint(greaterThanOrEqualToConstant: 44).priority(.defaultHigh),
containerStackView.topAnchor.constraint(equalTo: topAnchor, constant: 6),
containerStackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
containerStackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
bottomAnchor.constraint(equalTo: containerStackView.bottomAnchor, constant: 6),
])
textView.setContentHuggingPriority(.required - 1, for: .vertical)
textView.setContentCompressionResistancePriority(.required - 1, for: .vertical)
containerStackView.addArrangedSubview(iconImageView)
iconImageView.setContentHuggingPriority(.required - 1, for: .horizontal)
containerStackView.addArrangedSubview(textView)
// iconImageView.translatesAutoresizingMaskIntoConstraints = false
// addSubview(iconImageView)
// NSLayoutConstraint.activate([
// iconImageView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
// iconImageView.widthAnchor.constraint(equalToConstant: StatusView.avatarImageSize.width).priority(.defaultHigh), // center alignment to avatar
// ])
// iconImageView.setContentHuggingPriority(.required - 2, for: .horizontal)
//
// textView.translatesAutoresizingMaskIntoConstraints = false
// addSubview(textView)
// NSLayoutConstraint.activate([
// textView.centerYAnchor.constraint(equalTo: centerYAnchor),
// textView.topAnchor.constraint(equalTo: topAnchor, constant: 6),
// textView.leadingAnchor.constraint(equalTo: iconImageView.trailingAnchor, constant: StatusView.avatarToLabelSpacing - 4), // align to name label. minus magic 4pt to remove addition inset
// textView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
// bottomAnchor.constraint(equalTo: textView.bottomAnchor, constant: 6),
// textView.heightAnchor.constraint(greaterThanOrEqualToConstant: 44).priority(.defaultHigh),
// ])
//
// textView.setContentHuggingPriority(.required - 1, for: .vertical)
// textView.setContentCompressionResistancePriority(.required - 1, for: .vertical)
}
}