fix: snap to replied-to UX lag issue
This commit is contained in:
parent
add3d7d32c
commit
d61a9b315f
|
@ -860,20 +860,45 @@ extension ComposeViewController {
|
||||||
|
|
||||||
let repliedToCellFrame = viewModel.repliedToCellFrame.value
|
let repliedToCellFrame = viewModel.repliedToCellFrame.value
|
||||||
guard repliedToCellFrame != .zero else { return }
|
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 {
|
switch viewModel.collectionViewState.value {
|
||||||
case .fold:
|
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
|
viewModel.collectionViewState.value = .expand
|
||||||
}
|
}
|
||||||
os_log("%{public}s[%{public}ld], %{public}s: fold", ((#file as NSString).lastPathComponent), #line, #function)
|
|
||||||
|
|
||||||
case .expand:
|
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
|
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 {
|
extension ComposeViewController: UIAdaptivePresentationControllerDelegate {
|
||||||
|
|
||||||
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
|
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 {
|
func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
|
||||||
|
|
|
@ -253,6 +253,8 @@ extension ComposeViewModel: UITableViewDataSource {
|
||||||
cell.statusContentWarningEditorView.alpha = 0
|
cell.statusContentWarningEditorView.alpha = 0
|
||||||
UIView.animate(withDuration: 0.33, delay: 0, options: [.curveEaseOut]) {
|
UIView.animate(withDuration: 0.33, delay: 0, options: [.curveEaseOut]) {
|
||||||
cell.statusContentWarningEditorView.alpha = 1
|
cell.statusContentWarningEditorView.alpha = 1
|
||||||
|
tableView.beginUpdates()
|
||||||
|
tableView.endUpdates()
|
||||||
} completion: { _ in
|
} completion: { _ in
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ final class ComposeViewModel: NSObject {
|
||||||
let selectedStatusVisibility: CurrentValueSubject<ComposeToolbarView.VisibilitySelectionType, Never>
|
let selectedStatusVisibility: CurrentValueSubject<ComposeToolbarView.VisibilitySelectionType, Never>
|
||||||
let activeAuthentication: CurrentValueSubject<MastodonAuthentication?, Never>
|
let activeAuthentication: CurrentValueSubject<MastodonAuthentication?, Never>
|
||||||
let activeAuthenticationBox: CurrentValueSubject<AuthenticationService.MastodonAuthenticationBox?, 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 repliedToCellFrame = CurrentValueSubject<CGRect, Never>(.zero)
|
||||||
let autoCompleteRetryLayoutTimes = CurrentValueSubject<Int, Never>(0)
|
let autoCompleteRetryLayoutTimes = CurrentValueSubject<Int, Never>(0)
|
||||||
let autoCompleteInfo = CurrentValueSubject<ComposeViewController.AutoCompleteInfo?, Never>(nil)
|
let autoCompleteInfo = CurrentValueSubject<ComposeViewController.AutoCompleteInfo?, Never>(nil)
|
||||||
|
|
|
@ -57,29 +57,43 @@ extension StatusContentWarningEditorView {
|
||||||
containerBackgroundView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 1024),
|
containerBackgroundView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 1024),
|
||||||
containerBackgroundView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
containerBackgroundView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||||
])
|
])
|
||||||
|
|
||||||
iconImageView.translatesAutoresizingMaskIntoConstraints = false
|
let containerStackView = UIStackView()
|
||||||
addSubview(iconImageView)
|
containerStackView.axis = .horizontal
|
||||||
|
containerStackView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
addSubview(containerStackView)
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
iconImageView.centerYAnchor.constraint(equalTo: centerYAnchor),
|
containerStackView.topAnchor.constraint(equalTo: topAnchor, constant: 6),
|
||||||
iconImageView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
|
containerStackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
|
||||||
iconImageView.widthAnchor.constraint(equalToConstant: StatusView.avatarImageSize.width).priority(.defaultHigh), // center alignment to avatar
|
containerStackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
|
||||||
])
|
bottomAnchor.constraint(equalTo: containerStackView.bottomAnchor, constant: 6),
|
||||||
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),
|
|
||||||
])
|
])
|
||||||
|
|
||||||
textView.setContentHuggingPriority(.required - 1, for: .vertical)
|
containerStackView.addArrangedSubview(iconImageView)
|
||||||
textView.setContentCompressionResistancePriority(.required - 1, for: .vertical)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue