Various improvements

This commit is contained in:
Ilya Laktyushin 2025-10-20 00:46:26 +04:00
parent bcc1213db2
commit 35c9a5d279
17 changed files with 358 additions and 294 deletions

View file

@ -1060,14 +1060,9 @@ public class AttachmentController: ViewController, MinimizableController {
let sourceButtonScale = sourceButtonFrame.width / targetFrame.width
if let sourceGlassView = findParentGlassBackgroundView(attachmentButton), let glassParams = sourceGlassView.params {
let containerView = UIView()
containerView.clipsToBounds = true
let containerView = ClipContainerView()
containerView.update(bounds: CGRect(origin: CGPoint(x: 0.0, y: (targetFrame.height - targetFrame.width) * 0.5), size: CGSize(width: targetFrame.width, height: targetFrame.width)), topCornerRadius: targetFrame.width * 0.5, bottomCornerRadius: targetFrame.width * 0.5, boundsTransition: .immediate, cornersTransition: .immediate)
containerView.frame = targetFrame
if #available(iOS 26.0, *) {
containerView.cornerConfiguration = .uniformCorners(radius: .fixed(containerView.bounds.width * 0.5))
} else {
containerView.layer.cornerRadius = containerView.bounds.width * 0.5
}
self.view.addSubview(containerView)
let localGlassView = GlassBackgroundView()
@ -1079,7 +1074,7 @@ public class AttachmentController: ViewController, MinimizableController {
transition: .immediate
)
localGlassView.frame = CGRect(origin: .zero, size: targetFrame.size)
containerView.addSubview(localGlassView)
containerView.contentView.addSubview(localGlassView)
let initialContainerBounds = self.container.bounds
let initialContainerFrame = self.container.frame
@ -1087,10 +1082,10 @@ public class AttachmentController: ViewController, MinimizableController {
let clipInnerFrame = self.container.container.view.convert(self.container.container.view.bounds, to: self.container.view)
self.container.bounds = CGRect(origin: .zero, size: self.container.bounds.size)
self.container.frame = CGRect(origin: CGPoint(x: floorToScreenPixels ((targetFrame.width - self.container.frame.width) / 2.0), y: -clipInnerFrame.minY), size: self.container.frame.size)
self.container.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((targetFrame.width - self.container.frame.width) / 2.0), y: -clipInnerFrame.minY), size: self.container.frame.size)
self.container.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.container.bottomClipNode.cornerRadius = 0.0
containerView.addSubnode(self.container)
containerView.contentView.addSubnode(self.container)
let buttonIcon = GlassBackgroundView.ContentImageView()
let presentationData = controller.context.sharedContext.currentPresentationData.with { $0 }
@ -1104,14 +1099,8 @@ public class AttachmentController: ViewController, MinimizableController {
localGlassView.contentView.addSubview(buttonIcon)
ComponentTransition(buttonTransition).animateBlur(layer: buttonIcon.layer, fromRadius: 0.0, toRadius: 10.0)
scaleTransition.animateBounds(layer: containerView.layer, from: CGRect(origin: CGPoint(x: 0.0, y: (targetFrame.height - targetFrame.width) * 0.5), size: CGSize(width: targetFrame.width, height: targetFrame.width)))
cornersTransition.animateView {
if #available(iOS 26.0, *) {
containerView.cornerConfiguration = .corners(topLeftRadius: 38.0, topRightRadius: 38.0, bottomLeftRadius: .fixed(layout.deviceMetrics.screenCornerRadius - 2.0), bottomRightRadius: .fixed(layout.deviceMetrics.screenCornerRadius - 2.0))
} else {
containerView.layer.cornerRadius = layout.deviceMetrics.screenCornerRadius - 2.0
}
}
containerView.update(bounds: CGRect(origin: .zero, size: targetFrame.size), topCornerRadius: 38.0, bottomCornerRadius: layout.deviceMetrics.screenCornerRadius - 2.0, boundsTransition: scaleTransition, cornersTransition: cornersTransition)
scaleTransition.animateBounds(layer: containerView.layer, from: CGRect(origin: .zero, size: CGSize(width: targetFrame.width, height: targetFrame.width)))
scaleTransition.animateTransformScale(view: containerView, from: sourceButtonScale)
positionTransition.animatePosition(layer: containerView.layer, from: sourceButtonFrame.center, to: containerView.center, completion: { _ in
self.container.bottomClipNode.cornerRadius = initialBottomClipRadius
@ -1176,14 +1165,9 @@ public class AttachmentController: ViewController, MinimizableController {
let targetButtonScale = targetButtonFrame.width / initialFrame.width
if let sourceGlassView = findParentGlassBackgroundView(attachmentButton), let glassParams = sourceGlassView.params {
let containerView = UIView()
containerView.clipsToBounds = true
let containerView = ClipContainerView()
containerView.frame = initialFrame
if #available(iOS 26.0, *) {
containerView.cornerConfiguration = .corners(topLeftRadius: 38.0, topRightRadius: 38.0, bottomLeftRadius: .fixed(layout.deviceMetrics.screenCornerRadius - 2.0), bottomRightRadius: .fixed(layout.deviceMetrics.screenCornerRadius - 2.0))
} else {
containerView.layer.cornerRadius = layout.deviceMetrics.screenCornerRadius - 2.0
}
containerView.update(bounds: CGRect(origin: .zero, size: initialFrame.size), topCornerRadius: 38.0, bottomCornerRadius: layout.deviceMetrics.screenCornerRadius - 2.0, boundsTransition: .immediate, cornersTransition: .immediate)
self.view.addSubview(containerView)
let localGlassView = GlassBackgroundView()
@ -1195,14 +1179,14 @@ public class AttachmentController: ViewController, MinimizableController {
transition: .immediate
)
localGlassView.frame = CGRect(origin: .zero, size: initialFrame.size)
containerView.addSubview(localGlassView)
containerView.contentView.addSubview(localGlassView)
let clipInnerFrame = self.container.container.view.convert(self.container.container.view.bounds, to: self.container.view)
self.container.bounds = CGRect(origin: .zero, size: self.container.bounds.size)
self.container.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((initialFrame.width - self.container.frame.width) / 2.0), y: -clipInnerFrame.minY), size: self.container.frame.size)
self.container.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((initialFrame.width - self.container.frame.width) / 2.0), y: -clipInnerFrame.minY), size: self.container.frame.size)
self.container.isUserInteractionEnabled = false
self.container.view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.12, removeOnCompletion: false)
containerView.addSubnode(self.container)
containerView.contentView.addSubnode(self.container)
let buttonIcon = GlassBackgroundView.ContentImageView()
let presentationData = controller.context.sharedContext.currentPresentationData.with { $0 }
@ -1217,14 +1201,9 @@ public class AttachmentController: ViewController, MinimizableController {
ComponentTransition(buttonTransition).animateBlur(layer: buttonIcon.layer, fromRadius: 10.0, toRadius: 0.0)
ComponentTransition(buttonTransition).animateBlur(layer: sourceGlassView.contentView.layer, fromRadius: 10.0, toRadius: 0.0)
scaleTransition.updateBounds(layer: containerView.layer, bounds: CGRect(origin: CGPoint(x: 0.0, y: (initialFrame.height - initialFrame.width) * 0.5), size: CGSize(width: initialFrame.width, height: initialFrame.width)))
cornersTransition.animateView {
if #available(iOS 26.0, *) {
containerView.cornerConfiguration = .uniformCorners(radius: .fixed(containerView.bounds.width * 0.5))
} else {
containerView.layer.cornerRadius = containerView.bounds.width * 0.5
}
}
containerView.update(bounds: CGRect(origin: CGPoint(x: 0.0, y: (initialFrame.height - initialFrame.width) * 0.5), size: CGSize(width: initialFrame.width, height: initialFrame.width)), topCornerRadius: initialFrame.width * 0.5, bottomCornerRadius: initialFrame.width * 0.5, boundsTransition: scaleTransition, cornersTransition: cornersTransition)
scaleTransition.updateBounds(layer: containerView.layer, bounds: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: initialFrame.width, height: initialFrame.width)))
scaleTransition.updateTransformScale(layer: containerView.layer, scale: targetButtonScale)
positionTransition.updatePosition(layer: containerView.layer, position: targetButtonFrame.center, completion: { [weak self] _ in
let _ = self?.container.dismiss(transition: .immediate, completion: completion)
@ -1232,7 +1211,6 @@ public class AttachmentController: ViewController, MinimizableController {
})
localGlassView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, delay: 0.2, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false)
//sourceGlassView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: 0.27, timingFunction: CAMediaTimingFunctionName.linear.rawValue)
scaleTransition.animateTransformScale(view: sourceGlassView, from: 1.0 / targetButtonScale)
positionTransition.animatePosition(layer: sourceGlassView.layer, from: self.view.convert(initialFrame.center, to: sourceGlassView.superview), to: sourceGlassView.center)
@ -1712,3 +1690,36 @@ private func findParentGlassBackgroundView(_ view: UIView) -> GlassBackgroundVie
}
return nil
}
private final class ClipContainerView: UIView {
private let clipView = UIView()
let contentView = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
self.clipView.clipsToBounds = true
self.clipView.layer.cornerCurve = .continuous
self.clipView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
self.contentView.clipsToBounds = true
self.contentView.layer.cornerCurve = .continuous
self.contentView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
self.addSubview(self.clipView)
self.clipView.addSubview(self.contentView)
}
required init?(coder: NSCoder) {
preconditionFailure()
}
func update(bounds: CGRect, topCornerRadius: CGFloat, bottomCornerRadius: CGFloat, boundsTransition: ContainedViewLayoutTransition, cornersTransition: ContainedViewLayoutTransition) {
cornersTransition.updateCornerRadius(layer: self.clipView.layer, cornerRadius: topCornerRadius)
cornersTransition.updateCornerRadius(layer: self.contentView.layer, cornerRadius: bottomCornerRadius)
boundsTransition.updateFrame(view: self.clipView, frame: CGRect(origin: .zero, size: bounds.size))
boundsTransition.updatePosition(layer: self.contentView.layer, position: CGPoint(x: bounds.size.width * 0.5, y: bounds.size.height * 0.5))
boundsTransition.updateBounds(layer: self.contentView.layer, bounds: bounds)
}
}

View file

@ -156,6 +156,40 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
return true
}
}
final class BackgroundView: UIView {
let topCornersView = UIView()
let bottomCornersView = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
self.topCornersView.clipsToBounds = true
self.topCornersView.layer.cornerCurve = .continuous
self.topCornersView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
self.bottomCornersView.clipsToBounds = true
self.bottomCornersView.layer.cornerCurve = .continuous
self.bottomCornersView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
self.addSubview(self.topCornersView)
self.topCornersView.addSubview(self.bottomCornersView)
}
required init?(coder: NSCoder) {
preconditionFailure()
}
func update(size: CGSize, color: UIColor, topCornerRadius: CGFloat, bottomCornerRadius: CGFloat, transition: ComponentTransition) {
transition.setCornerRadius(layer: self.topCornersView.layer, cornerRadius: topCornerRadius)
transition.setCornerRadius(layer: self.bottomCornersView.layer, cornerRadius: bottomCornerRadius)
transition.setFrame(view: self.topCornersView, frame: CGRect(origin: .zero, size: size))
transition.setFrame(view: self.bottomCornersView, frame: CGRect(origin: .zero, size: size))
transition.setBackgroundColor(view: self.bottomCornersView, color: color)
}
}
public final class View: UIView, UIScrollViewDelegate, ComponentTaggedView {
public final class Tag {
@ -174,7 +208,7 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
private let dimView: UIView
private let scrollView: ScrollView
private let backgroundView: DynamicCornerRadiusView
private let backgroundView: BackgroundView
private var effectView: UIVisualEffectView?
private let contentView: ComponentView<ChildEnvironmentType>
private var headerView: ComponentView<Empty>?
@ -198,7 +232,7 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
self.scrollView.showsHorizontalScrollIndicator = false
self.scrollView.alwaysBounceVertical = true
self.backgroundView = DynamicCornerRadiusView()
self.backgroundView = BackgroundView()
self.contentView = ComponentView<ChildEnvironmentType>()
@ -377,21 +411,22 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
bottomCornerRadius = 12.0
}
var backgroundColor: UIColor = .clear
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.updateColor(color: color, transition: .immediate)
self.backgroundView.isHidden = false
self.effectView?.removeFromSuperview()
self.effectView = nil
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):
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)
@ -432,6 +467,7 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
}
contentView.clipsToBounds = component.clipsContent
contentView.layer.cornerRadius = topCornerRadius
if sheetEnvironment.isCentered {
let y: CGFloat = floorToScreenPixels((availableSize.height - contentSize.height) / 2.0)
transition.setFrame(view: contentView, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - contentSize.width) / 2.0), y: -y), size: contentSize), completion: nil)
@ -439,7 +475,7 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
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)
}
self.backgroundView.update(size: contentSize, corners: .init(minXMinY: topCornerRadius, maxXMinY: topCornerRadius, minXMaxY: bottomCornerRadius, maxXMaxY: bottomCornerRadius), transition: transition)
self.backgroundView.update(size: contentSize, color: backgroundColor, topCornerRadius: topCornerRadius, bottomCornerRadius: topCornerRadius, transition: transition)
} else {
switch component.style {
case .glass:
@ -452,7 +488,7 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
transition.setFrame(view: effectView, frame: CGRect(origin: .zero, size: CGSize(width: contentSize.width, height: contentSize.height + 1000.0)), completion: nil)
}
}
self.backgroundView.update(size: contentSize, corners: .init(minXMinY: topCornerRadius, maxXMinY: topCornerRadius, minXMaxY: bottomCornerRadius, maxXMaxY: bottomCornerRadius), transition: transition)
self.backgroundView.update(size: contentSize, color: backgroundColor, topCornerRadius: topCornerRadius, bottomCornerRadius: bottomCornerRadius, transition: transition)
}
}
transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: availableSize), completion: nil)

View file

@ -25,7 +25,7 @@ import EmojiStatusComponent
private let extensionImageCache = Atomic<[UInt32: UIImage]>(value: [:])
private let redColors: (UInt32, UInt32) = (0xed6b7b, 0xe63f45)
private let redColors: (UInt32, UInt32) = (0xff875f, 0xff5069)
private let greenColors: (UInt32, UInt32) = (0x99de6f, 0x5fb84f)
private let blueColors: (UInt32, UInt32) = (0x72d5fd, 0x2a9ef1)
private let yellowColors: (UInt32, UInt32) = (0xffa24b, 0xed705c)
@ -63,11 +63,22 @@ private func generateExtensionImage(colors: (UInt32, UInt32)) -> UIImage? {
context.restoreGState()
context.saveGState()
let rounded = UIBezierPath(roundedRect: CGRect(origin: .zero, size: size), cornerRadius: 13).cgPath
let full = UIBezierPath(rect: CGRect(origin: .zero, size: size)).cgPath
context.addPath(full)
context.addPath(rounded)
context.setBlendMode(.destinationOut)
context.drawPath(using: .eoFill)
context.setBlendMode(.normal)
context.restoreGState()
context.beginPath()
let _ = try? drawSvgPath(context, path: "M6,0 L26.7573593,0 C27.5530088,-8.52837125e-16 28.3160705,0.316070521 28.8786797,0.878679656 L39.1213203,11.1213203 C39.6839295,11.6839295 40,12.4469912 40,13.2426407 L40,34 C40,37.3137085 37.3137085,40 34,40 L6,40 C2.6862915,40 4.05812251e-16,37.3137085 0,34 L0,6 C-4.05812251e-16,2.6862915 2.6862915,6.08718376e-16 6,0 ")
context.clip()
context.setFillColor(UIColor(rgb: 0xffffff, alpha: 0.2).cgColor)
context.setBlendMode(.overlay)
context.setFillColor(UIColor(rgb: 0xffffff, alpha: 0.5).cgColor)
context.translateBy(x: 40.0 - 14.0, y: 0.0)
let _ = try? drawSvgPath(context, path: "M-1,0 L14,0 L14,15 L14,14 C14,12.8954305 13.1045695,12 12,12 L4,12 C2.8954305,12 2,11.1045695 2,10 L2,2 C2,0.8954305 1.1045695,-2.02906125e-16 0,0 L-1,0 L-1,0 Z ")
})
@ -1250,7 +1261,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
let iconFrame = CGRect(origin: CGPoint(x: params.leftInset + leftOffset + 12.0, y: 8.0 + verticalInset), size: iconSize)
transition.updateFrame(node: strongSelf.extensionIconNode, frame: iconFrame)
strongSelf.extensionIconNode.image = extensionIconImage
transition.updateFrame(node: strongSelf.extensionIconText, frame: CGRect(origin: CGPoint(x: iconFrame.minX + floorToScreenPixels((iconFrame.width - extensionTextLayout.size.width) / 2.0), y: iconFrame.minY + 7.0 + floorToScreenPixels((iconFrame.height - extensionTextLayout.size.height) / 2.0)), size: extensionTextLayout.size))
transition.updateFrame(node: strongSelf.extensionIconText, frame: CGRect(origin: CGPoint(x: iconFrame.minX + floorToScreenPixels((iconFrame.width - extensionTextLayout.size.width) / 2.0), y: iconFrame.minY + 5.0 + floorToScreenPixels((iconFrame.height - extensionTextLayout.size.height) / 2.0)), size: extensionTextLayout.size))
transition.updateFrame(node: strongSelf.iconStatusNode, frame: iconFrame)

View file

@ -2564,7 +2564,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att
} else {
topEdgeColor = .clear
}
let topEdgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: 100.0))
let topEdgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: 80.0))
transition.updateFrame(view: self.controllerNode.topEdgeEffectView, frame: topEdgeEffectFrame)
transition.updateAlpha(layer: self.controllerNode.topEdgeEffectView.layer, alpha: self.controllerNode.scrolledExactlyToTop && self.controllerNode.currentDisplayMode == .all ? 0.0 : 1.0)
self.controllerNode.topEdgeEffectView.update(content: topEdgeColor, blur: true, alpha: 0.8, rect: topEdgeEffectFrame, edge: .top, edgeSize: topEdgeEffectFrame.height, transition: ComponentTransition(transition))

View file

@ -399,7 +399,7 @@ public final class SegmentedControlNode: ASDisplayNode, ASGestureRecognizerDeleg
}
if !self.dividerNodes.isEmpty {
let dividerSize = CGSize(width: 1.0, height: 16.0)
let dividerSize = CGSize(width: 1.0, height: size.height - 8.0)
let delta: CGFloat = size.width / CGFloat(self.dividerNodes.count + 1)
for i in 0 ..< self.dividerNodes.count {
let dividerNode = self.dividerNodes[i]

View file

@ -797,25 +797,27 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
self.subtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
}
let chromeView: UIImageView
var chromeTransition = transition
if let current = self.chromeView {
chromeView = current
} else {
chromeTransition = .immediate
chromeView = UIImageView()
self.chromeView = chromeView
if let shimmeringView = self.shimmerView {
self.view.insertSubview(chromeView, aboveSubview: shimmeringView)
if self.glass {
let chromeView: UIImageView
var chromeTransition = transition
if let current = self.chromeView {
chromeView = current
} else {
self.view.insertSubview(chromeView, aboveSubview: self.buttonBackgroundNode.view)
chromeTransition = .immediate
chromeView = UIImageView()
self.chromeView = chromeView
if let shimmeringView = self.shimmerView {
self.view.insertSubview(chromeView, aboveSubview: shimmeringView)
} else {
self.view.insertSubview(chromeView, aboveSubview: self.buttonBackgroundNode.view)
}
chromeView.layer.compositingFilter = "overlayBlendMode"
chromeView.alpha = 0.8
chromeView.image = GlassBackgroundView.generateForegroundImage(size: CGSize(width: 26.0 * 2.0, height: 26.0 * 2.0), isDark: self.theme.backgroundColor.lightness < 0.4, fillColor: .clear)
}
chromeView.layer.compositingFilter = "overlayBlendMode"
chromeView.alpha = 0.8
chromeView.image = GlassBackgroundView.generateForegroundImage(size: CGSize(width: 26.0 * 2.0, height: 26.0 * 2.0), isDark: self.theme.backgroundColor.lightness < 0.4, fillColor: .clear)
chromeTransition.updateFrame(view: chromeView, frame: CGRect(origin: .zero, size: buttonSize))
}
chromeTransition.updateFrame(view: chromeView, frame: CGRect(origin: .zero, size: buttonSize))
return buttonSize.height
}

View file

@ -10,7 +10,6 @@ import PresentationDataUtils
import TelegramPresentationData
import TelegramUIPreferences
import AccountContext
import SearchBarNode
import MergeLists
import ChatListSearchItemHeader
import ItemListUI
@ -20,92 +19,6 @@ import ListMessageItem
import ComponentFlow
import SearchInputPanelComponent
private let searchBarFont = Font.regular(17.0)
private final class AttachmentFileSearchNavigationContentNode: NavigationBarContentNode, ItemListControllerSearchNavigationContentNode {
private var theme: PresentationTheme
private let strings: PresentationStrings
private let focus: () -> Void
private let cancel: () -> Void
private let searchBar: SearchBarNode
private var queryUpdated: ((String) -> Void)?
var activity: Bool = false {
didSet {
self.searchBar.activity = activity
}
}
init(theme: PresentationTheme, strings: PresentationStrings, focus: @escaping () -> Void, cancel: @escaping () -> Void, updateActivity: @escaping(@escaping(Bool)->Void) -> Void) {
self.theme = theme
self.strings = strings
self.focus = focus
self.cancel = cancel
self.searchBar = SearchBarNode(theme: SearchBarNodeTheme(theme: theme, hasSeparator: false), strings: strings, fieldStyle: .modern, displayBackground: false)
super.init()
//self.addSubnode(self.searchBar)
self.searchBar.cancel = { [weak self] in
self?.searchBar.deactivate(clear: false)
self?.cancel()
}
self.searchBar.textUpdated = { [weak self] query, _ in
self?.queryUpdated?(query)
}
self.searchBar.focusUpdated = { [weak self] focus in
if focus {
self?.focus()
}
}
updateActivity({ [weak self] value in
self?.activity = value
})
self.updatePlaceholder()
}
func setQueryUpdated(_ f: @escaping (String) -> Void) {
self.queryUpdated = f
}
func updateTheme(_ theme: PresentationTheme) {
self.theme = theme
self.searchBar.updateThemeAndStrings(theme: SearchBarNodeTheme(theme: self.theme), strings: self.strings)
self.updatePlaceholder()
}
func updatePlaceholder() {
self.searchBar.placeholderString = NSAttributedString(string: self.strings.Attachment_FilesSearchPlaceholder, font: searchBarFont, textColor: self.theme.rootController.navigationSearchBar.inputPlaceholderTextColor)
}
override var nominalHeight: CGFloat {
return 56.0
}
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
let searchBarFrame = CGRect(origin: CGPoint(x: 0.0, y: size.height - self.nominalHeight), size: CGSize(width: size.width, height: 56.0))
self.searchBar.frame = searchBarFrame
self.searchBar.updateLayout(boundingSize: searchBarFrame.size, leftInset: leftInset, rightInset: rightInset, transition: transition)
}
func activate() {
//self.searchBar.activate()
}
func deactivate() {
//self.searchBar.deactivate(clear: false)
}
}
final class AttachmentFileSearchItem: ItemListControllerSearch {
let context: AccountContext
let presentationData: PresentationData
@ -153,15 +66,6 @@ final class AttachmentFileSearchItem: ItemListControllerSearch {
func titleContentNode(current: (NavigationBarContentNode & ItemListControllerSearchNavigationContentNode)?) -> (NavigationBarContentNode & ItemListControllerSearchNavigationContentNode)? {
return nil
// let presentationData = self.presentationData
// if let current = current as? AttachmentFileSearchNavigationContentNode {
// current.updateTheme(presentationData.theme)
// return current
// } else {
// return AttachmentFileSearchNavigationContentNode(theme: presentationData.theme, strings: presentationData.strings, focus: self.focus, cancel: self.cancel, updateActivity: { [weak self] value in
// self?.updateActivity = value
// })
// }
}
func node(current: ItemListControllerSearchNode?, titleContentNode: (NavigationBarContentNode & ItemListControllerSearchNavigationContentNode)?) -> ItemListControllerSearchNode {
@ -195,7 +99,6 @@ private final class AttachmentFileSearchItemNode: ItemListControllerSearchNode {
send(message)
}, updateActivity: updateActivity)
super.init()
self.addSubnode(self.containerNode)
@ -346,7 +249,7 @@ private final class AttachmentFileSearchEntry: Comparable, Identifiable {
interaction.send(message)
return false
}, openMessageContextMenu: { _, _, _, _, _ in }, toggleMessagesSelection: { _, _ in }, openUrl: { _, _, _, _ in }, openInstantPage: { _, _ in }, longTap: { _, _ in }, getHiddenMedia: { return [:] })
return ListMessageItem(presentationData: ChatPresentationData(presentationData: interaction.context.sharedContext.currentPresentationData.with({$0})), context: interaction.context, chatLocation: .peer(id: PeerId(0)), interaction: itemInteraction, message: message, selection: .none, displayHeader: true, displayFileInfo: false, displayBackground: true, style: .plain)
return ListMessageItem(presentationData: ChatPresentationData(presentationData: interaction.context.sharedContext.currentPresentationData.with({$0})), systemStyle: .glass, context: interaction.context, chatLocation: .peer(id: PeerId(0)), interaction: itemInteraction, message: message, selection: .none, displayHeader: true, displayFileInfo: false, displayBackground: true, style: .plain)
}
}
@ -412,7 +315,7 @@ public final class AttachmentFileSearchContainerNode: SearchDisplayControllerCon
self.presentationDataPromise = Promise(self.presentationData)
self.dimNode = ASDisplayNode()
self.dimNode.backgroundColor = .clear // UIColor.black.withAlphaComponent(0.5)
self.dimNode.backgroundColor = .clear
self.listNode = ListView()
self.listNode.accessibilityPageScrolledString = { row, count in
@ -435,7 +338,6 @@ public final class AttachmentFileSearchContainerNode: SearchDisplayControllerCon
self.listNode.backgroundColor = self.presentationData.theme.chatList.backgroundColor
self.listNode.alpha = 0.0
//self.listNode.isHidden = true
self._hasDim = true
@ -488,7 +390,7 @@ public final class AttachmentFileSearchContainerNode: SearchDisplayControllerCon
index += 1
}
} else {
for _ in 0 ..< 2 {
for _ in 0 ..< 16 {
entries.append(AttachmentFileSearchEntry(index: index, message: nil))
index += 1
}

View file

@ -141,7 +141,7 @@ private final class ChatScheduleTimeSheetContentComponent: Component {
size: barButtonSize,
backgroundColor: environment.theme.rootController.navigationBar.glassBarButtonBackgroundColor,
isDark: environment.theme.overallDarkAppearance,
state: .glass,
state: .generic,
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
BundleIconComponent(
name: "Navigation/Close",

View file

@ -112,7 +112,7 @@ private final class SheetContent: CombinedComponent {
size: CGSize(width: 40.0, height: 40.0),
backgroundColor: theme.rootController.navigationBar.glassBarButtonBackgroundColor,
isDark: theme.overallDarkAppearance,
state: .tintedGlass,
state: .generic,
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
BundleIconComponent(
name: "Navigation/Close",

View file

@ -283,7 +283,7 @@ final class ThemeGridControllerNode: ASDisplayNode {
self.descriptionItem = ItemListTextItem(presentationData: ItemListPresentationData(presentationData), text: .plain(descriptionText), sectionId: 0)
self.resetItemNode = ItemListActionItemNode()
self.resetItem = ItemListActionItem(presentationData: ItemListPresentationData(presentationData), title: presentationData.strings.Wallpaper_ResetWallpapers, kind: .generic, alignment: .natural, sectionId: 0, style: .blocks, action: {
self.resetItem = ItemListActionItem(presentationData: ItemListPresentationData(presentationData), systemStyle: .glass, title: presentationData.strings.Wallpaper_ResetWallpapers, kind: .generic, alignment: .natural, sectionId: 0, style: .blocks, action: {
resetWallpapers()
})
self.resetDescriptionItemNode = ItemListTextItemNode()

View file

@ -30,7 +30,7 @@ swift_library(
"//submodules/UndoUI",
"//submodules/TextFormat",
"//submodules/Components/BundleIconComponent",
"//submodules/Components/SolidRoundedButtonComponent",
"//submodules/TelegramUI/Components/ButtonComponent",
"//submodules/AvatarNode",
"//submodules/TelegramUI/Components/Stars/StarsImageComponent",
"//submodules/TelegramUI/Components/Stars/StarsAvatarComponent",
@ -38,6 +38,7 @@ swift_library(
"//submodules/TelegramUI/Components/MiniAppListScreen",
"//submodules/TelegramUI/Components/Premium/PremiumStarComponent",
"//submodules/TelegramUI/Components/Gifts/GiftAnimationComponent",
"//submodules/TelegramUI/Components/GlassBarButtonComponent",
],
visibility = [
"//visibility:public",

View file

@ -14,7 +14,7 @@ import SheetComponent
import MultilineTextComponent
import MultilineTextWithEntitiesComponent
import BundleIconComponent
import SolidRoundedButtonComponent
import ButtonComponent
import Markdown
import BalancedTextComponent
import AvatarNode
@ -27,6 +27,7 @@ import StarsAvatarComponent
import MiniAppListScreen
import PremiumStarComponent
import GiftAnimationComponent
import GlassBarButtonComponent
private final class StarsTransactionSheetContent: CombinedComponent {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -86,8 +87,6 @@ private final class StarsTransactionSheetContent: CombinedComponent {
var peerMap: [EnginePeer.Id: EnginePeer] = [:]
var cachedCloseImage: (UIImage, PresentationTheme)?
var cachedOverlayCloseImage: UIImage?
var cachedChevronImage: (UIImage, PresentationTheme)?
var inProgress = false
@ -153,7 +152,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
}
static var body: Body {
let closeButton = Child(Button.self)
let closeButton = Child(GlassBarButtonComponent.self)
let title = Child(MultilineTextComponent.self)
let star = Child(StarsImageComponent.self)
let activeStar = Child(PremiumStarComponent.self)
@ -165,8 +164,8 @@ private final class StarsTransactionSheetContent: CombinedComponent {
let table = Child(TableComponent.self)
let additional = Child(BalancedTextComponent.self)
let status = Child(BalancedTextComponent.self)
let cancelButton = Child(SolidRoundedButtonComponent.self)
let button = Child(SolidRoundedButtonComponent.self)
let cancelButton = Child(ButtonComponent.self)
let button = Child(ButtonComponent.self)
let transactionStatusBackgound = Child(RoundedRectangle.self)
let transactionStatusText = Child(MultilineTextComponent.self)
@ -189,23 +188,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
let textSideInset: CGFloat = 32.0 + environment.safeInsets.left
let closeImage: UIImage
if let (image, theme) = state.cachedCloseImage, theme === environment.theme {
closeImage = image
} else {
closeImage = generateCloseButtonImage(backgroundColor: UIColor(rgb: 0x808084, alpha: 0.1), foregroundColor: theme.actionSheet.inputClearButtonColor)!
state.cachedCloseImage = (closeImage, theme)
}
let closeOverlayImage: UIImage
if let image = state.cachedOverlayCloseImage {
closeOverlayImage = image
} else {
closeOverlayImage = generateCloseButtonImage(backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.1), foregroundColor: .white)!
state.cachedOverlayCloseImage = closeOverlayImage
}
let titleText: String
let amountText: String
var descriptionText: String
@ -655,15 +638,20 @@ private final class StarsTransactionSheetContent: CombinedComponent {
descriptionText = modifiedString
}
var closeButtonImage = closeImage
if case .unique = giftAnimationSubject {
closeButtonImage = closeOverlayImage
}
let closeButton = closeButton.update(
component: Button(
content: AnyComponent(Image(image: closeButtonImage)),
action: { [weak component] in
component?.cancel(true)
component: GlassBarButtonComponent(
size: CGSize(width: 40.0, height: 40.0),
backgroundColor: theme.rootController.navigationBar.glassBarButtonBackgroundColor,
isDark: theme.overallDarkAppearance,
state: .generic,
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
BundleIconComponent(
name: "Navigation/Close",
tintColor: theme.rootController.navigationBar.glassBarButtonForegroundColor
)
)),
action: { _ in
component.cancel(true)
}
),
availableSize: CGSize(width: 30.0, height: 30.0),
@ -1615,18 +1603,39 @@ private final class StarsTransactionSheetContent: CombinedComponent {
if let cancelButtonText {
let cancelButton = cancelButton.update(
component: SolidRoundedButtonComponent(
title: cancelButtonText,
theme: SolidRoundedButtonComponent.Theme(backgroundColor: .clear, foregroundColor: linkColor),
font: .regular,
fontSize: 17.0,
height: 50.0,
cornerRadius: 10.0,
gloss: false,
iconName: nil,
animationName: nil,
iconPosition: .left,
isLoading: state.inProgress,
// component: SolidRoundedButtonComponent(
// title: cancelButtonText,
// theme: SolidRoundedButtonComponent.Theme(backgroundColor: .clear, foregroundColor: linkColor),
// font: .regular,
// fontSize: 17.0,
// height: 50.0,
// cornerRadius: 10.0,
// gloss: false,
// iconName: nil,
// animationName: nil,
// iconPosition: .left,
// isLoading: state.inProgress,
// action: {
// component.cancel(true)
// if isSubscription {
// component.updateSubscription()
// }
// }
// ),
component: ButtonComponent(
background: ButtonComponent.Background(
style: .glass,
color: theme.list.itemCheckColors.fillColor,
foreground: theme.list.itemCheckColors.foregroundColor,
pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9),
cornerRadius: 10.0,
),
content: AnyComponentWithIdentity(
id: AnyHashable(0),
component: AnyComponent(MultilineTextComponent(text: .plain(NSMutableAttributedString(string: cancelButtonText, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
),
isEnabled: true,
displaysProgress: state.inProgress,
action: {
component.cancel(true)
if isSubscription {
@ -1634,13 +1643,12 @@ private final class StarsTransactionSheetContent: CombinedComponent {
}
}
),
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
availableSize: CGSize(width: context.availableSize.width - 30.0 * 2.0, height: 52.0),
transition: context.transition
)
let cancelButtonFrame = CGRect(origin: CGPoint(x: sideInset, y: originY), size: cancelButton.size)
context.add(cancelButton
.position(CGPoint(x: cancelButtonFrame.midX, y: cancelButtonFrame.midY))
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + cancelButton.size.height / 2.0))
)
originY += cancelButton.size.height
originY += 8.0
@ -1648,18 +1656,20 @@ private final class StarsTransactionSheetContent: CombinedComponent {
if let buttonText {
let button = button.update(
component: SolidRoundedButtonComponent(
title: buttonText,
theme: SolidRoundedButtonComponent.Theme(theme: theme),
font: .bold,
fontSize: 17.0,
height: 50.0,
cornerRadius: 10.0,
gloss: false,
iconName: nil,
animationName: nil,
iconPosition: .left,
isLoading: state.inProgress,
component: ButtonComponent(
background: ButtonComponent.Background(
style: .glass,
color: theme.list.itemCheckColors.fillColor,
foreground: theme.list.itemCheckColors.foregroundColor,
pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9),
cornerRadius: 10.0,
),
content: AnyComponentWithIdentity(
id: AnyHashable(0),
component: AnyComponent(MultilineTextComponent(text: .plain(NSMutableAttributedString(string: buttonText, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
),
isEnabled: true,
displaysProgress: state.inProgress,
action: {
component.cancel(true)
if isSubscription && cancelButtonText == nil {
@ -1667,20 +1677,19 @@ private final class StarsTransactionSheetContent: CombinedComponent {
}
}
),
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
availableSize: CGSize(width: context.availableSize.width - 30.0 * 2.0, height: 52.0),
transition: context.transition
)
let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: originY), size: button.size)
context.add(button
.position(CGPoint(x: buttonFrame.midX, y: buttonFrame.midY))
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + button.size.height / 2.0))
)
originY += button.size.height
originY += 7.0
}
context.add(closeButton
.position(CGPoint(x: context.availableSize.width - environment.safeInsets.left - closeButton.size.width, y: 28.0))
.position(CGPoint(x: 16.0 + closeButton.size.width / 2.0, y: 16.0 + closeButton.size.height / 2.0))
)
let effectiveBottomInset: CGFloat = environment.metrics.isTablet ? 0.0 : environment.safeInsets.bottom
@ -1773,6 +1782,7 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
updateSubscription: context.component.updateSubscription,
sendGift: context.component.sendGift
)),
style: .glass,
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
followContentSizeChanges: true,
clipsContent: true,

View file

@ -19,7 +19,7 @@ swift_library(
"//submodules/Components/ViewControllerComponent:ViewControllerComponent",
"//submodules/Components/ComponentDisplayAdapters:ComponentDisplayAdapters",
"//submodules/Components/MultilineTextComponent:MultilineTextComponent",
"//submodules/Components/SolidRoundedButtonComponent",
"//submodules/TelegramUI/Components/ButtonComponent",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/AccountContext:AccountContext",
"//submodules/AppBundle:AppBundle",

View file

@ -51,12 +51,50 @@ final class SegmentControlComponent: Component {
return true
}
class SegmentedControlView: UISegmentedControl {
var foregroundColor: UIColor? {
didSet {
self.resetChrome()
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
self.resetChrome()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
self.resetChrome()
}
func resetChrome() {
for case let view as UIImageView in self.subviews {
view.isHidden = true
}
if let selectorView = self.subviews.last, let loupe = selectorView.subviews.first, let innerLoupe = loupe.subviews.first {
for view in innerLoupe.subviews {
if type(of: view) == UIView.self {
view.backgroundColor = self.foregroundColor
}
}
}
}
override func layoutSubviews() {
super.layoutSubviews()
self.resetChrome()
}
}
class View: UIView {
private let title = ComponentView<Empty>()
private var component: SegmentControlComponent?
private var segmentedNode: SegmentedControlNode?
private var nativeSegmentedView: SegmentedControlView?
private var legacySegmentedNode: SegmentedControlNode?
override init(frame: CGRect) {
super.init(frame: frame)
@ -70,34 +108,73 @@ final class SegmentControlComponent: Component {
let themeUpdated = self.component?.theme !== component.theme
self.component = component
let segmentedNode: SegmentedControlNode
if let current = self.segmentedNode {
segmentedNode = current
var controlSize = CGSize()
if #available(iOS 26.0, *) {
let segmentedView: SegmentedControlView
if let current = self.nativeSegmentedView {
segmentedView = current
} else {
let mappedActions: [UIAction] = component.items.map { item -> UIAction in
return UIAction(title: item.title, handler: { [weak self] _ in
guard let self, let component = self.component else {
return
}
component.action(item.id)
})
}
segmentedView = SegmentedControlView(frame: .zero, actions: mappedActions)
segmentedView.selectedSegmentIndex = component.items.firstIndex(where: { $0.id == component.selectedId }) ?? 0
self.nativeSegmentedView = segmentedView
self.addSubview(segmentedView)
}
if themeUpdated {
segmentedNode.updateTheme(SegmentedControlTheme(theme: component.theme))
let backgroundColor = component.theme.overallDarkAppearance ? component.theme.list.itemBlocksBackgroundColor : component.theme.rootController.navigationBar.segmentedBackgroundColor
segmentedView.setTitleTextAttributes([
.font: UIFont.boldSystemFont(ofSize: 15.0),
.foregroundColor: component.theme.rootController.navigationBar.segmentedTextColor
], for: .normal)
segmentedView.foregroundColor = component.theme.rootController.navigationBar.segmentedForegroundColor
segmentedView.backgroundColor = backgroundColor
}
} else {
let mappedItems: [SegmentedControlItem] = component.items.map { item -> SegmentedControlItem in
return SegmentedControlItem(title: item.title)
}
segmentedNode = SegmentedControlNode(theme: SegmentedControlTheme(theme: component.theme), items: mappedItems, selectedIndex: component.items.firstIndex(where: { $0.id == component.selectedId }) ?? 0)
self.segmentedNode = segmentedNode
self.addSubnode(segmentedNode)
segmentedNode.selectedIndexChanged = { [weak self] index in
guard let self, let component = self.component else {
return
controlSize = segmentedView.sizeThatFits(availableSize)
controlSize.width = min(availableSize.width - 32.0, max(300.0, controlSize.width))
controlSize.height = 36.0
segmentedView.frame = CGRect(origin: .zero, size: controlSize)
} else {
let segmentedNode: SegmentedControlNode
if let current = self.legacySegmentedNode {
segmentedNode = current
if themeUpdated {
let backgroundColor = component.theme.overallDarkAppearance ? component.theme.list.itemBlocksBackgroundColor : component.theme.rootController.navigationBar.segmentedBackgroundColor
let controlTheme = SegmentedControlTheme(backgroundColor: backgroundColor, foregroundColor: component.theme.rootController.navigationBar.segmentedForegroundColor, shadowColor: .clear, textColor: component.theme.rootController.navigationBar.segmentedTextColor, dividerColor: component.theme.rootController.navigationBar.segmentedDividerColor)
segmentedNode.updateTheme(controlTheme)
}
} else {
let mappedItems: [SegmentedControlItem] = component.items.map { item -> SegmentedControlItem in
return SegmentedControlItem(title: item.title)
}
let backgroundColor = component.theme.overallDarkAppearance ? component.theme.list.itemBlocksBackgroundColor : component.theme.rootController.navigationBar.segmentedBackgroundColor
let controlTheme = SegmentedControlTheme(backgroundColor: backgroundColor, foregroundColor: component.theme.rootController.navigationBar.segmentedForegroundColor, shadowColor: .clear, textColor: component.theme.rootController.navigationBar.segmentedTextColor, dividerColor: component.theme.rootController.navigationBar.segmentedDividerColor)
segmentedNode = SegmentedControlNode(theme: controlTheme, items: mappedItems, selectedIndex: component.items.firstIndex(where: { $0.id == component.selectedId }) ?? 0, cornerRadius: 18.0)
self.legacySegmentedNode = segmentedNode
self.addSubnode(segmentedNode)
segmentedNode.selectedIndexChanged = { [weak self] index in
guard let self, let component = self.component else {
return
}
component.action(component.items[index].id)
}
self.component?.action(component.items[index].id)
}
controlSize = segmentedNode.updateLayout(SegmentedControlLayout.sizeToFit(maximumWidth: availableSize.width, minimumWidth: min(availableSize.width, 300.0), height: 36.0), transition: transition.containedViewLayoutTransition)
transition.containedViewLayoutTransition.updateFrame(node: segmentedNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: controlSize))
}
let controlSize = segmentedNode.updateLayout(SegmentedControlLayout.sizeToFit(maximumWidth: availableSize.width, minimumWidth: min(availableSize.width, 296.0), height: 31.0), transition: transition.containedViewLayoutTransition)
transition.containedViewLayoutTransition.updateFrame(node: segmentedNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: controlSize))
return controlSize
}
}

View file

@ -12,7 +12,7 @@ import TelegramCore
import MultilineTextComponent
import EmojiStatusComponent
import CheckNode
import SolidRoundedButtonComponent
import ButtonComponent
final class StorageCategoriesComponent: Component {
struct CategoryData: Equatable {
@ -214,35 +214,48 @@ final class StorageCategoriesComponent: Component {
label = dataSizeString(totalSelectedSize, formatting: DataSizeStringFormatting(strings: component.strings, decimalSeparator: "."))
}
var buttonContents: [AnyComponentWithIdentity<Empty>] = [
AnyComponentWithIdentity(id: "title", component: AnyComponent(
MultilineTextComponent(text: .plain(NSMutableAttributedString(string: clearTitle, font: Font.semibold(17.0), textColor: component.theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)))
))
]
if let label {
buttonContents.append(
AnyComponentWithIdentity(id: "label", component: AnyComponent(
MultilineTextComponent(text: .plain(NSMutableAttributedString(string: label, font: Font.semibold(17.0), textColor: component.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.6), paragraphAlignment: .center)))
))
)
}
contentHeight += 8.0
let buttonSize = self.button.update(
transition: transition,
component: AnyComponent(SolidRoundedButtonComponent(
title: clearTitle,
label: label,
theme: SolidRoundedButtonComponent.Theme(
backgroundColor: component.theme.list.itemCheckColors.fillColor,
backgroundColors: [],
foregroundColor: component.theme.list.itemCheckColors.foregroundColor
),
font: .bold,
fontSize: 17.0,
height: 52.0,
cornerRadius: 26.0,
gloss: false,
isEnabled: totalSelectedSize != 0,
animationName: nil,
iconPosition: .right,
iconSpacing: 4.0,
action: { [weak self] in
guard let self, let component = self.component else {
return
component: AnyComponent(
ButtonComponent(
background: ButtonComponent.Background(
style: .glass,
color: component.theme.list.itemCheckColors.fillColor,
foreground: component.theme.list.itemCheckColors.foregroundColor,
pressedColor: component.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9)
),
content: AnyComponentWithIdentity(
id: AnyHashable(0),
component: AnyComponent(
HStack(buttonContents, spacing: 4.0)
)
),
isEnabled: totalSelectedSize != 0,
displaysProgress: false,
action: { [weak self] in
guard let self, let component = self.component else {
return
}
component.clearAction()
}
component.clearAction()
}
)),
)
),
environment: {},
containerSize: CGSize(width: availableSize.width - 16.0 * 2.0, height: 50.0)
containerSize: CGSize(width: availableSize.width - 16.0 * 2.0, height: 52.0)
)
let buttonFrame = CGRect(origin: CGPoint(x: 16.0, y: contentHeight), size: buttonSize)
if let buttonView = self.button.view {

View file

@ -223,7 +223,7 @@ private final class StoryStealthModeSheetContentComponent: Component {
size: CGSize(width: 40.0, height: 40.0),
backgroundColor: environment.theme.rootController.navigationBar.glassBarButtonBackgroundColor,
isDark: environment.theme.overallDarkAppearance,
state: .tintedGlass,
state: .generic,
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
BundleIconComponent(
name: "Navigation/Close",

View file

@ -65,9 +65,10 @@ public final class AnimatableProperty<T: Interpolatable> {
guard let animation = self.animation, case let .curve(duration, curve) = animation.animation else {
return false
}
let effectiveDuration = duration * UIView.animationDurationFactor()
let timeFromStart = timestamp - animation.startTimestamp
var t = max(0.0, timeFromStart / duration)
var t = max(0.0, timeFromStart / effectiveDuration)
switch curve {
case .linear:
break
@ -80,7 +81,7 @@ public final class AnimatableProperty<T: Interpolatable> {
}
self.presentationValue = animation.valueAt(t) as! T
if timeFromStart <= duration {
if timeFromStart <= effectiveDuration {
return true
}
self.animation = nil