This commit is contained in:
Ilya Laktyushin
2022-12-03 21:57:32 +04:00
parent 9c1c901a97
commit 02e06779ef
135 changed files with 19521 additions and 711 deletions

View File

@@ -3,15 +3,18 @@ import UIKit
import Display
import ComponentFlow
import ViewControllerComponent
import SwiftSignalKit
public final class SheetComponentEnvironment: Equatable {
public let isDisplaying: Bool
public let isCentered: Bool
public let hasInputHeight: Bool
public let dismiss: (Bool) -> Void
public init(isDisplaying: Bool, isCentered: Bool, dismiss: @escaping (Bool) -> Void) {
public init(isDisplaying: Bool, isCentered: Bool, hasInputHeight: Bool, dismiss: @escaping (Bool) -> Void) {
self.isDisplaying = isDisplaying
self.isCentered = isCentered
self.hasInputHeight = hasInputHeight
self.dismiss = dismiss
}
@@ -22,6 +25,9 @@ public final class SheetComponentEnvironment: Equatable {
if lhs.isCentered != rhs.isCentered {
return false
}
if lhs.hasInputHeight != rhs.hasInputHeight {
return false
}
return true
}
}
@@ -29,11 +35,21 @@ public final class SheetComponentEnvironment: Equatable {
public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
public typealias EnvironmentType = (ChildEnvironmentType, SheetComponentEnvironment)
public enum BackgroundColor: Equatable {
public enum BlurStyle: Equatable {
case light
case dark
}
case color(UIColor)
case blur(BlurStyle)
}
public let content: AnyComponent<ChildEnvironmentType>
public let backgroundColor: UIColor
public let backgroundColor: BackgroundColor
public let animateOut: ActionSlot<Action<()>>
public init(content: AnyComponent<ChildEnvironmentType>, backgroundColor: UIColor, animateOut: ActionSlot<Action<()>>) {
public init(content: AnyComponent<ChildEnvironmentType>, backgroundColor: BackgroundColor, animateOut: ActionSlot<Action<()>>) {
self.content = content
self.backgroundColor = backgroundColor
self.animateOut = animateOut
@@ -53,20 +69,36 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
return true
}
private class ScrollView: UIScrollView {
var ignoreScroll = false
override func setContentOffset(_ contentOffset: CGPoint, animated: Bool) {
guard !self.ignoreScroll else {
return
}
if animated && abs(contentOffset.y - self.contentOffset.y) > 200.0 {
return
}
super.setContentOffset(contentOffset, animated: animated)
}
}
public final class View: UIView, UIScrollViewDelegate {
private let dimView: UIView
private let scrollView: UIScrollView
private let scrollView: ScrollView
private let backgroundView: UIView
private var effectView: UIVisualEffectView?
private let contentView: ComponentHostView<ChildEnvironmentType>
private var previousIsDisplaying: Bool = false
private var dismiss: ((Bool) -> Void)?
private var keyboardWillShowObserver: AnyObject?
override init(frame: CGRect) {
self.dimView = UIView()
self.dimView.backgroundColor = UIColor(white: 0.0, alpha: 0.4)
self.scrollView = UIScrollView()
self.scrollView = ScrollView()
self.scrollView.delaysContentTouches = false
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
self.scrollView.contentInsetAdjustmentBehavior = .never
@@ -76,7 +108,7 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
self.scrollView.alwaysBounceVertical = true
self.backgroundView = UIView()
self.backgroundView.layer.cornerRadius = 10.0
self.backgroundView.layer.cornerRadius = 12.0
self.backgroundView.layer.masksToBounds = true
self.contentView = ComponentHostView<ChildEnvironmentType>()
@@ -91,12 +123,27 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
self.addSubview(self.scrollView)
self.dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimViewTapGesture(_:))))
self.keyboardWillShowObserver = NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: nil, using: { [weak self] _ in
if let strongSelf = self {
strongSelf.scrollView.ignoreScroll = true
Queue.mainQueue().after(0.1, {
strongSelf.scrollView.ignoreScroll = false
})
}
})
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
if let keyboardFrameChangeObserver = self.keyboardWillShowObserver {
NotificationCenter.default.removeObserver(keyboardFrameChangeObserver)
}
}
@objc private func dimViewTapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.dismiss?(true)
@@ -183,8 +230,10 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
}
}
private var currentHasInputHeight = false
private var currentAvailableSize: CGSize?
func update(component: SheetComponent<ChildEnvironmentType>, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
let previousHasInputHeight = self.currentHasInputHeight
let sheetEnvironment = environment[SheetComponentEnvironment.self].value
component.animateOut.connect { [weak self] completion in
guard let strongSelf = self else {
@@ -195,10 +244,25 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
}
}
if self.backgroundView.backgroundColor != component.backgroundColor {
self.backgroundView.backgroundColor = component.backgroundColor
}
self.currentHasInputHeight = sheetEnvironment.hasInputHeight
switch component.backgroundColor {
case let .blur(style):
self.backgroundView.isHidden = true
if self.effectView == nil {
let effectView = UIVisualEffectView(effect: UIBlurEffect(style: style == .dark ? .dark : .light))
effectView.layer.cornerRadius = self.backgroundView.layer.cornerRadius
effectView.layer.masksToBounds = true
self.backgroundView.superview?.insertSubview(effectView, aboveSubview: self.backgroundView)
self.effectView = effectView
}
case let .color(color):
self.backgroundView.backgroundColor = color
self.backgroundView.isHidden = false
self.effectView?.removeFromSuperview()
self.effectView = nil
}
transition.setFrame(view: self.dimView, frame: CGRect(origin: CGPoint(), size: availableSize), completion: nil)
let containerSize: CGSize
@@ -226,9 +290,15 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
let y: CGFloat = floorToScreenPixels((availableSize.height - contentSize.height) / 2.0)
transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - contentSize.width) / 2.0), y: -y), size: contentSize), completion: nil)
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - contentSize.width) / 2.0), y: -y), size: contentSize), completion: nil)
if let effectView = self.effectView {
transition.setFrame(view: effectView, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - contentSize.width) / 2.0), y: -y), size: contentSize), completion: nil)
}
} else {
transition.setFrame(view: self.contentView, frame: CGRect(origin: .zero, size: contentSize), completion: nil)
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: .zero, size: CGSize(width: contentSize.width, height: contentSize.height + 1000.0)), completion: nil)
if let effectView = self.effectView {
transition.setFrame(view: effectView, frame: CGRect(origin: .zero, size: CGSize(width: contentSize.width, height: contentSize.height + 1000.0)), completion: nil)
}
}
transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: availableSize), completion: nil)
@@ -239,6 +309,10 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
if let currentAvailableSize = self.currentAvailableSize, currentAvailableSize.height != availableSize.height {
self.scrollView.contentOffset = CGPoint(x: 0.0, y: -(availableSize.height - contentSize.height))
}
if self.currentHasInputHeight != previousHasInputHeight {
transition.setBounds(view: self.scrollView, bounds: CGRect(origin: CGPoint(x: 0.0, y: -(availableSize.height - contentSize.height)), size: self.scrollView.bounds.size))
}
self.currentAvailableSize = availableSize
if environment[SheetComponentEnvironment.self].value.isDisplaying, !self.previousIsDisplaying, let _ = transition.userData(ViewControllerComponentContainer.AnimateInTransition.self) {