Фикс: кастомный header на экране Request Chats — glass chevron, separator, full-width swipe back
This commit is contained in:
1290
Rosetta/Features/Chats/ChatDetail/ChatDetailViewController.swift
Normal file
1290
Rosetta/Features/Chats/ChatDetail/ChatDetailViewController.swift
Normal file
File diff suppressed because it is too large
Load Diff
@@ -82,7 +82,7 @@ final class RequestChatsController: UIViewController {
|
||||
[weak self] cell, indexPath, dialog in
|
||||
guard let self else { return }
|
||||
cell.configure(with: dialog, isSyncing: self.isSyncing)
|
||||
cell.setSeparatorHidden(indexPath.item == 0)
|
||||
cell.setSeparatorHidden(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,8 @@ final class ChatListCollectionController: UIViewController {
|
||||
|
||||
private var collectionView: UICollectionView!
|
||||
private var dataSource: UICollectionViewDiffableDataSource<Section, String>!
|
||||
/// Overscroll background — fills rubber-band area with pinnedItemBackgroundColor (Telegram parity)
|
||||
private let overscrollBackgroundView = UIView()
|
||||
private var cellRegistration: UICollectionView.CellRegistration<ChatListCell, Dialog>!
|
||||
private var requestsCellRegistration: UICollectionView.CellRegistration<ChatListRequestsCell, Int>!
|
||||
private let floatingTabBarTotalHeight: CGFloat = 72
|
||||
@@ -94,6 +96,11 @@ final class ChatListCollectionController: UIViewController {
|
||||
applyInsets()
|
||||
view.addSubview(collectionView)
|
||||
|
||||
// Overscroll background — behind cells, shows pinnedItemBackgroundColor on pull-down bounce
|
||||
overscrollBackgroundView.isUserInteractionEnabled = false
|
||||
overscrollBackgroundView.isHidden = true
|
||||
collectionView.insertSubview(overscrollBackgroundView, at: 0)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
collectionView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
@@ -155,6 +162,34 @@ final class ChatListCollectionController: UIViewController {
|
||||
reportPinnedHeaderFraction()
|
||||
}
|
||||
|
||||
private func updateOverscrollBackground() {
|
||||
let hasPin = !pinnedDialogs.isEmpty
|
||||
overscrollBackgroundView.isHidden = !hasPin
|
||||
guard hasPin, collectionView != nil else { return }
|
||||
|
||||
let isDark = traitCollection.userInterfaceStyle == .dark
|
||||
overscrollBackgroundView.backgroundColor = isDark
|
||||
? UIColor(red: 0x1C / 255, green: 0x1C / 255, blue: 0x1D / 255, alpha: 1)
|
||||
: UIColor(red: 0xF7 / 255, green: 0xF7 / 255, blue: 0xF7 / 255, alpha: 1)
|
||||
|
||||
let offset = collectionView.contentOffset.y
|
||||
let insetTop = collectionView.contentInset.top
|
||||
let overscrollAmount = -(offset + insetTop)
|
||||
let width = collectionView.bounds.width
|
||||
|
||||
if overscrollAmount > 0 {
|
||||
overscrollBackgroundView.frame = CGRect(
|
||||
x: 0, y: offset,
|
||||
width: width, height: insetTop + overscrollAmount
|
||||
)
|
||||
} else {
|
||||
overscrollBackgroundView.frame = CGRect(
|
||||
x: 0, y: -insetTop,
|
||||
width: width, height: insetTop
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func schedulePinnedHeaderFractionReport(force: Bool = false) {
|
||||
if isPinnedFractionReportScheduled { return }
|
||||
isPinnedFractionReportScheduled = true
|
||||
@@ -171,23 +206,33 @@ final class ChatListCollectionController: UIViewController {
|
||||
collectionView.window != nil,
|
||||
!pinnedDialogs.isEmpty else { return 0.0 }
|
||||
|
||||
var maxPinnedOffset: CGFloat = 0.0
|
||||
// Telegram: itemNode.frame is in VISUAL space (changes with scroll).
|
||||
// UICollectionView cell.frame is in CONTENT space (static).
|
||||
// Convert: visibleMaxY = cell.frame.maxY - contentOffset.y
|
||||
let offset = collectionView.contentOffset.y
|
||||
var maxPinnedVisibleOffset: CGFloat = 0.0
|
||||
var foundAny = false
|
||||
|
||||
for cell in collectionView.visibleCells {
|
||||
guard let indexPath = collectionView.indexPath(for: cell),
|
||||
sectionForIndexPath(indexPath) == .pinned else {
|
||||
continue
|
||||
}
|
||||
maxPinnedOffset = max(maxPinnedOffset, cell.frame.maxY)
|
||||
let visibleMaxY = cell.frame.maxY - offset
|
||||
maxPinnedVisibleOffset = max(maxPinnedVisibleOffset, visibleMaxY)
|
||||
foundAny = true
|
||||
}
|
||||
|
||||
if !foundAny { return 0.0 }
|
||||
|
||||
let viewportInsetTop = collectionView.contentInset.top
|
||||
guard viewportInsetTop > 0 else { return 0.0 }
|
||||
|
||||
if maxPinnedOffset >= viewportInsetTop {
|
||||
if maxPinnedVisibleOffset >= viewportInsetTop {
|
||||
return 1.0
|
||||
}
|
||||
|
||||
return max(0.0, min(1.0, maxPinnedOffset / viewportInsetTop))
|
||||
return max(0.0, min(1.0, maxPinnedVisibleOffset / viewportInsetTop))
|
||||
}
|
||||
|
||||
private func reportPinnedHeaderFraction(force: Bool = false) {
|
||||
@@ -216,14 +261,18 @@ final class ChatListCollectionController: UIViewController {
|
||||
let section = NSCollectionLayoutSection.list(using: listConfig, layoutEnvironment: environment)
|
||||
section.interGroupSpacing = 0
|
||||
|
||||
// Add pinned section background decoration
|
||||
// Add pinned section background decoration to pinned AND requests sections
|
||||
// (requests sit above pinned, so they share the same gray background)
|
||||
if let self,
|
||||
sectionIndex < self.dataSource?.snapshot().sectionIdentifiers.count ?? 0,
|
||||
self.dataSource?.snapshot().sectionIdentifiers[sectionIndex] == .pinned {
|
||||
let bgItem = NSCollectionLayoutDecorationItem.background(
|
||||
elementKind: PinnedSectionBackgroundView.elementKind
|
||||
)
|
||||
section.decorationItems = [bgItem]
|
||||
!self.pinnedDialogs.isEmpty {
|
||||
let sectionId = self.dataSource?.snapshot().sectionIdentifiers[sectionIndex]
|
||||
if sectionId == .pinned || sectionId == .requests {
|
||||
let bgItem = NSCollectionLayoutDecorationItem.background(
|
||||
elementKind: PinnedSectionBackgroundView.elementKind
|
||||
)
|
||||
section.decorationItems = [bgItem]
|
||||
}
|
||||
}
|
||||
|
||||
return section
|
||||
@@ -262,9 +311,8 @@ final class ChatListCollectionController: UIViewController {
|
||||
|
||||
requestsCellRegistration = UICollectionView.CellRegistration<ChatListRequestsCell, Int> {
|
||||
[weak self] cell, _, count in
|
||||
let hasPinned = !(self?.pinnedDialogs.isEmpty ?? true)
|
||||
// Requests row separator should be hidden when pinned section exists.
|
||||
cell.configure(count: count, showBottomSeparator: !hasPinned)
|
||||
// Always show separator under requests row
|
||||
cell.configure(count: count, showBottomSeparator: true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,6 +401,7 @@ final class ChatListCollectionController: UIViewController {
|
||||
|
||||
// Notify host immediately so top chrome reacts in the same frame.
|
||||
onPinnedStateChange?(!pinned.isEmpty)
|
||||
updateOverscrollBackground()
|
||||
}
|
||||
|
||||
/// Directly reconfigure only visible cells — no snapshot rebuild, no animation.
|
||||
@@ -366,7 +415,7 @@ final class ChatListCollectionController: UIViewController {
|
||||
let typingUsers = typingDialogs[dialog.opponentKey]
|
||||
chatCell.configure(with: dialog, isSyncing: isSyncing, typingUsers: typingUsers)
|
||||
} else if let reqCell = cell as? ChatListRequestsCell {
|
||||
reqCell.configure(count: requestsCount, showBottomSeparator: pinnedDialogs.isEmpty)
|
||||
reqCell.configure(count: requestsCount, showBottomSeparator: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -474,6 +523,7 @@ extension ChatListCollectionController: UICollectionViewDelegate {
|
||||
onScrollOffsetChange?(expansion)
|
||||
}
|
||||
reportPinnedHeaderFraction()
|
||||
updateOverscrollBackground()
|
||||
}
|
||||
|
||||
func scrollViewWillEndDragging(
|
||||
|
||||
@@ -66,10 +66,8 @@ private final class ChatListUIKitCoordinator {
|
||||
}
|
||||
|
||||
func setDetailPresented(_ presented: Bool) {
|
||||
DispatchQueue.main.async {
|
||||
if self.isDetailPresented.wrappedValue != presented {
|
||||
self.isDetailPresented.wrappedValue = presented
|
||||
}
|
||||
if self.isDetailPresented.wrappedValue != presented {
|
||||
self.isDetailPresented.wrappedValue = presented
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,9 +195,10 @@ final class ChatListRootViewController: UIViewController, UINavigationController
|
||||
}
|
||||
|
||||
private func updateNavigationBlurHeight() {
|
||||
let headerBottom = headerTotalHeight
|
||||
let expandedSearchHeight = searchChromeHeight * lastSearchExpansion
|
||||
navigationBlurHeightConstraint?.constant = max(0, headerBottom + expandedSearchHeight + 14.0)
|
||||
// Telegram: edgeEffectHeight = componentHeight + 14 - searchBarExpansion
|
||||
// Result: edge effect covers toolbar area + 14pt ONLY (not search bar).
|
||||
// Search bar has its own background, doesn't need blur coverage.
|
||||
navigationBlurHeightConstraint?.constant = max(0, headerTotalHeight + 14.0)
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
@@ -604,16 +603,9 @@ final class ChatListRootViewController: UIViewController, UINavigationController
|
||||
}
|
||||
|
||||
private func openChat(route: ChatRoute) {
|
||||
let detail = ChatDetailView(
|
||||
route: route,
|
||||
onPresentedChange: { [weak self] presented in
|
||||
self?.onDetailPresentedChanged?(presented)
|
||||
}
|
||||
)
|
||||
|
||||
let hosting = UIHostingController(rootView: detail.id(route.publicKey))
|
||||
hosting.view.backgroundColor = UIColor(RosettaColors.Adaptive.background)
|
||||
navigationController?.pushViewController(hosting, animated: true)
|
||||
onDetailPresentedChanged?(true) // hide tab bar BEFORE push animation
|
||||
let detail = ChatDetailViewController(route: route)
|
||||
navigationController?.pushViewController(detail, animated: true)
|
||||
}
|
||||
|
||||
private func openRequests() {
|
||||
@@ -705,9 +697,13 @@ final class ChatListRootViewController: UIViewController, UINavigationController
|
||||
willShow viewController: UIViewController,
|
||||
animated: Bool
|
||||
) {
|
||||
// Show standard nav bar for pushed screens, hide on chat list
|
||||
let isChatList = viewController === self
|
||||
navigationController.setNavigationBarHidden(isChatList, animated: animated)
|
||||
let hideNavBar = viewController === self
|
||||
|| viewController is ChatDetailViewController
|
||||
|| viewController is RequestChatsUIKitShellController
|
||||
navigationController.setNavigationBarHidden(hideNavBar, animated: animated)
|
||||
|
||||
let isPresented = navigationController.viewControllers.count > 1
|
||||
onDetailPresentedChanged?(isPresented)
|
||||
}
|
||||
|
||||
func navigationController(
|
||||
@@ -729,6 +725,14 @@ final class RequestChatsUIKitShellController: UIViewController {
|
||||
private let requestsController = RequestChatsController()
|
||||
private var observationTask: Task<Void, Never>?
|
||||
|
||||
// Custom header elements (direct subviews — glass needs this)
|
||||
private let headerBarHeight: CGFloat = 44
|
||||
private let backButton = UIControl()
|
||||
private let backGlassBackground = ChatListToolbarGlassCapsuleView()
|
||||
private let chevronLayer = CAShapeLayer()
|
||||
private let titleLabel = UILabel()
|
||||
private var addedFullWidthGesture = false
|
||||
|
||||
init(viewModel: ChatListViewModel) {
|
||||
self.viewModel = viewModel
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
@@ -741,13 +745,14 @@ final class RequestChatsUIKitShellController: UIViewController {
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.backgroundColor = UIColor(RosettaColors.Adaptive.background)
|
||||
title = "Request Chats"
|
||||
|
||||
setupCustomHeader()
|
||||
|
||||
addChild(requestsController)
|
||||
view.addSubview(requestsController.view)
|
||||
requestsController.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
requestsController.view.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
requestsController.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: headerBarHeight),
|
||||
requestsController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
requestsController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
requestsController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
@@ -765,10 +770,117 @@ final class RequestChatsUIKitShellController: UIViewController {
|
||||
render()
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
setupFullWidthSwipeBack()
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
layoutCustomHeader()
|
||||
}
|
||||
|
||||
deinit {
|
||||
observationTask?.cancel()
|
||||
}
|
||||
|
||||
// MARK: - Custom Header
|
||||
|
||||
private func setupCustomHeader() {
|
||||
// Back button — glass capsule + SVG chevron (TelegramIconPath.backChevron)
|
||||
backGlassBackground.translatesAutoresizingMaskIntoConstraints = false
|
||||
backButton.addSubview(backGlassBackground)
|
||||
NSLayoutConstraint.activate([
|
||||
backGlassBackground.topAnchor.constraint(equalTo: backButton.topAnchor),
|
||||
backGlassBackground.leadingAnchor.constraint(equalTo: backButton.leadingAnchor),
|
||||
backGlassBackground.trailingAnchor.constraint(equalTo: backButton.trailingAnchor),
|
||||
backGlassBackground.bottomAnchor.constraint(equalTo: backButton.bottomAnchor),
|
||||
])
|
||||
|
||||
// Chevron from SVG path (viewBox 11×20, same as ChatDetailView)
|
||||
let viewBox = CGSize(width: 11, height: 20)
|
||||
let iconSize = CGSize(width: 11, height: 20)
|
||||
var parser = SVGPathParser(pathData: TelegramIconPath.backChevron)
|
||||
let rawPath = parser.parse()
|
||||
let buttonSize: CGFloat = 44
|
||||
var transform = CGAffineTransform(
|
||||
scaleX: iconSize.width / viewBox.width,
|
||||
y: iconSize.height / viewBox.height
|
||||
).translatedBy(
|
||||
x: (buttonSize - iconSize.width) / 2 * (viewBox.width / iconSize.width),
|
||||
y: (buttonSize - iconSize.height) / 2 * (viewBox.height / iconSize.height)
|
||||
)
|
||||
chevronLayer.path = rawPath.copy(using: &transform)
|
||||
chevronLayer.fillColor = UIColor(RosettaColors.Adaptive.text).cgColor
|
||||
chevronLayer.frame = CGRect(x: 0, y: 0, width: buttonSize, height: buttonSize)
|
||||
backButton.layer.addSublayer(chevronLayer)
|
||||
|
||||
backButton.addTarget(self, action: #selector(backTapped), for: .touchUpInside)
|
||||
backButton.layer.zPosition = 55
|
||||
view.addSubview(backButton)
|
||||
|
||||
// Title
|
||||
titleLabel.text = "Request Chats"
|
||||
titleLabel.font = .systemFont(ofSize: 17, weight: .semibold)
|
||||
titleLabel.textColor = UIColor(RosettaColors.Adaptive.text)
|
||||
titleLabel.textAlignment = .center
|
||||
titleLabel.layer.zPosition = 55
|
||||
view.addSubview(titleLabel)
|
||||
}
|
||||
|
||||
private func layoutCustomHeader() {
|
||||
let statusBarHeight = view.safeAreaInsets.top
|
||||
let centerY = statusBarHeight + headerBarHeight * 0.5
|
||||
let sideInset: CGFloat = 16
|
||||
let buttonSize: CGFloat = 44
|
||||
|
||||
backButton.frame = CGRect(
|
||||
x: sideInset,
|
||||
y: centerY - buttonSize * 0.5,
|
||||
width: buttonSize,
|
||||
height: buttonSize
|
||||
)
|
||||
|
||||
let titleSize = titleLabel.intrinsicContentSize
|
||||
titleLabel.frame = CGRect(
|
||||
x: (view.bounds.width - titleSize.width) * 0.5,
|
||||
y: centerY - titleSize.height * 0.5,
|
||||
width: titleSize.width,
|
||||
height: titleSize.height
|
||||
)
|
||||
}
|
||||
|
||||
@objc private func backTapped() {
|
||||
navigationController?.popViewController(animated: true)
|
||||
}
|
||||
|
||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
super.traitCollectionDidChange(previousTraitCollection)
|
||||
chevronLayer.fillColor = UIColor(RosettaColors.Adaptive.text).cgColor
|
||||
}
|
||||
|
||||
// MARK: - Full-Width Swipe Back
|
||||
|
||||
private func setupFullWidthSwipeBack() {
|
||||
guard !addedFullWidthGesture else { return }
|
||||
addedFullWidthGesture = true
|
||||
|
||||
guard let nav = navigationController,
|
||||
let edgeGesture = nav.interactivePopGestureRecognizer,
|
||||
let targets = edgeGesture.value(forKey: "targets") as? NSArray,
|
||||
targets.count > 0
|
||||
else { return }
|
||||
|
||||
edgeGesture.isEnabled = true
|
||||
|
||||
let fullWidthGesture = UIPanGestureRecognizer()
|
||||
fullWidthGesture.setValue(targets, forKey: "targets")
|
||||
fullWidthGesture.delegate = self
|
||||
nav.view.addGestureRecognizer(fullWidthGesture)
|
||||
}
|
||||
|
||||
// MARK: - Observation
|
||||
|
||||
private func startObservationLoop() {
|
||||
observationTask?.cancel()
|
||||
observationTask = Task { @MainActor [weak self] in
|
||||
@@ -797,6 +909,23 @@ final class RequestChatsUIKitShellController: UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIGestureRecognizerDelegate (full-width swipe back)
|
||||
|
||||
extension RequestChatsUIKitShellController: UIGestureRecognizerDelegate {
|
||||
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
guard let pan = gestureRecognizer as? UIPanGestureRecognizer else { return false }
|
||||
let velocity = pan.velocity(in: pan.view)
|
||||
return velocity.x > 0 && abs(velocity.x) > abs(velocity.y)
|
||||
}
|
||||
|
||||
func gestureRecognizer(
|
||||
_ gestureRecognizer: UIGestureRecognizer,
|
||||
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer
|
||||
) -> Bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private final class ChatListSearchHeaderView: UIView, UITextFieldDelegate {
|
||||
|
||||
var onQueryChanged: ((String) -> Void)?
|
||||
@@ -1039,12 +1168,12 @@ private final class ChatListSearchHeaderView: UIView, UITextFieldDelegate {
|
||||
|
||||
private final class ChatListHeaderBlurView: UIView {
|
||||
|
||||
// Tint overlay — shows pinned section background color via gradient mask
|
||||
// Telegram EdgeEffectView port:
|
||||
// 1. CABackdropLayer — variable blur of content behind (radius 1.0)
|
||||
// 2. Tint overlay — color changes with pinnedFraction, alpha 0.85, gradient-masked
|
||||
private let tintView = UIView()
|
||||
private let tintMaskView = UIImageView()
|
||||
// CABackdropLayer — captures content behind and applies subtle blur
|
||||
private var backdropLayer: CALayer?
|
||||
private let fadeMaskLayer = CAGradientLayer()
|
||||
private var plainBackgroundColor: UIColor = .black
|
||||
private var pinnedBackgroundColor: UIColor = .black
|
||||
private var currentProgress: CGFloat = 0.0
|
||||
@@ -1055,7 +1184,7 @@ private final class ChatListHeaderBlurView: UIView {
|
||||
super.init(frame: frame)
|
||||
isUserInteractionEnabled = false
|
||||
|
||||
// Backdrop blur layer — very subtle (radius 1.0), no colorMatrix
|
||||
// Backdrop blur layer (Telegram: VariableBlurView maxRadius 1.0)
|
||||
if let backdrop = BackdropLayerHelper.createBackdropLayer() {
|
||||
backdrop.delegate = BackdropLayerDelegate.shared
|
||||
BackdropLayerHelper.setScale(backdrop, scale: 0.5)
|
||||
@@ -1067,14 +1196,11 @@ private final class ChatListHeaderBlurView: UIView {
|
||||
self.backdropLayer = backdrop
|
||||
}
|
||||
|
||||
// Tint view with gradient mask (for pinned section color)
|
||||
// Tint overlay with gradient mask (Telegram: contentView at alpha 0.85)
|
||||
tintView.mask = tintMaskView
|
||||
tintView.alpha = 0.85
|
||||
addSubview(tintView)
|
||||
|
||||
// Gradient fade mask on the whole view
|
||||
layer.mask = fadeMaskLayer
|
||||
|
||||
applyAdaptiveColors()
|
||||
}
|
||||
|
||||
@@ -1093,7 +1219,6 @@ private final class ChatListHeaderBlurView: UIView {
|
||||
backdropLayer?.frame = bounds
|
||||
tintView.frame = bounds
|
||||
tintMaskView.frame = bounds
|
||||
updateFadeMask()
|
||||
updateTintMask()
|
||||
}
|
||||
|
||||
@@ -1104,6 +1229,7 @@ private final class ChatListHeaderBlurView: UIView {
|
||||
updateChromeOpacity()
|
||||
}
|
||||
|
||||
// Telegram: plainBackgroundColor.mixedWith(pinnedItemBackgroundColor, alpha: pinnedFraction)
|
||||
private func updateEdgeEffectColor() {
|
||||
let effectivePinnedFraction = isSearchCurrentlyActive ? 0.0 : currentPinnedFraction
|
||||
let resolved = plainBackgroundColor.mixedWith(pinnedBackgroundColor, alpha: effectivePinnedFraction)
|
||||
@@ -1112,8 +1238,7 @@ private final class ChatListHeaderBlurView: UIView {
|
||||
|
||||
private func updateChromeOpacity() {
|
||||
let clamped = max(0.0, min(1.0, currentProgress))
|
||||
// Backdrop blur is always present — its visibility depends on content behind.
|
||||
// Tint overlay fades in with scroll progress.
|
||||
// Telegram: content alpha is always 0.85; we modulate by scroll progress
|
||||
tintView.alpha = 0.85 * clamped
|
||||
}
|
||||
|
||||
@@ -1123,17 +1248,6 @@ private final class ChatListHeaderBlurView: UIView {
|
||||
tintMaskView.image = VariableBlurEdgeView.generateEdgeGradient(baseHeight: edgeSize)
|
||||
}
|
||||
|
||||
private func updateFadeMask() {
|
||||
let height = max(1, bounds.height)
|
||||
let fadeHeight = min(54.0, height)
|
||||
let fadeStart = max(0.0, (height - fadeHeight) / height)
|
||||
fadeMaskLayer.frame = bounds
|
||||
fadeMaskLayer.startPoint = CGPoint(x: 0.5, y: 0)
|
||||
fadeMaskLayer.endPoint = CGPoint(x: 0.5, y: 1)
|
||||
fadeMaskLayer.colors = [UIColor.black.cgColor, UIColor.black.cgColor, UIColor.clear.cgColor]
|
||||
fadeMaskLayer.locations = [0, NSNumber(value: Float(fadeStart)), 1]
|
||||
}
|
||||
|
||||
func setProgress(_ progress: CGFloat, pinnedFraction: CGFloat, isSearchActive: Bool) {
|
||||
currentProgress = max(0.0, min(1.0, progress))
|
||||
currentPinnedFraction = max(0.0, min(1.0, pinnedFraction))
|
||||
|
||||
Reference in New Issue
Block a user