Various improvements

This commit is contained in:
Ilya Laktyushin 2022-04-24 06:30:30 +04:00
parent c929cce94f
commit b9f141aae4
10 changed files with 279 additions and 151 deletions

View File

@ -7,64 +7,145 @@ import SwiftSignalKit
import TelegramPresentationData import TelegramPresentationData
import AccountContext import AccountContext
private struct BotCheckoutPasswordAlertAction { private final class BotCheckoutPassworInputFieldNode: ASDisplayNode, UITextFieldDelegate {
public let title: String private var theme: PresentationTheme
public let action: () -> Void private let backgroundNode: ASImageNode
private let textInputNode: TextFieldNode
private let placeholderNode: ASTextNode
public init(title: String, action: @escaping () -> Void) { var updateHeight: (() -> Void)?
self.title = title var complete: (() -> Void)?
self.action = action var textChanged: ((String) -> Void)?
private let backgroundInsets = UIEdgeInsets(top: 8.0, left: 16.0, bottom: 15.0, right: 16.0)
private let inputInsets = UIEdgeInsets(top: 5.0, left: 12.0, bottom: 5.0, right: 12.0)
var text: String {
get {
return self.textInputNode.textField.text ?? ""
}
set {
self.textInputNode.textField.text = newValue
self.placeholderNode.isHidden = !newValue.isEmpty
}
} }
}
private final class BotCheckoutPasswordAlertActionNode: HighlightableButtonNode { var placeholder: String = "" {
private let backgroundNode: ASDisplayNode didSet {
self.placeholderNode.attributedText = NSAttributedString(string: self.placeholder, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor)
}
}
let action: BotCheckoutPasswordAlertAction init(theme: PresentationTheme, placeholder: String) {
self.theme = theme
init(theme: PresentationTheme, action: BotCheckoutPasswordAlertAction) { self.backgroundNode = ASImageNode()
self.backgroundNode = ASDisplayNode()
self.backgroundNode.isLayerBacked = true self.backgroundNode.isLayerBacked = true
self.backgroundNode.backgroundColor = theme.actionSheet.opaqueItemHighlightedBackgroundColor self.backgroundNode.displaysAsynchronously = false
self.backgroundNode.alpha = 0.0 self.backgroundNode.displayWithoutProcessing = true
self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 12.0, color: theme.actionSheet.inputHollowBackgroundColor, strokeColor: theme.actionSheet.inputBorderColor, strokeWidth: 1.0)
self.action = action self.textInputNode = TextFieldNode()
self.textInputNode.textField.typingAttributes = [NSAttributedString.Key.font: Font.regular(17.0), NSAttributedString.Key.foregroundColor: theme.actionSheet.inputTextColor]
self.textInputNode.textField.clipsToBounds = true
self.textInputNode.hitTestSlop = UIEdgeInsets(top: -5.0, left: -5.0, bottom: -5.0, right: -5.0)
self.textInputNode.textField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance
self.textInputNode.textField.returnKeyType = .done
self.textInputNode.textField.isSecureTextEntry = true
self.textInputNode.textField.tintColor = theme.actionSheet.controlAccentColor
self.placeholderNode = ASTextNode()
self.placeholderNode.isUserInteractionEnabled = false
self.placeholderNode.displaysAsynchronously = false
self.placeholderNode.attributedText = NSAttributedString(string: placeholder, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor)
super.init() super.init()
self.setTitle(action.title, with: Font.regular(17.0), with: theme.actionSheet.controlAccentColor, for: []) self.textInputNode.textField.delegate = self
self.setTitle(action.title, with: Font.regular(17.0), with: theme.actionSheet.disabledActionTextColor, for: [.disabled]) self.textInputNode.textField.addTarget(self, action: #selector(self.textDidChange), for: .editingChanged)
self.highligthedChanged = { [weak self] value in self.addSubnode(self.backgroundNode)
if let strongSelf = self { self.addSubnode(self.textInputNode)
if value { self.addSubnode(self.placeholderNode)
if strongSelf.backgroundNode.supernode == nil {
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
} }
strongSelf.backgroundNode.layer.removeAnimation(forKey: "opacity")
strongSelf.backgroundNode.alpha = 1.0 func updateTheme(_ theme: PresentationTheme) {
} else if !strongSelf.backgroundNode.alpha.isZero { self.theme = theme
strongSelf.backgroundNode.alpha = 0.0
strongSelf.backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25) self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 12.0, color: self.theme.actionSheet.inputHollowBackgroundColor, strokeColor: self.theme.actionSheet.inputBorderColor, strokeWidth: 1.0)
self.textInputNode.textField.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance
self.placeholderNode.attributedText = NSAttributedString(string: self.placeholderNode.attributedText?.string ?? "", font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor)
self.textInputNode.textField.tintColor = self.theme.actionSheet.controlAccentColor
self.textInputNode.textField.typingAttributes = [NSAttributedString.Key.font: Font.regular(17.0), NSAttributedString.Key.foregroundColor: theme.actionSheet.inputTextColor]
} }
func updateLayout(width: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
let backgroundInsets = self.backgroundInsets
let inputInsets = self.inputInsets
let textFieldHeight = self.calculateTextFieldMetrics(width: width)
let panelHeight = textFieldHeight + backgroundInsets.top + backgroundInsets.bottom
let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top), size: CGSize(width: width - backgroundInsets.left - backgroundInsets.right, height: panelHeight - backgroundInsets.top - backgroundInsets.bottom))
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
let placeholderSize = self.placeholderNode.measure(backgroundFrame.size)
transition.updateFrame(node: self.placeholderNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY + floor((backgroundFrame.size.height - placeholderSize.height) / 2.0)), size: placeholderSize))
transition.updateFrame(node: self.textInputNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.size.width - inputInsets.left - inputInsets.right, height: backgroundFrame.size.height)))
return panelHeight
} }
func activateInput() {
self.textInputNode.becomeFirstResponder()
}
func deactivateInput() {
self.textInputNode.resignFirstResponder()
}
func shake() {
self.layer.addShakeAnimation()
}
@objc func textDidChange() {
self.updateTextNodeText(animated: true)
self.textChanged?(self.textInputNode.textField.text ?? "")
self.placeholderNode.isHidden = !(self.textInputNode.textField.text ?? "").isEmpty
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if text == "\n" {
self.complete?()
return false
}
return true
}
private func calculateTextFieldMetrics(width: CGFloat) -> CGFloat {
let backgroundInsets = self.backgroundInsets
let inputInsets = self.inputInsets
let unboundTextFieldHeight = max(33.0, ceil(self.textInputNode.measure(CGSize(width: width - backgroundInsets.left - backgroundInsets.right - inputInsets.left - inputInsets.right, height: CGFloat.greatestFiniteMagnitude)).height))
return min(61.0, max(33.0, unboundTextFieldHeight))
}
private func updateTextNodeText(animated: Bool) {
let backgroundInsets = self.backgroundInsets
let textFieldHeight = self.calculateTextFieldMetrics(width: self.bounds.size.width)
let panelHeight = textFieldHeight + backgroundInsets.top + backgroundInsets.bottom
if !self.bounds.size.height.isEqual(to: panelHeight) {
self.updateHeight?()
} }
} }
override func didLoad() { @objc func clearPressed() {
super.didLoad() self.textInputNode.textField.text = nil
self.deactivateInput()
self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside)
}
@objc func pressed() {
self.action.action()
}
override func layout() {
super.layout()
self.backgroundNode.frame = self.bounds
} }
} }
@ -78,14 +159,13 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
private let textNode: ASTextNode private let textNode: ASTextNode
private let actionNodesSeparator: ASDisplayNode private let actionNodesSeparator: ASDisplayNode
private let actionNodes: [BotCheckoutPasswordAlertActionNode] private let actionNodes: [TextAlertContentActionNode]
private let actionVerticalSeparators: [ASDisplayNode] private let actionVerticalSeparators: [ASDisplayNode]
private let cancelActionNode: BotCheckoutPasswordAlertActionNode private let cancelActionNode: TextAlertContentActionNode
private let doneActionNode: BotCheckoutPasswordAlertActionNode private let doneActionNode: TextAlertContentActionNode
private let textFieldNodeBackground: ASImageNode let inputFieldNode: BotCheckoutPassworInputFieldNode
private let textFieldNode: TextFieldNode
private var validLayout: CGSize? private var validLayout: CGSize?
private var isVerifying = false private var isVerifying = false
@ -99,6 +179,8 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
self.requiresBiometrics = requiresBiometrics self.requiresBiometrics = requiresBiometrics
self.completion = completion self.completion = completion
let alertTheme = AlertControllerTheme(presentationTheme: theme, fontSize: .regular)
let titleNode = ASTextNode() let titleNode = ASTextNode()
titleNode.attributedText = NSAttributedString(string: strings.Checkout_PasswordEntry_Title, font: Font.semibold(17.0), textColor: theme.actionSheet.primaryTextColor, paragraphAlignment: .center) titleNode.attributedText = NSAttributedString(string: strings.Checkout_PasswordEntry_Title, font: Font.semibold(17.0), textColor: theme.actionSheet.primaryTextColor, paragraphAlignment: .center)
titleNode.displaysAsynchronously = false titleNode.displaysAsynchronously = false
@ -112,16 +194,18 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
self.textNode.displaysAsynchronously = false self.textNode.displaysAsynchronously = false
self.textNode.isUserInteractionEnabled = false self.textNode.isUserInteractionEnabled = false
self.inputFieldNode = BotCheckoutPassworInputFieldNode(theme: theme, placeholder: passwordTip ?? "")
self.actionNodesSeparator = ASDisplayNode() self.actionNodesSeparator = ASDisplayNode()
self.actionNodesSeparator.isLayerBacked = true self.actionNodesSeparator.isLayerBacked = true
self.actionNodesSeparator.backgroundColor = theme.actionSheet.opaqueItemSeparatorColor self.actionNodesSeparator.backgroundColor = theme.actionSheet.opaqueItemSeparatorColor
self.cancelActionNode = BotCheckoutPasswordAlertActionNode(theme: theme, action: BotCheckoutPasswordAlertAction(title: strings.Common_Cancel, action: { self.cancelActionNode = TextAlertContentActionNode(theme: alertTheme, action: TextAlertAction(type: .genericAction, title: strings.Common_Cancel, action: {
cancel() cancel()
})) }))
var doneImpl: (() -> Void)? var doneImpl: (() -> Void)?
self.doneActionNode = BotCheckoutPasswordAlertActionNode(theme: theme, action: BotCheckoutPasswordAlertAction(title: strings.Checkout_PasswordEntry_Pay, action: { self.doneActionNode = TextAlertContentActionNode(theme: alertTheme, action: TextAlertAction(type: .defaultAction, title: strings.Checkout_PasswordEntry_Pay, action: {
doneImpl?() doneImpl?()
})) }))
@ -138,26 +222,6 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
} }
self.actionVerticalSeparators = actionVerticalSeparators self.actionVerticalSeparators = actionVerticalSeparators
self.textFieldNodeBackground = ASImageNode()
self.textFieldNodeBackground.displaysAsynchronously = false
self.textFieldNodeBackground.displayWithoutProcessing = true
self.textFieldNodeBackground.image = generateImage(CGSize(width: 4.0, height: 4.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setStrokeColor(theme.actionSheet.primaryTextColor.cgColor)
context.setLineWidth(UIScreenPixel)
context.stroke(CGRect(origin: CGPoint(), size: size))
})?.stretchableImage(withLeftCapWidth: 2, topCapHeight: 2)
self.textFieldNode = TextFieldNode()
self.textFieldNode.textField.textColor = theme.actionSheet.primaryTextColor
self.textFieldNode.textField.font = Font.regular(12.0)
self.textFieldNode.textField.typingAttributes = [NSAttributedString.Key.font: Font.regular(12.0)]
self.textFieldNode.textField.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance
self.textFieldNode.textField.isSecureTextEntry = true
self.textFieldNode.textField.tintColor = theme.list.itemAccentColor
self.textFieldNode.textField.placeholder = passwordTip
super.init() super.init()
self.addSubnode(self.titleNode) self.addSubnode(self.titleNode)
@ -173,10 +237,13 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
self.addSubnode(separatorNode) self.addSubnode(separatorNode)
} }
self.addSubnode(self.textFieldNodeBackground) self.addSubnode(self.inputFieldNode)
self.addSubnode(self.textFieldNode)
self.textFieldNode.textField.addTarget(self, action: #selector(self.textFieldChanged(_:)), for: .editingChanged) self.inputFieldNode.textChanged = { [weak self] _ in
if let strongSelf = self {
strongSelf.updateState()
}
}
self.updateState() self.updateState()
@ -213,13 +280,11 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
let textFrame = CGRect(origin: CGPoint(x: insets.left + floor((contentWidth - textSize.width) / 2.0), y: titleFrame.maxY + spacing), size: textSize) let textFrame = CGRect(origin: CGPoint(x: insets.left + floor((contentWidth - textSize.width) / 2.0), y: titleFrame.maxY + spacing), size: textSize)
transition.updateFrame(node: self.textNode, frame: textFrame) transition.updateFrame(node: self.textNode, frame: textFrame)
let inputHeight: CGFloat = 38.0 let resultSize = CGSize(width: contentWidth + insets.left + insets.right, height: titleSize.height + spacing + textSize.height + actionsHeight + insets.top + insets.bottom + 46.0)
let resultSize = CGSize(width: contentWidth + insets.left + insets.right, height: titleSize.height + spacing + textSize.height + actionsHeight + insets.top + insets.bottom + inputHeight) let inputFieldWidth = resultSize.width
let inputFieldHeight = self.inputFieldNode.updateLayout(width: inputFieldWidth, transition: transition)
let textFieldBackgroundFrame = CGRect(origin: CGPoint(x: insets.left, y: resultSize.height - inputHeight + 12.0 - actionsHeight - insets.bottom), size: CGSize(width: resultSize.width - insets.left - insets.right, height: 25.0)) transition.updateFrame(node: self.inputFieldNode, frame: CGRect(x: 0.0, y: resultSize.height - 36.0 - actionsHeight - insets.bottom, width: resultSize.width, height: inputFieldHeight))
self.textFieldNodeBackground.frame = textFieldBackgroundFrame
self.textFieldNode.frame = textFieldBackgroundFrame.offsetBy(dx: 0.0, dy: 0.0).insetBy(dx: 4.0, dy: 0.0)
self.actionNodesSeparator.frame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)) self.actionNodesSeparator.frame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))
@ -250,7 +315,7 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
} }
if previousLayout == nil { if previousLayout == nil {
self.textFieldNode.textField.becomeFirstResponder() self.inputFieldNode.activateInput()
} }
return resultSize return resultSize
@ -262,24 +327,15 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
private func updateState() { private func updateState() {
var enabled = true var enabled = true
if self.isVerifying || self.inputFieldNode.text.isEmpty {
if self.isVerifying {
enabled = false enabled = false
} }
self.doneActionNode.actionEnabled = enabled
if let text = self.textFieldNode.textField.text {
if text.isEmpty {
enabled = false
}
} else {
enabled = false
}
self.doneActionNode.isEnabled = enabled
} }
private func verify() { private func verify() {
guard let text = self.textFieldNode.textField.text, !text.isEmpty else { let text = self.inputFieldNode.text
guard !text.isEmpty else {
return return
} }
@ -290,8 +346,7 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
} }
}, error: { [weak self] _ in }, error: { [weak self] _ in
if let strongSelf = self { if let strongSelf = self {
strongSelf.textFieldNodeBackground.layer.addShakeAnimation() strongSelf.inputFieldNode.shake()
strongSelf.textFieldNode.layer.addShakeAnimation()
strongSelf.hapticFeedback.error() strongSelf.hapticFeedback.error()
strongSelf.isVerifying = false strongSelf.isVerifying = false
strongSelf.updateState() strongSelf.updateState()

View File

@ -45,6 +45,7 @@
_explicit = explicit; _explicit = explicit;
_sliderView = [[TGPhotoEditorSliderView alloc] initWithFrame:CGRectZero]; _sliderView = [[TGPhotoEditorSliderView alloc] initWithFrame:CGRectZero];
_sliderView.enablePanHandling = true;
if (editorItem.segmented) if (editorItem.segmented)
_sliderView.positionsCount = (NSInteger)editorItem.maximumValue + 1; _sliderView.positionsCount = (NSInteger)editorItem.maximumValue + 1;
_sliderView.minimumValue = editorItem.minimumValue; _sliderView.minimumValue = editorItem.minimumValue;

View File

@ -68,6 +68,7 @@ const CGFloat TGPhotoEditorSliderViewInternalMargin = 7.0f;
_panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; _panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
_panGestureRecognizer.enabled = false; _panGestureRecognizer.enabled = false;
_panGestureRecognizer.delegate = self;
[self addGestureRecognizer:_panGestureRecognizer]; [self addGestureRecognizer:_panGestureRecognizer];
_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; _tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
@ -531,6 +532,19 @@ const CGFloat TGPhotoEditorSliderViewInternalMargin = 7.0f;
} }
} }
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer {
if (gestureRecognizer == _panGestureRecognizer) {
CGPoint velocity = [gestureRecognizer velocityInView:gestureRecognizer.view];
if (ABS(velocity.x) > ABS(velocity.y)) {
return true;
} else {
return false;
}
}
return true;
}
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)__unused event - (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)__unused event
{ {
if (!_enablePanHandling) { if (!_enablePanHandling) {

View File

@ -266,6 +266,7 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
_portraitCollectionView.toolsDataSource = self; _portraitCollectionView.toolsDataSource = self;
_portraitCollectionView.interactionBegan = _interactionBegan; _portraitCollectionView.interactionBegan = _interactionBegan;
_portraitCollectionView.interactionEnded = _interactionEnded; _portraitCollectionView.interactionEnded = _interactionEnded;
_portraitCollectionView.canCancelContentTouches = true;
[_portraitToolsWrapperView addSubview:_portraitCollectionView]; [_portraitToolsWrapperView addSubview:_portraitCollectionView];
if (!TGIsPad()) if (!TGIsPad())
@ -278,6 +279,7 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
_landscapeCollectionView.toolsDataSource = self; _landscapeCollectionView.toolsDataSource = self;
_landscapeCollectionView.interactionBegan = _interactionBegan; _landscapeCollectionView.interactionBegan = _interactionBegan;
_landscapeCollectionView.interactionEnded = _interactionEnded; _landscapeCollectionView.interactionEnded = _interactionEnded;
_landscapeCollectionView.canCancelContentTouches = true;
[_landscapeToolsWrapperView addSubview:_landscapeCollectionView]; [_landscapeToolsWrapperView addSubview:_landscapeCollectionView];
} }

View File

@ -93,6 +93,17 @@ private final class TabBarItemNode: ASDisplayNode {
var contentWidth: CGFloat? var contentWidth: CGFloat?
var isSelected: Bool = false var isSelected: Bool = false
let ringImageNode: ASImageNode
var ringColor: UIColor? {
didSet {
if let ringColor = self.ringColor {
self.ringImageNode.image = generateCircleImage(diameter: 29.0, lineWidth: 1.0, color: ringColor, backgroundColor: nil)
} else {
self.ringImageNode.image = nil
}
}
}
var swiped: ((TabBarItemSwipeDirection) -> Void)? var swiped: ((TabBarItemSwipeDirection) -> Void)?
var pointerInteraction: PointerInteraction? var pointerInteraction: PointerInteraction?
@ -101,6 +112,11 @@ private final class TabBarItemNode: ASDisplayNode {
self.extractedContainerNode = ContextExtractedContentContainingNode() self.extractedContainerNode = ContextExtractedContentContainingNode()
self.containerNode = ContextControllerSourceNode() self.containerNode = ContextControllerSourceNode()
self.ringImageNode = ASImageNode()
self.ringImageNode.isUserInteractionEnabled = false
self.ringImageNode.displayWithoutProcessing = true
self.ringImageNode.displaysAsynchronously = false
self.imageNode = ASImageNode() self.imageNode = ASImageNode()
self.imageNode.isUserInteractionEnabled = false self.imageNode.isUserInteractionEnabled = false
self.imageNode.displayWithoutProcessing = true self.imageNode.displayWithoutProcessing = true
@ -136,6 +152,7 @@ private final class TabBarItemNode: ASDisplayNode {
self.isAccessibilityElement = true self.isAccessibilityElement = true
self.extractedContainerNode.contentNode.addSubnode(self.ringImageNode)
self.extractedContainerNode.contentNode.addSubnode(self.textImageNode) self.extractedContainerNode.contentNode.addSubnode(self.textImageNode)
self.extractedContainerNode.contentNode.addSubnode(self.imageNode) self.extractedContainerNode.contentNode.addSubnode(self.imageNode)
self.extractedContainerNode.contentNode.addSubnode(self.animationContainerNode) self.extractedContainerNode.contentNode.addSubnode(self.animationContainerNode)
@ -150,6 +167,7 @@ private final class TabBarItemNode: ASDisplayNode {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
transition.updateAlpha(node: strongSelf.ringImageNode, alpha: isExtracted ? 0.0 : 1.0)
transition.updateAlpha(node: strongSelf.imageNode, alpha: isExtracted ? 0.0 : 1.0) transition.updateAlpha(node: strongSelf.imageNode, alpha: isExtracted ? 0.0 : 1.0)
transition.updateAlpha(node: strongSelf.animationNode, alpha: isExtracted ? 0.0 : 1.0) transition.updateAlpha(node: strongSelf.animationNode, alpha: isExtracted ? 0.0 : 1.0)
transition.updateAlpha(node: strongSelf.textImageNode, alpha: isExtracted ? 0.0 : 1.0) transition.updateAlpha(node: strongSelf.textImageNode, alpha: isExtracted ? 0.0 : 1.0)
@ -441,6 +459,12 @@ class TabBarNode: ASDisplayNode {
}, swipeAction: { [weak self] direction in }, swipeAction: { [weak self] direction in
self?.swipeAction(i, direction) self?.swipeAction(i, direction)
}) })
if item.item.ringSelection {
node.ringColor = self.theme.tabBarSelectedIconColor
} else {
node.ringColor = nil
}
if let selectedIndex = self.selectedIndex, selectedIndex == i { if let selectedIndex = self.selectedIndex, selectedIndex == i {
let (textImage, contentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered) let (textImage, contentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
let (image, imageContentWidth): (UIImage, CGFloat) let (image, imageContentWidth): (UIImage, CGFloat)
@ -507,6 +531,12 @@ class TabBarNode: ASDisplayNode {
self.centered = self.theme.tabBarTextColor == .clear self.centered = self.theme.tabBarTextColor == .clear
if item.item.ringSelection {
node.ringColor = self.theme.tabBarSelectedIconColor
} else {
node.ringColor = nil
}
let previousImageSize = node.imageNode.image?.size ?? CGSize() let previousImageSize = node.imageNode.image?.size ?? CGSize()
let previousTextImageSize = node.textImageNode.image?.size ?? CGSize() let previousTextImageSize = node.textImageNode.image?.size ?? CGSize()
if let selectedIndex = self.selectedIndex, selectedIndex == index { if let selectedIndex = self.selectedIndex, selectedIndex == index {
@ -523,8 +553,12 @@ class TabBarNode: ASDisplayNode {
} }
node.animationNode.setOverlayColor(self.theme.tabBarSelectedIconColor, replace: true, animated: false) node.animationNode.setOverlayColor(self.theme.tabBarSelectedIconColor, replace: true, animated: false)
node.animationNode.updateLayout(size: CGSize(width: 51.0, height: 51.0)) node.animationNode.updateLayout(size: CGSize(width: 51.0, height: 51.0))
} else {
if item.item.ringSelection {
(image, imageContentWidth) = (item.item.selectedImage ?? UIImage(), item.item.selectedImage?.size.width ?? 0.0)
} else { } else {
(image, imageContentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) (image, imageContentWidth) = tabBarItemImage(item.item.selectedImage, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarSelectedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
}
node.animationNode.isHidden = true node.animationNode.isHidden = true
node.animationNode.visibility = false node.animationNode.visibility = false
@ -539,9 +573,22 @@ class TabBarNode: ASDisplayNode {
node.contextImageNode.image = contextImage node.contextImageNode.image = contextImage
node.contentWidth = max(contentWidth, imageContentWidth) node.contentWidth = max(contentWidth, imageContentWidth)
node.isSelected = true node.isSelected = true
ContainedViewLayoutTransition.animated(duration: 0.35, curve: .easeInOut).updateTransformScale(node: node.ringImageNode, scale: 1.0, delay: 0.1)
node.imageNode.layer.animateScale(from: 1.0, to: 0.87, duration: 0.1, removeOnCompletion: false, completion: { [weak node] _ in
node?.imageNode.layer.animateScale(from: 0.87, to: 1.0, duration: 0.35, removeOnCompletion: false, completion: { [weak node] _ in
node?.imageNode.layer.removeAllAnimations()
})
})
} else { } else {
let (textImage, contentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered) let (textImage, contentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
let (image, imageContentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
let (image, imageContentWidth): (UIImage, CGFloat)
if item.item.ringSelection {
(image, imageContentWidth) = (item.item.image ?? UIImage(), item.item.image?.size.width ?? 0.0)
} else {
(image, imageContentWidth) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
}
let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered) let (contextTextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedTextColor, horizontal: self.horizontal, imageMode: false, centered: self.centered)
let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered) let (contextImage, _) = tabBarItemImage(item.item.image, title: item.item.title ?? "", backgroundColor: .clear, tintColor: self.theme.tabBarExtractedIconColor, horizontal: self.horizontal, imageMode: true, centered: self.centered)
@ -556,6 +603,8 @@ class TabBarNode: ASDisplayNode {
node.contextImageNode.image = contextImage node.contextImageNode.image = contextImage
node.contentWidth = max(contentWidth, imageContentWidth) node.contentWidth = max(contentWidth, imageContentWidth)
node.isSelected = false node.isSelected = false
ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut).updateTransformScale(node: node.ringImageNode, scale: 0.5)
} }
let updatedImageSize = node.imageNode.image?.size ?? CGSize() let updatedImageSize = node.imageNode.image?.size ?? CGSize()
@ -647,7 +696,9 @@ class TabBarNode: ASDisplayNode {
node.containerNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size) node.containerNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
node.hitTestSlop = UIEdgeInsets(top: -3.0, left: -horizontalHitTestInset, bottom: -3.0, right: -horizontalHitTestInset) node.hitTestSlop = UIEdgeInsets(top: -3.0, left: -horizontalHitTestInset, bottom: -3.0, right: -horizontalHitTestInset)
node.containerNode.hitTestSlop = UIEdgeInsets(top: -3.0, left: -horizontalHitTestInset, bottom: -3.0, right: -horizontalHitTestInset) node.containerNode.hitTestSlop = UIEdgeInsets(top: -3.0, left: -horizontalHitTestInset, bottom: -3.0, right: -horizontalHitTestInset)
if node.ringColor == nil {
node.imageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size) node.imageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
}
node.textImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size) node.textImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
node.contextImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size) node.contextImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
node.contextTextImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size) node.contextTextImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
@ -655,10 +706,23 @@ class TabBarNode: ASDisplayNode {
let scaleFactor: CGFloat = horizontal ? 0.8 : 1.0 let scaleFactor: CGFloat = horizontal ? 0.8 : 1.0
node.animationContainerNode.subnodeTransform = CATransform3DMakeScale(scaleFactor, scaleFactor, 1.0) node.animationContainerNode.subnodeTransform = CATransform3DMakeScale(scaleFactor, scaleFactor, 1.0)
let animationOffset: CGPoint = self.tabBarItems[i].item.animationOffset let animationOffset: CGPoint = self.tabBarItems[i].item.animationOffset
let ringImageFrame: CGRect
let imageFrame: CGRect
if horizontal { if horizontal {
node.animationNode.frame = CGRect(origin: CGPoint(x: -10.0 - UIScreenPixel, y: -4.0 - UIScreenPixel), size: CGSize(width: 51.0, height: 51.0)) node.animationNode.frame = CGRect(origin: CGPoint(x: -10.0 - UIScreenPixel, y: -4.0 - UIScreenPixel), size: CGSize(width: 51.0, height: 51.0))
ringImageFrame = CGRect(origin: CGPoint(x: UIScreenPixel, y: 5.0 + UIScreenPixel), size: CGSize(width: 23.0, height: 23.0))
imageFrame = ringImageFrame.insetBy(dx: -1.0 + UIScreenPixel, dy: -1.0 + UIScreenPixel)
} else { } else {
node.animationNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((nodeSize.width - 51.0) / 2.0), y: -10.0 - UIScreenPixel).offsetBy(dx: animationOffset.x, dy: animationOffset.y), size: CGSize(width: 51.0, height: 51.0)) node.animationNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((nodeSize.width - 51.0) / 2.0), y: -10.0 - UIScreenPixel).offsetBy(dx: animationOffset.x, dy: animationOffset.y), size: CGSize(width: 51.0, height: 51.0))
ringImageFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((nodeSize.width - 29.0) / 2.0), y: 1.0), size: CGSize(width: 29.0, height: 29.0))
imageFrame = ringImageFrame.insetBy(dx: -1.0, dy: -1.0)
}
node.ringImageNode.bounds = CGRect(origin: CGPoint(), size: ringImageFrame.size)
node.ringImageNode.position = ringImageFrame.center
if node.ringColor != nil {
node.imageNode.bounds = CGRect(origin: CGPoint(), size: imageFrame.size)
node.imageNode.position = imageFrame.center
} }
if container.badgeValue != container.appliedBadgeValue { if container.badgeValue != container.appliedBadgeValue {

View File

@ -367,7 +367,12 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
self.expandBackgroundNode.image = generateExpandBackground(size: expandBackgroundFrame.size, color: presentationData.theme.list.itemBlocksBackgroundColor) self.expandBackgroundNode.image = generateExpandBackground(size: expandBackgroundFrame.size, color: presentationData.theme.list.itemBlocksBackgroundColor)
transition.updateFrame(node: self.labelNode, frame: labelFrame) transition.updateFrame(node: self.labelNode, frame: labelFrame)
transition.updateFrame(node: self.textNode, frame: textFrame)
var textTransition = transition
if self.textNode.frame.size != textFrame.size {
textTransition = .immediate
}
textTransition.updateFrame(node: self.textNode, frame: textFrame)
let height = labelSize.height + 3.0 + textSize.height + 22.0 let height = labelSize.height + 3.0 + textSize.height + 22.0

View File

@ -2010,7 +2010,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
var requestAvatarExpansion: ((Bool, [AvatarGalleryEntry], AvatarGalleryEntry?, (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?) -> Void)? var requestAvatarExpansion: ((Bool, [AvatarGalleryEntry], AvatarGalleryEntry?, (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?) -> Void)?
var requestOpenAvatarForEditing: ((Bool) -> Void)? var requestOpenAvatarForEditing: ((Bool) -> Void)?
var cancelUpload: (() -> Void)? var cancelUpload: (() -> Void)?
var requestUpdateLayout: (() -> Void)? var requestUpdateLayout: ((Bool) -> Void)?
var animateOverlaysFadeIn: (() -> Void)? var animateOverlaysFadeIn: (() -> Void)?
var displayAvatarContextMenu: ((ASDisplayNode, ContextGesture?) -> Void)? var displayAvatarContextMenu: ((ASDisplayNode, ContextGesture?) -> Void)?
@ -2100,7 +2100,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
super.init() super.init()
requestUpdateLayoutImpl = { [weak self] in requestUpdateLayoutImpl = { [weak self] in
self?.requestUpdateLayout?() self?.requestUpdateLayout?(false)
} }

View File

@ -460,7 +460,7 @@ private final class PeerInfoInteraction {
let performMemberAction: (PeerInfoMember, PeerInfoMemberAction) -> Void let performMemberAction: (PeerInfoMember, PeerInfoMemberAction) -> Void
let openPeerInfoContextMenu: (PeerInfoContextSubject, ASDisplayNode) -> Void let openPeerInfoContextMenu: (PeerInfoContextSubject, ASDisplayNode) -> Void
let performBioLinkAction: (TextLinkItemActionType, TextLinkItem) -> Void let performBioLinkAction: (TextLinkItemActionType, TextLinkItem) -> Void
let requestLayout: () -> Void let requestLayout: (Bool) -> Void
let openEncryptionKey: () -> Void let openEncryptionKey: () -> Void
let openSettings: (PeerInfoSettingsSection) -> Void let openSettings: (PeerInfoSettingsSection) -> Void
let switchToAccount: (AccountRecordId) -> Void let switchToAccount: (AccountRecordId) -> Void
@ -502,7 +502,7 @@ private final class PeerInfoInteraction {
performMemberAction: @escaping (PeerInfoMember, PeerInfoMemberAction) -> Void, performMemberAction: @escaping (PeerInfoMember, PeerInfoMemberAction) -> Void,
openPeerInfoContextMenu: @escaping (PeerInfoContextSubject, ASDisplayNode) -> Void, openPeerInfoContextMenu: @escaping (PeerInfoContextSubject, ASDisplayNode) -> Void,
performBioLinkAction: @escaping (TextLinkItemActionType, TextLinkItem) -> Void, performBioLinkAction: @escaping (TextLinkItemActionType, TextLinkItem) -> Void,
requestLayout: @escaping () -> Void, requestLayout: @escaping (Bool) -> Void,
openEncryptionKey: @escaping () -> Void, openEncryptionKey: @escaping () -> Void,
openSettings: @escaping (PeerInfoSettingsSection) -> Void, openSettings: @escaping (PeerInfoSettingsSection) -> Void,
switchToAccount: @escaping (AccountRecordId) -> Void, switchToAccount: @escaping (AccountRecordId) -> Void,
@ -864,7 +864,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
}, longTapAction: { sourceNode in }, longTapAction: { sourceNode in
interaction.openPeerInfoContextMenu(.phone(formattedPhone), sourceNode) interaction.openPeerInfoContextMenu(.phone(formattedPhone), sourceNode)
}, requestLayout: { }, requestLayout: {
interaction.requestLayout() interaction.requestLayout(false)
})) }))
} }
if let username = user.username { if let username = user.username {
@ -875,21 +875,21 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
}, iconAction: { }, iconAction: {
interaction.openQrCode() interaction.openQrCode()
}, requestLayout: { }, requestLayout: {
interaction.requestLayout() interaction.requestLayout(false)
})) }))
} }
if let cachedData = data.cachedData as? CachedUserData { if let cachedData = data.cachedData as? CachedUserData {
if user.isFake { if user.isFake {
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: user.botInfo != nil ? presentationData.strings.UserInfo_FakeBotWarning : presentationData.strings.UserInfo_FakeUserWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.botInfo != nil ? enabledPrivateBioEntities : []), action: nil, requestLayout: { items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: user.botInfo != nil ? presentationData.strings.UserInfo_FakeBotWarning : presentationData.strings.UserInfo_FakeUserWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.botInfo != nil ? enabledPrivateBioEntities : []), action: nil, requestLayout: {
interaction.requestLayout() interaction.requestLayout(false)
})) }))
} else if user.isScam { } else if user.isScam {
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: user.botInfo != nil ? presentationData.strings.UserInfo_ScamBotWarning : presentationData.strings.UserInfo_ScamUserWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.botInfo != nil ? enabledPrivateBioEntities : []), action: nil, requestLayout: { items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: user.botInfo != nil ? presentationData.strings.UserInfo_ScamBotWarning : presentationData.strings.UserInfo_ScamUserWarning, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: user.botInfo != nil ? enabledPrivateBioEntities : []), action: nil, requestLayout: {
interaction.requestLayout() interaction.requestLayout(false)
})) }))
} else if let about = cachedData.about, !about.isEmpty { } else if let about = cachedData.about, !about.isEmpty {
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: about, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPrivateBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: { items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: user.botInfo == nil ? presentationData.strings.Profile_About : presentationData.strings.Profile_BotInfo, text: about, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPrivateBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
interaction.requestLayout() interaction.requestLayout(false)
})) }))
} }
} }
@ -982,7 +982,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
}, iconAction: { }, iconAction: {
interaction.openQrCode() interaction.openQrCode()
}, requestLayout: { }, requestLayout: {
interaction.requestLayout() interaction.requestLayout(false)
})) }))
} }
if let cachedData = data.cachedData as? CachedChannelData { if let cachedData = data.cachedData as? CachedChannelData {
@ -1011,7 +1011,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
enabledEntities = enabledPrivateBioEntities enabledEntities = enabledPrivateBioEntities
} }
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: { items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
interaction.requestLayout() interaction.requestLayout(true)
})) }))
} }
@ -1056,7 +1056,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
if let aboutText = aboutText { if let aboutText = aboutText {
items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPrivateBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: { items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPrivateBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: {
interaction.requestLayout() interaction.requestLayout(true)
})) }))
} }
} }
@ -1776,8 +1776,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
performBioLinkAction: { [weak self] action, item in performBioLinkAction: { [weak self] action, item in
self?.performBioLinkAction(action: action, item: item) self?.performBioLinkAction(action: action, item: item)
}, },
requestLayout: { [weak self] in requestLayout: { [weak self] animated in
self?.requestLayout() self?.requestLayout(animated: animated)
}, },
openEncryptionKey: { [weak self] in openEncryptionKey: { [weak self] in
self?.openEncryptionKey() self?.openEncryptionKey()
@ -2571,12 +2571,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
navigationBar.layer.animateAlpha(from: 0.0, to: navigationBar.alpha, duration: 0.25) navigationBar.layer.animateAlpha(from: 0.0, to: navigationBar.alpha, duration: 0.25)
} }
self.headerNode.requestUpdateLayout = { [weak self] in self.headerNode.requestUpdateLayout = { [weak self] animated in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
if let (layout, navigationHeight) = strongSelf.validLayout { if let (layout, navigationHeight) = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate, additive: false) strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: animated ? .animated(duration: 0.35, curve: .slide) : .immediate, additive: false)
} }
} }
@ -5630,8 +5630,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
self.context.sharedContext.handleTextLinkAction(context: self.context, peerId: peer.id, navigateDisposable: self.resolveUrlDisposable, controller: controller, action: action, itemLink: item) self.context.sharedContext.handleTextLinkAction(context: self.context, peerId: peer.id, navigateDisposable: self.resolveUrlDisposable, controller: controller, action: action, itemLink: item)
} }
private func requestLayout() { private func requestLayout(animated: Bool = false) {
self.headerNode.requestUpdateLayout?() self.headerNode.requestUpdateLayout?(animated)
} }
private func openDeletePeer() { private func openDeletePeer() {
@ -7763,18 +7763,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
if let signal = peerAvatarImage(account: primary.0, peerReference: PeerReference(primary.1._asPeer()), authorOfMessage: nil, representation: primary.1.profileImageRepresentations.first, displayDimensions: size, inset: 3.0, emptyColor: nil, synchronousLoad: false) { if let signal = peerAvatarImage(account: primary.0, peerReference: PeerReference(primary.1._asPeer()), authorOfMessage: nil, representation: primary.1.profileImageRepresentations.first, displayDimensions: size, inset: 3.0, emptyColor: nil, synchronousLoad: false) {
return signal return signal
|> map { imageVersions -> (UIImage, UIImage)? in |> map { imageVersions -> (UIImage, UIImage)? in
let image = imageVersions?.0 if let image = imageVersions?.0 {
if let image = image, let selectedImage = generateImage(size, rotatedContext: { size, context in return (image.withRenderingMode(.alwaysOriginal), image.withRenderingMode(.alwaysOriginal))
context.clear(CGRect(origin: CGPoint(), size: size))
context.translateBy(x: size.width / 2.0, y: size.height / 2.0)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0)
context.draw(image.cgImage!, in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
context.setLineWidth(1.0)
context.setStrokeColor(primary.2.rootController.tabBar.selectedIconColor.cgColor)
context.strokeEllipse(in: CGRect(x: 1.5, y: 1.5, width: 28.0, height: 28.0))
}) {
return (image.withRenderingMode(.alwaysOriginal), selectedImage.withRenderingMode(.alwaysOriginal))
} else { } else {
return nil return nil
} }
@ -7792,22 +7782,8 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
drawPeerAvatarLetters(context: context, size: CGSize(width: size.width - inset * 2.0, height: size.height - inset * 2.0), font: avatarFont, letters: displayLetters, peerId: primary.1.id) drawPeerAvatarLetters(context: context, size: CGSize(width: size.width - inset * 2.0, height: size.height - inset * 2.0), font: avatarFont, letters: displayLetters, peerId: primary.1.id)
})?.withRenderingMode(.alwaysOriginal) })?.withRenderingMode(.alwaysOriginal)
if let image = image {
let selectedImage = generateImage(size, rotatedContext: { size, context in subscriber.putNext((image, image))
context.clear(CGRect(origin: CGPoint(), size: size))
context.translateBy(x: size.width / 2.0, y: size.height / 2.0)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0)
if let cgImage = image?.cgImage {
context.draw(cgImage, in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
}
context.setLineWidth(1.0)
context.setStrokeColor(primary.2.rootController.tabBar.selectedIconColor.cgColor)
context.strokeEllipse(in: CGRect(x: 1.5, y: 1.5, width: 28.0, height: 28.0))
})?.withRenderingMode(.alwaysOriginal)
if let image = image, let selectedImage = selectedImage {
subscriber.putNext((image, selectedImage))
} else { } else {
subscriber.putNext(nil) subscriber.putNext(nil)
} }
@ -7884,6 +7860,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
strongSelf.tabBarItem.image = image strongSelf.tabBarItem.image = image
strongSelf.tabBarItem.selectedImage = selectedImage strongSelf.tabBarItem.selectedImage = selectedImage
strongSelf.tabBarItem.animationName = isAvatar ? nil : "TabSettings" strongSelf.tabBarItem.animationName = isAvatar ? nil : "TabSettings"
strongSelf.tabBarItem.ringSelection = isAvatar
strongSelf.tabBarItem.badgeValue = badgeValue strongSelf.tabBarItem.badgeValue = badgeValue
} }
}) })

View File

@ -50,5 +50,6 @@ NSInteger UITabBarItem_addSetBadgeListener(UITabBarItem * _Nonnull item, UITabBa
@property (nonatomic, strong) NSString * _Nullable animationName; @property (nonatomic, strong) NSString * _Nullable animationName;
@property (nonatomic, assign) CGPoint animationOffset; @property (nonatomic, assign) CGPoint animationOffset;
@property (nonatomic, assign) bool ringSelection;
@end @end

View File

@ -18,6 +18,7 @@ static const void *setBadgeListenerBagKey = &setBadgeListenerBagKey;
static const void *badgeKey = &badgeKey; static const void *badgeKey = &badgeKey;
static const void *animationNameKey = &animationNameKey; static const void *animationNameKey = &animationNameKey;
static const void *animationOffsetKey = &animationOffsetKey; static const void *animationOffsetKey = &animationOffsetKey;
static const void *ringSelectionKey = &ringSelectionKey;
@implementation UINavigationItem (Proxy) @implementation UINavigationItem (Proxy)
@ -419,4 +420,12 @@ NSInteger UITabBarItem_addSetBadgeListener(UITabBarItem *item, UITabBarItemSetBa
return ((NSValue *)[self associatedObjectForKey:animationOffsetKey]).CGPointValue; return ((NSValue *)[self associatedObjectForKey:animationOffsetKey]).CGPointValue;
} }
- (void)setRingSelection:(bool)ringSelection {
[self setAssociatedObject:@(ringSelection) forKey:ringSelectionKey];
}
- (bool)ringSelection {
return ((NSNumber *)[self associatedObjectForKey:ringSelectionKey]).boolValue;
}
@end @end