mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
c929cce94f
commit
b9f141aae4
@ -7,64 +7,145 @@ import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
|
||||
private struct BotCheckoutPasswordAlertAction {
|
||||
public let title: String
|
||||
public let action: () -> Void
|
||||
private final class BotCheckoutPassworInputFieldNode: ASDisplayNode, UITextFieldDelegate {
|
||||
private var theme: PresentationTheme
|
||||
private let backgroundNode: ASImageNode
|
||||
private let textInputNode: TextFieldNode
|
||||
private let placeholderNode: ASTextNode
|
||||
|
||||
public init(title: String, action: @escaping () -> Void) {
|
||||
self.title = title
|
||||
self.action = action
|
||||
}
|
||||
}
|
||||
|
||||
private final class BotCheckoutPasswordAlertActionNode: HighlightableButtonNode {
|
||||
private let backgroundNode: ASDisplayNode
|
||||
var updateHeight: (() -> Void)?
|
||||
var complete: (() -> Void)?
|
||||
var textChanged: ((String) -> Void)?
|
||||
|
||||
let action: BotCheckoutPasswordAlertAction
|
||||
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)
|
||||
|
||||
init(theme: PresentationTheme, action: BotCheckoutPasswordAlertAction) {
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
self.backgroundNode.backgroundColor = theme.actionSheet.opaqueItemHighlightedBackgroundColor
|
||||
self.backgroundNode.alpha = 0.0
|
||||
|
||||
self.action = action
|
||||
|
||||
super.init()
|
||||
|
||||
self.setTitle(action.title, with: Font.regular(17.0), with: theme.actionSheet.controlAccentColor, for: [])
|
||||
self.setTitle(action.title, with: Font.regular(17.0), with: theme.actionSheet.disabledActionTextColor, for: [.disabled])
|
||||
|
||||
self.highligthedChanged = { [weak self] value in
|
||||
if let strongSelf = self {
|
||||
if value {
|
||||
if strongSelf.backgroundNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
|
||||
}
|
||||
strongSelf.backgroundNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.backgroundNode.alpha = 1.0
|
||||
} else if !strongSelf.backgroundNode.alpha.isZero {
|
||||
strongSelf.backgroundNode.alpha = 0.0
|
||||
strongSelf.backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25)
|
||||
}
|
||||
}
|
||||
var text: String {
|
||||
get {
|
||||
return self.textInputNode.textField.text ?? ""
|
||||
}
|
||||
set {
|
||||
self.textInputNode.textField.text = newValue
|
||||
self.placeholderNode.isHidden = !newValue.isEmpty
|
||||
}
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside)
|
||||
var placeholder: String = "" {
|
||||
didSet {
|
||||
self.placeholderNode.attributedText = NSAttributedString(string: self.placeholder, font: Font.regular(17.0), textColor: self.theme.actionSheet.inputPlaceholderColor)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func pressed() {
|
||||
self.action.action()
|
||||
init(theme: PresentationTheme, placeholder: String) {
|
||||
self.theme = theme
|
||||
|
||||
self.backgroundNode = ASImageNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
self.backgroundNode.displaysAsynchronously = false
|
||||
self.backgroundNode.displayWithoutProcessing = true
|
||||
self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: 12.0, color: theme.actionSheet.inputHollowBackgroundColor, strokeColor: theme.actionSheet.inputBorderColor, strokeWidth: 1.0)
|
||||
|
||||
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()
|
||||
|
||||
self.textInputNode.textField.delegate = self
|
||||
self.textInputNode.textField.addTarget(self, action: #selector(self.textDidChange), for: .editingChanged)
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.textInputNode)
|
||||
self.addSubnode(self.placeholderNode)
|
||||
}
|
||||
|
||||
override func layout() {
|
||||
super.layout()
|
||||
func updateTheme(_ theme: PresentationTheme) {
|
||||
self.theme = theme
|
||||
|
||||
self.backgroundNode.frame = self.bounds
|
||||
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?()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func clearPressed() {
|
||||
self.textInputNode.textField.text = nil
|
||||
self.deactivateInput()
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,14 +159,13 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
|
||||
private let textNode: ASTextNode
|
||||
|
||||
private let actionNodesSeparator: ASDisplayNode
|
||||
private let actionNodes: [BotCheckoutPasswordAlertActionNode]
|
||||
private let actionNodes: [TextAlertContentActionNode]
|
||||
private let actionVerticalSeparators: [ASDisplayNode]
|
||||
|
||||
private let cancelActionNode: BotCheckoutPasswordAlertActionNode
|
||||
private let doneActionNode: BotCheckoutPasswordAlertActionNode
|
||||
private let cancelActionNode: TextAlertContentActionNode
|
||||
private let doneActionNode: TextAlertContentActionNode
|
||||
|
||||
private let textFieldNodeBackground: ASImageNode
|
||||
private let textFieldNode: TextFieldNode
|
||||
let inputFieldNode: BotCheckoutPassworInputFieldNode
|
||||
|
||||
private var validLayout: CGSize?
|
||||
private var isVerifying = false
|
||||
@ -99,6 +179,8 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
|
||||
self.requiresBiometrics = requiresBiometrics
|
||||
self.completion = completion
|
||||
|
||||
let alertTheme = AlertControllerTheme(presentationTheme: theme, fontSize: .regular)
|
||||
|
||||
let titleNode = ASTextNode()
|
||||
titleNode.attributedText = NSAttributedString(string: strings.Checkout_PasswordEntry_Title, font: Font.semibold(17.0), textColor: theme.actionSheet.primaryTextColor, paragraphAlignment: .center)
|
||||
titleNode.displaysAsynchronously = false
|
||||
@ -112,16 +194,18 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
|
||||
self.textNode.displaysAsynchronously = false
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
|
||||
self.inputFieldNode = BotCheckoutPassworInputFieldNode(theme: theme, placeholder: passwordTip ?? "")
|
||||
|
||||
self.actionNodesSeparator = ASDisplayNode()
|
||||
self.actionNodesSeparator.isLayerBacked = true
|
||||
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()
|
||||
}))
|
||||
|
||||
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?()
|
||||
}))
|
||||
|
||||
@ -138,26 +222,6 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
|
||||
}
|
||||
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()
|
||||
|
||||
self.addSubnode(self.titleNode)
|
||||
@ -173,11 +237,14 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
|
||||
self.addSubnode(separatorNode)
|
||||
}
|
||||
|
||||
self.addSubnode(self.textFieldNodeBackground)
|
||||
self.addSubnode(self.textFieldNode)
|
||||
|
||||
self.textFieldNode.textField.addTarget(self, action: #selector(self.textFieldChanged(_:)), for: .editingChanged)
|
||||
self.addSubnode(self.inputFieldNode)
|
||||
|
||||
self.inputFieldNode.textChanged = { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateState()
|
||||
}
|
||||
}
|
||||
|
||||
self.updateState()
|
||||
|
||||
doneImpl = { [weak self] in
|
||||
@ -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)
|
||||
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 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))
|
||||
self.textFieldNodeBackground.frame = textFieldBackgroundFrame
|
||||
self.textFieldNode.frame = textFieldBackgroundFrame.offsetBy(dx: 0.0, dy: 0.0).insetBy(dx: 4.0, dy: 0.0)
|
||||
let inputFieldWidth = resultSize.width
|
||||
let inputFieldHeight = self.inputFieldNode.updateLayout(width: inputFieldWidth, transition: transition)
|
||||
transition.updateFrame(node: self.inputFieldNode, frame: CGRect(x: 0.0, y: resultSize.height - 36.0 - actionsHeight - insets.bottom, width: resultSize.width, height: inputFieldHeight))
|
||||
|
||||
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 {
|
||||
self.textFieldNode.textField.becomeFirstResponder()
|
||||
self.inputFieldNode.activateInput()
|
||||
}
|
||||
|
||||
return resultSize
|
||||
@ -262,24 +327,15 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
|
||||
|
||||
private func updateState() {
|
||||
var enabled = true
|
||||
|
||||
if self.isVerifying {
|
||||
if self.isVerifying || self.inputFieldNode.text.isEmpty {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
if let text = self.textFieldNode.textField.text {
|
||||
if text.isEmpty {
|
||||
enabled = false
|
||||
}
|
||||
} else {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
self.doneActionNode.isEnabled = enabled
|
||||
self.doneActionNode.actionEnabled = enabled
|
||||
}
|
||||
|
||||
private func verify() {
|
||||
guard let text = self.textFieldNode.textField.text, !text.isEmpty else {
|
||||
let text = self.inputFieldNode.text
|
||||
guard !text.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -290,8 +346,7 @@ private final class BotCheckoutPasswordAlertContentNode: AlertContentNode {
|
||||
}
|
||||
}, error: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.textFieldNodeBackground.layer.addShakeAnimation()
|
||||
strongSelf.textFieldNode.layer.addShakeAnimation()
|
||||
strongSelf.inputFieldNode.shake()
|
||||
strongSelf.hapticFeedback.error()
|
||||
strongSelf.isVerifying = false
|
||||
strongSelf.updateState()
|
||||
|
@ -45,6 +45,7 @@
|
||||
_explicit = explicit;
|
||||
|
||||
_sliderView = [[TGPhotoEditorSliderView alloc] initWithFrame:CGRectZero];
|
||||
_sliderView.enablePanHandling = true;
|
||||
if (editorItem.segmented)
|
||||
_sliderView.positionsCount = (NSInteger)editorItem.maximumValue + 1;
|
||||
_sliderView.minimumValue = editorItem.minimumValue;
|
||||
|
@ -68,6 +68,7 @@ const CGFloat TGPhotoEditorSliderViewInternalMargin = 7.0f;
|
||||
|
||||
_panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
|
||||
_panGestureRecognizer.enabled = false;
|
||||
_panGestureRecognizer.delegate = self;
|
||||
[self addGestureRecognizer:_panGestureRecognizer];
|
||||
|
||||
_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
|
||||
{
|
||||
if (!_enablePanHandling) {
|
||||
|
@ -266,6 +266,7 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
|
||||
_portraitCollectionView.toolsDataSource = self;
|
||||
_portraitCollectionView.interactionBegan = _interactionBegan;
|
||||
_portraitCollectionView.interactionEnded = _interactionEnded;
|
||||
_portraitCollectionView.canCancelContentTouches = true;
|
||||
[_portraitToolsWrapperView addSubview:_portraitCollectionView];
|
||||
|
||||
if (!TGIsPad())
|
||||
@ -278,6 +279,7 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
|
||||
_landscapeCollectionView.toolsDataSource = self;
|
||||
_landscapeCollectionView.interactionBegan = _interactionBegan;
|
||||
_landscapeCollectionView.interactionEnded = _interactionEnded;
|
||||
_landscapeCollectionView.canCancelContentTouches = true;
|
||||
[_landscapeToolsWrapperView addSubview:_landscapeCollectionView];
|
||||
}
|
||||
|
||||
|
@ -93,6 +93,17 @@ private final class TabBarItemNode: ASDisplayNode {
|
||||
var contentWidth: CGFloat?
|
||||
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 pointerInteraction: PointerInteraction?
|
||||
@ -101,6 +112,11 @@ private final class TabBarItemNode: ASDisplayNode {
|
||||
self.extractedContainerNode = ContextExtractedContentContainingNode()
|
||||
self.containerNode = ContextControllerSourceNode()
|
||||
|
||||
self.ringImageNode = ASImageNode()
|
||||
self.ringImageNode.isUserInteractionEnabled = false
|
||||
self.ringImageNode.displayWithoutProcessing = true
|
||||
self.ringImageNode.displaysAsynchronously = false
|
||||
|
||||
self.imageNode = ASImageNode()
|
||||
self.imageNode.isUserInteractionEnabled = false
|
||||
self.imageNode.displayWithoutProcessing = true
|
||||
@ -136,6 +152,7 @@ private final class TabBarItemNode: ASDisplayNode {
|
||||
|
||||
self.isAccessibilityElement = true
|
||||
|
||||
self.extractedContainerNode.contentNode.addSubnode(self.ringImageNode)
|
||||
self.extractedContainerNode.contentNode.addSubnode(self.textImageNode)
|
||||
self.extractedContainerNode.contentNode.addSubnode(self.imageNode)
|
||||
self.extractedContainerNode.contentNode.addSubnode(self.animationContainerNode)
|
||||
@ -150,6 +167,7 @@ private final class TabBarItemNode: ASDisplayNode {
|
||||
guard let strongSelf = self else {
|
||||
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.animationNode, 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
|
||||
self?.swipeAction(i, direction)
|
||||
})
|
||||
if item.item.ringSelection {
|
||||
node.ringColor = self.theme.tabBarSelectedIconColor
|
||||
} else {
|
||||
node.ringColor = nil
|
||||
}
|
||||
|
||||
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 (image, imageContentWidth): (UIImage, CGFloat)
|
||||
@ -507,6 +531,12 @@ class TabBarNode: ASDisplayNode {
|
||||
|
||||
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 previousTextImageSize = node.textImageNode.image?.size ?? CGSize()
|
||||
if let selectedIndex = self.selectedIndex, selectedIndex == index {
|
||||
@ -524,7 +554,11 @@ class TabBarNode: ASDisplayNode {
|
||||
node.animationNode.setOverlayColor(self.theme.tabBarSelectedIconColor, replace: true, animated: false)
|
||||
node.animationNode.updateLayout(size: CGSize(width: 51.0, height: 51.0))
|
||||
} 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)
|
||||
if item.item.ringSelection {
|
||||
(image, imageContentWidth) = (item.item.selectedImage ?? UIImage(), item.item.selectedImage?.size.width ?? 0.0)
|
||||
} 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)
|
||||
}
|
||||
|
||||
node.animationNode.isHidden = true
|
||||
node.animationNode.visibility = false
|
||||
@ -539,9 +573,22 @@ class TabBarNode: ASDisplayNode {
|
||||
node.contextImageNode.image = contextImage
|
||||
node.contentWidth = max(contentWidth, imageContentWidth)
|
||||
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 {
|
||||
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 (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.contentWidth = max(contentWidth, imageContentWidth)
|
||||
node.isSelected = false
|
||||
|
||||
ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut).updateTransformScale(node: node.ringImageNode, scale: 0.5)
|
||||
}
|
||||
|
||||
let updatedImageSize = node.imageNode.image?.size ?? CGSize()
|
||||
@ -647,18 +696,33 @@ class TabBarNode: ASDisplayNode {
|
||||
node.containerNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
|
||||
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.imageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
|
||||
if node.ringColor == nil {
|
||||
node.imageNode.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.contextTextImageNode.frame = CGRect(origin: CGPoint(), size: nodeFrame.size)
|
||||
|
||||
|
||||
let scaleFactor: CGFloat = horizontal ? 0.8 : 1.0
|
||||
node.animationContainerNode.subnodeTransform = CATransform3DMakeScale(scaleFactor, scaleFactor, 1.0)
|
||||
let animationOffset: CGPoint = self.tabBarItems[i].item.animationOffset
|
||||
let ringImageFrame: CGRect
|
||||
let imageFrame: CGRect
|
||||
if horizontal {
|
||||
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 {
|
||||
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 {
|
||||
|
@ -367,7 +367,12 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode {
|
||||
self.expandBackgroundNode.image = generateExpandBackground(size: expandBackgroundFrame.size, color: presentationData.theme.list.itemBlocksBackgroundColor)
|
||||
|
||||
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
|
||||
|
||||
|
@ -2010,7 +2010,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
var requestAvatarExpansion: ((Bool, [AvatarGalleryEntry], AvatarGalleryEntry?, (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?) -> Void)?
|
||||
var requestOpenAvatarForEditing: ((Bool) -> Void)?
|
||||
var cancelUpload: (() -> Void)?
|
||||
var requestUpdateLayout: (() -> Void)?
|
||||
var requestUpdateLayout: ((Bool) -> Void)?
|
||||
var animateOverlaysFadeIn: (() -> Void)?
|
||||
|
||||
var displayAvatarContextMenu: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||
@ -2100,7 +2100,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
super.init()
|
||||
|
||||
requestUpdateLayoutImpl = { [weak self] in
|
||||
self?.requestUpdateLayout?()
|
||||
self?.requestUpdateLayout?(false)
|
||||
}
|
||||
|
||||
|
||||
|
@ -460,7 +460,7 @@ private final class PeerInfoInteraction {
|
||||
let performMemberAction: (PeerInfoMember, PeerInfoMemberAction) -> Void
|
||||
let openPeerInfoContextMenu: (PeerInfoContextSubject, ASDisplayNode) -> Void
|
||||
let performBioLinkAction: (TextLinkItemActionType, TextLinkItem) -> Void
|
||||
let requestLayout: () -> Void
|
||||
let requestLayout: (Bool) -> Void
|
||||
let openEncryptionKey: () -> Void
|
||||
let openSettings: (PeerInfoSettingsSection) -> Void
|
||||
let switchToAccount: (AccountRecordId) -> Void
|
||||
@ -502,7 +502,7 @@ private final class PeerInfoInteraction {
|
||||
performMemberAction: @escaping (PeerInfoMember, PeerInfoMemberAction) -> Void,
|
||||
openPeerInfoContextMenu: @escaping (PeerInfoContextSubject, ASDisplayNode) -> Void,
|
||||
performBioLinkAction: @escaping (TextLinkItemActionType, TextLinkItem) -> Void,
|
||||
requestLayout: @escaping () -> Void,
|
||||
requestLayout: @escaping (Bool) -> Void,
|
||||
openEncryptionKey: @escaping () -> Void,
|
||||
openSettings: @escaping (PeerInfoSettingsSection) -> Void,
|
||||
switchToAccount: @escaping (AccountRecordId) -> Void,
|
||||
@ -864,7 +864,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
}, longTapAction: { sourceNode in
|
||||
interaction.openPeerInfoContextMenu(.phone(formattedPhone), sourceNode)
|
||||
}, requestLayout: {
|
||||
interaction.requestLayout()
|
||||
interaction.requestLayout(false)
|
||||
}))
|
||||
}
|
||||
if let username = user.username {
|
||||
@ -875,21 +875,21 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
}, iconAction: {
|
||||
interaction.openQrCode()
|
||||
}, requestLayout: {
|
||||
interaction.requestLayout()
|
||||
interaction.requestLayout(false)
|
||||
}))
|
||||
}
|
||||
if let cachedData = data.cachedData as? CachedUserData {
|
||||
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: {
|
||||
interaction.requestLayout()
|
||||
interaction.requestLayout(false)
|
||||
}))
|
||||
} 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: {
|
||||
interaction.requestLayout()
|
||||
interaction.requestLayout(false)
|
||||
}))
|
||||
} 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: {
|
||||
interaction.requestLayout()
|
||||
interaction.requestLayout(false)
|
||||
}))
|
||||
}
|
||||
}
|
||||
@ -982,7 +982,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
}, iconAction: {
|
||||
interaction.openQrCode()
|
||||
}, requestLayout: {
|
||||
interaction.requestLayout()
|
||||
interaction.requestLayout(false)
|
||||
}))
|
||||
}
|
||||
if let cachedData = data.cachedData as? CachedChannelData {
|
||||
@ -1011,7 +1011,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
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: {
|
||||
interaction.requestLayout()
|
||||
interaction.requestLayout(true)
|
||||
}))
|
||||
}
|
||||
|
||||
@ -1056,7 +1056,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
|
||||
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: {
|
||||
interaction.requestLayout()
|
||||
interaction.requestLayout(true)
|
||||
}))
|
||||
}
|
||||
}
|
||||
@ -1776,8 +1776,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
performBioLinkAction: { [weak self] action, item in
|
||||
self?.performBioLinkAction(action: action, item: item)
|
||||
},
|
||||
requestLayout: { [weak self] in
|
||||
self?.requestLayout()
|
||||
requestLayout: { [weak self] animated in
|
||||
self?.requestLayout(animated: animated)
|
||||
},
|
||||
openEncryptionKey: { [weak self] in
|
||||
self?.openEncryptionKey()
|
||||
@ -2571,12 +2571,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
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 {
|
||||
return
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
private func requestLayout() {
|
||||
self.headerNode.requestUpdateLayout?()
|
||||
private func requestLayout(animated: Bool = false) {
|
||||
self.headerNode.requestUpdateLayout?(animated)
|
||||
}
|
||||
|
||||
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) {
|
||||
return signal
|
||||
|> map { imageVersions -> (UIImage, UIImage)? in
|
||||
let image = imageVersions?.0
|
||||
if let image = image, let selectedImage = generateImage(size, rotatedContext: { size, context in
|
||||
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))
|
||||
if let image = imageVersions?.0 {
|
||||
return (image.withRenderingMode(.alwaysOriginal), image.withRenderingMode(.alwaysOriginal))
|
||||
} else {
|
||||
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)
|
||||
})?.withRenderingMode(.alwaysOriginal)
|
||||
|
||||
let selectedImage = generateImage(size, rotatedContext: { size, context in
|
||||
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))
|
||||
if let image = image {
|
||||
subscriber.putNext((image, image))
|
||||
} else {
|
||||
subscriber.putNext(nil)
|
||||
}
|
||||
@ -7884,6 +7860,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen {
|
||||
strongSelf.tabBarItem.image = image
|
||||
strongSelf.tabBarItem.selectedImage = selectedImage
|
||||
strongSelf.tabBarItem.animationName = isAvatar ? nil : "TabSettings"
|
||||
strongSelf.tabBarItem.ringSelection = isAvatar
|
||||
strongSelf.tabBarItem.badgeValue = badgeValue
|
||||
}
|
||||
})
|
||||
|
@ -50,5 +50,6 @@ NSInteger UITabBarItem_addSetBadgeListener(UITabBarItem * _Nonnull item, UITabBa
|
||||
|
||||
@property (nonatomic, strong) NSString * _Nullable animationName;
|
||||
@property (nonatomic, assign) CGPoint animationOffset;
|
||||
@property (nonatomic, assign) bool ringSelection;
|
||||
|
||||
@end
|
||||
|
@ -18,6 +18,7 @@ static const void *setBadgeListenerBagKey = &setBadgeListenerBagKey;
|
||||
static const void *badgeKey = &badgeKey;
|
||||
static const void *animationNameKey = &animationNameKey;
|
||||
static const void *animationOffsetKey = &animationOffsetKey;
|
||||
static const void *ringSelectionKey = &ringSelectionKey;
|
||||
|
||||
@implementation UINavigationItem (Proxy)
|
||||
|
||||
@ -419,4 +420,12 @@ NSInteger UITabBarItem_addSetBadgeListener(UITabBarItem *item, UITabBarItemSetBa
|
||||
return ((NSValue *)[self associatedObjectForKey:animationOffsetKey]).CGPointValue;
|
||||
}
|
||||
|
||||
- (void)setRingSelection:(bool)ringSelection {
|
||||
[self setAssociatedObject:@(ringSelection) forKey:ringSelectionKey];
|
||||
}
|
||||
|
||||
- (bool)ringSelection {
|
||||
return ((NSNumber *)[self associatedObjectForKey:ringSelectionKey]).boolValue;
|
||||
}
|
||||
|
||||
@end
|
||||
|
Loading…
x
Reference in New Issue
Block a user