mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Add gradient backgrounds in passcode entry screen
This commit is contained in:
parent
ad98e74477
commit
4fcb54c687
@ -184,7 +184,7 @@ public final class AppLockContextImpl: AppLockContext {
|
||||
}
|
||||
passcodeController.ensureInputFocused()
|
||||
} else {
|
||||
let passcodeController = PasscodeEntryController(applicationBindings: strongSelf.applicationBindings, accountManager: strongSelf.accountManager, appLockContext: strongSelf, presentationData: presentationData, presentationDataSignal: strongSelf.presentationDataSignal, challengeData: accessChallengeData.data, biometrics: biometrics, arguments: PasscodeEntryControllerPresentationArguments(animated: !becameActiveRecently, lockIconInitialFrame: { [weak self] in
|
||||
let passcodeController = PasscodeEntryController(applicationBindings: strongSelf.applicationBindings, accountManager: strongSelf.accountManager, appLockContext: strongSelf, presentationData: presentationData, presentationDataSignal: strongSelf.presentationDataSignal, statusBarHost: window?.statusBarHost, challengeData: accessChallengeData.data, biometrics: biometrics, arguments: PasscodeEntryControllerPresentationArguments(animated: !becameActiveRecently, lockIconInitialFrame: { [weak self] in
|
||||
if let lockViewFrame = lockIconInitialFrame() {
|
||||
return lockViewFrame
|
||||
} else {
|
||||
|
@ -237,7 +237,7 @@ public class Window1 {
|
||||
|
||||
private var deviceMetrics: DeviceMetrics
|
||||
|
||||
private let statusBarHost: StatusBarHost?
|
||||
public let statusBarHost: StatusBarHost?
|
||||
private let keyboardManager: KeyboardManager?
|
||||
private let keyboardViewManager: KeyboardViewManager?
|
||||
private var statusBarChangeObserver: AnyObject?
|
||||
|
@ -219,16 +219,6 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
private struct PhaseTransitionKey: Hashable {
|
||||
var width: Int
|
||||
var height: Int
|
||||
var fromPhase: Int
|
||||
var toPhase: Int
|
||||
var numberOfFrames: Int
|
||||
var curve: ContainedViewLayoutTransitionCurve
|
||||
}
|
||||
|
||||
private let cloneNodes = SparseBag<Weak<CloneNode>>()
|
||||
|
||||
private let useSharedAnimationPhase: Bool
|
||||
@ -259,7 +249,7 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
deinit {
|
||||
}
|
||||
|
||||
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition, extendAnimation: Bool = false) {
|
||||
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition, extendAnimation: Bool = false, backwards: Bool = false) {
|
||||
let sizeUpdated = self.validLayout != size
|
||||
self.validLayout = size
|
||||
|
||||
@ -273,7 +263,20 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
self.invalidated = false
|
||||
|
||||
var steps: [[CGPoint]] = []
|
||||
if extendAnimation {
|
||||
if backwards {
|
||||
let phaseCount = extendAnimation ? 4 : 1
|
||||
self.phase = (self.phase + phaseCount) % 8
|
||||
self.validPhase = self.phase
|
||||
|
||||
var stepPhase = self.phase - phaseCount
|
||||
if stepPhase < 0 {
|
||||
stepPhase = 7
|
||||
}
|
||||
for _ in 0 ... phaseCount {
|
||||
steps.append(gatherPositions(shiftArray(array: GradientBackgroundNode.basePositions, offset: stepPhase)))
|
||||
stepPhase = (stepPhase + 1) % 8
|
||||
}
|
||||
} else if extendAnimation {
|
||||
let phaseCount = 4
|
||||
var stepPhase = (self.phase + phaseCount) % 8
|
||||
for _ in 0 ... phaseCount {
|
||||
@ -332,13 +335,13 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
let animation = CAKeyframeAnimation(keyPath: "contents")
|
||||
animation.values = images.map { $0.cgImage! }
|
||||
animation.duration = duration * UIView.animationDurationFactor()
|
||||
if extendAnimation {
|
||||
if backwards || extendAnimation {
|
||||
animation.calculationMode = .discrete
|
||||
} else {
|
||||
animation.calculationMode = .linear
|
||||
}
|
||||
animation.isRemovedOnCompletion = true
|
||||
if extendAnimation {
|
||||
if extendAnimation && !backwards {
|
||||
animation.fillMode = .backwards
|
||||
animation.beginTime = self.contentView.layer.convertTime(CACurrentMediaTime(), from: nil) + 0.25
|
||||
}
|
||||
@ -347,19 +350,13 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
self.contentView.layer.add(animation, forKey: "contents")
|
||||
|
||||
if !self.cloneNodes.isEmpty {
|
||||
let animation = CAKeyframeAnimation(keyPath: "contents")
|
||||
animation.values = dimmedImages.map { $0.cgImage! }
|
||||
animation.duration = duration * UIView.animationDurationFactor()
|
||||
if extendAnimation {
|
||||
animation.calculationMode = .discrete
|
||||
} else {
|
||||
animation.calculationMode = .linear
|
||||
}
|
||||
animation.isRemovedOnCompletion = true
|
||||
if extendAnimation {
|
||||
animation.fillMode = .backwards
|
||||
animation.beginTime = self.contentView.layer.convertTime(CACurrentMediaTime(), from: nil) + 0.25
|
||||
}
|
||||
let cloneAnimation = CAKeyframeAnimation(keyPath: "contents")
|
||||
cloneAnimation.values = dimmedImages.map { $0.cgImage! }
|
||||
cloneAnimation.duration = animation.duration
|
||||
cloneAnimation.calculationMode = animation.calculationMode
|
||||
cloneAnimation.isRemovedOnCompletion = animation.isRemovedOnCompletion
|
||||
cloneAnimation.fillMode = animation.fillMode
|
||||
cloneAnimation.beginTime = animation.beginTime
|
||||
|
||||
self._dimmedImage = dimmedImages.last
|
||||
|
||||
@ -367,7 +364,7 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
if let value = cloneNode.value {
|
||||
value.image = dimmedImages.last
|
||||
value.layer.removeAnimation(forKey: "contents")
|
||||
value.layer.add(animation, forKey: "contents")
|
||||
value.layer.add(cloneAnimation, forKey: "contents")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -422,12 +419,12 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
public func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool = false) {
|
||||
public func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool = false, backwards: Bool = false) {
|
||||
guard case let .animated(duration, _) = transition, duration > 0.001 else {
|
||||
return
|
||||
}
|
||||
|
||||
if extendAnimation {
|
||||
if extendAnimation || backwards {
|
||||
self.invalidated = true
|
||||
} else {
|
||||
if self.phase == 0 {
|
||||
@ -440,7 +437,7 @@ public final class GradientBackgroundNode: ASDisplayNode {
|
||||
GradientBackgroundNode.sharedPhase = self.phase
|
||||
}
|
||||
if let size = self.validLayout {
|
||||
self.updateLayout(size: size, transition: transition, extendAnimation: extendAnimation)
|
||||
self.updateLayout(size: size, transition: transition, extendAnimation: extendAnimation, backwards: backwards)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ swift_library(
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
"//submodules/PasscodeInputFieldNode:PasscodeInputFieldNode",
|
||||
"//submodules/MonotonicTime:MonotonicTime",
|
||||
"//submodules/GradientBackground:GradientBackground",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -1,19 +1,40 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import ImageBlur
|
||||
import FastBlur
|
||||
import GradientBackground
|
||||
|
||||
protocol PasscodeBackground {
|
||||
var size: CGSize { get }
|
||||
var backgroundImage: UIImage { get }
|
||||
var foregroundImage: UIImage { get }
|
||||
var backgroundImage: UIImage? { get }
|
||||
var foregroundImage: UIImage? { get }
|
||||
|
||||
func makeBackgroundNode() -> ASDisplayNode?
|
||||
}
|
||||
|
||||
final class CustomPasscodeBackground: PasscodeBackground {
|
||||
private let colors: [UIColor]
|
||||
|
||||
public private(set) var size: CGSize
|
||||
public private(set) var backgroundImage: UIImage? = nil
|
||||
public private(set) var foregroundImage: UIImage? = nil
|
||||
|
||||
init(size: CGSize, colors: [UIColor]) {
|
||||
self.size = size
|
||||
self.colors = colors
|
||||
}
|
||||
|
||||
func makeBackgroundNode() -> ASDisplayNode? {
|
||||
return createGradientBackgroundNode(colors: self.colors)
|
||||
}
|
||||
}
|
||||
|
||||
final class GradientPasscodeBackground: PasscodeBackground {
|
||||
public private(set) var size: CGSize
|
||||
public private(set) var backgroundImage: UIImage
|
||||
public private(set) var foregroundImage: UIImage
|
||||
public private(set) var backgroundImage: UIImage?
|
||||
public private(set) var foregroundImage: UIImage?
|
||||
|
||||
init(size: CGSize, backgroundColors: (UIColor, UIColor), buttonColor: UIColor) {
|
||||
self.size = size
|
||||
@ -35,12 +56,16 @@ final class GradientPasscodeBackground: PasscodeBackground {
|
||||
context.fill(bounds)
|
||||
})!
|
||||
}
|
||||
|
||||
func makeBackgroundNode() -> ASDisplayNode? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
final class ImageBasedPasscodeBackground: PasscodeBackground {
|
||||
public private(set) var size: CGSize
|
||||
public private(set) var backgroundImage: UIImage
|
||||
public private(set) var foregroundImage: UIImage
|
||||
public private(set) var backgroundImage: UIImage?
|
||||
public private(set) var foregroundImage: UIImage?
|
||||
|
||||
init(image: UIImage, size: CGSize) {
|
||||
self.size = size
|
||||
@ -82,4 +107,8 @@ final class ImageBasedPasscodeBackground: PasscodeBackground {
|
||||
}
|
||||
self.backgroundImage = backgroundContext.generateImage()!
|
||||
}
|
||||
|
||||
func makeBackgroundNode() -> ASDisplayNode? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,10 @@ public final class PasscodeEntryController: ViewController {
|
||||
private var inBackground: Bool = false
|
||||
private var inBackgroundDisposable: Disposable?
|
||||
|
||||
public init(applicationBindings: TelegramApplicationBindings, accountManager: AccountManager, appLockContext: AppLockContext, presentationData: PresentationData, presentationDataSignal: Signal<PresentationData, NoError>, challengeData: PostboxAccessChallengeData, biometrics: PasscodeEntryControllerBiometricsMode, arguments: PasscodeEntryControllerPresentationArguments) {
|
||||
private let statusBarHost: StatusBarHost?
|
||||
private var previousStatusBarStyle: UIStatusBarStyle?
|
||||
|
||||
public init(applicationBindings: TelegramApplicationBindings, accountManager: AccountManager, appLockContext: AppLockContext, presentationData: PresentationData, presentationDataSignal: Signal<PresentationData, NoError>, statusBarHost: StatusBarHost?, challengeData: PostboxAccessChallengeData, biometrics: PasscodeEntryControllerBiometricsMode, arguments: PasscodeEntryControllerPresentationArguments) {
|
||||
self.applicationBindings = applicationBindings
|
||||
self.accountManager = accountManager
|
||||
self.appLockContext = appLockContext
|
||||
@ -68,10 +71,12 @@ public final class PasscodeEntryController: ViewController {
|
||||
self.biometrics = biometrics
|
||||
self.arguments = arguments
|
||||
|
||||
self.statusBarHost = statusBarHost
|
||||
self.previousStatusBarStyle = statusBarHost?.statusBarStyle
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
self.statusBar.statusBarStyle = .White
|
||||
statusBarHost?.setStatusBarStyle(.lightContent, animated: true)
|
||||
|
||||
self.presentationDataDisposable = (presentationDataSignal
|
||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||
@ -128,7 +133,7 @@ public final class PasscodeEntryController: ViewController {
|
||||
} else {
|
||||
biometricsType = nil
|
||||
}
|
||||
self.displayNode = PasscodeEntryControllerNode(accountManager: self.accountManager, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, passcodeType: passcodeType, biometricsType: biometricsType, arguments: self.arguments, statusBar: self.statusBar, modalPresentation: self.arguments.modalPresentation)
|
||||
self.displayNode = PasscodeEntryControllerNode(accountManager: self.accountManager, presentationData: self.presentationData, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, passcodeType: passcodeType, biometricsType: biometricsType, arguments: self.arguments, modalPresentation: self.arguments.modalPresentation)
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
let _ = (self.appLockContext.invalidAttempts
|
||||
@ -271,6 +276,9 @@ public final class PasscodeEntryController: ViewController {
|
||||
}
|
||||
|
||||
public override func dismiss(completion: (() -> Void)? = nil) {
|
||||
if let statusBarHost = self.statusBarHost, let previousStatusBarStyle = self.previousStatusBarStyle {
|
||||
statusBarHost.setStatusBarStyle(previousStatusBarStyle, animated: true)
|
||||
}
|
||||
self.view.endEditing(true)
|
||||
self.controllerNode.animateOut { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
|
@ -12,6 +12,7 @@ import LocalAuth
|
||||
import AppBundle
|
||||
import PasscodeInputFieldNode
|
||||
import MonotonicTime
|
||||
import GradientBackground
|
||||
|
||||
private let titleFont = Font.regular(20.0)
|
||||
private let subtitleFont = Font.regular(15.0)
|
||||
@ -19,6 +20,7 @@ private let buttonFont = Font.regular(17.0)
|
||||
|
||||
final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
private let accountManager: AccountManager
|
||||
private var presentationData: PresentationData
|
||||
private var theme: PresentationTheme
|
||||
private var strings: PresentationStrings
|
||||
private var wallpaper: TelegramWallpaper
|
||||
@ -27,11 +29,11 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
private let arguments: PasscodeEntryControllerPresentationArguments
|
||||
private var background: PasscodeBackground?
|
||||
|
||||
private let statusBar: StatusBar
|
||||
|
||||
private let modalPresentation: Bool
|
||||
|
||||
private let backgroundNode: ASImageNode
|
||||
private var backgroundCustomNode: ASDisplayNode?
|
||||
private let backgroundDimNode: ASDisplayNode
|
||||
private let backgroundImageNode: ASImageNode
|
||||
private let iconNode: PasscodeLockIconNode
|
||||
private let titleNode: PasscodeEntryLabelNode
|
||||
private let inputFieldNode: PasscodeInputFieldNode
|
||||
@ -52,20 +54,24 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
var checkPasscode: ((String) -> Void)?
|
||||
var requestBiometrics: (() -> Void)?
|
||||
|
||||
init(accountManager: AccountManager, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, passcodeType: PasscodeEntryFieldType, biometricsType: LocalAuthBiometricAuthentication?, arguments: PasscodeEntryControllerPresentationArguments, statusBar: StatusBar, modalPresentation: Bool) {
|
||||
init(accountManager: AccountManager, presentationData: PresentationData, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, passcodeType: PasscodeEntryFieldType, biometricsType: LocalAuthBiometricAuthentication?, arguments: PasscodeEntryControllerPresentationArguments, modalPresentation: Bool) {
|
||||
self.accountManager = accountManager
|
||||
self.presentationData = presentationData
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.wallpaper = wallpaper
|
||||
self.passcodeType = passcodeType
|
||||
self.biometricsType = biometricsType
|
||||
self.arguments = arguments
|
||||
self.statusBar = statusBar
|
||||
self.modalPresentation = modalPresentation
|
||||
|
||||
self.backgroundNode = ASImageNode()
|
||||
self.backgroundNode.contentMode = .scaleToFill
|
||||
self.backgroundImageNode = ASImageNode()
|
||||
self.backgroundImageNode.contentMode = .scaleToFill
|
||||
|
||||
self.backgroundDimNode = ASDisplayNode()
|
||||
self.backgroundDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.15)
|
||||
self.backgroundDimNode.isHidden = true
|
||||
|
||||
self.iconNode = PasscodeLockIconNode()
|
||||
self.titleNode = PasscodeEntryLabelNode()
|
||||
self.inputFieldNode = PasscodeInputFieldNode(color: .white, accentColor: .white, fieldType: passcodeType, keyboardAppearance: .dark, useCustomNumpad: true)
|
||||
@ -86,7 +92,12 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
self.iconNode.unlockedColor = theme.rootController.navigationBar.primaryTextColor
|
||||
|
||||
self.keyboardNode.charactedEntered = { [weak self] character in
|
||||
self?.inputFieldNode.append(character)
|
||||
if let strongSelf = self {
|
||||
strongSelf.inputFieldNode.append(character)
|
||||
if let gradientNode = strongSelf.backgroundCustomNode as? GradientBackgroundNode {
|
||||
gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring))
|
||||
}
|
||||
}
|
||||
}
|
||||
self.inputFieldNode.complete = { [weak self] passcode in
|
||||
guard let strongSelf = self else {
|
||||
@ -111,7 +122,8 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.backgroundImageNode)
|
||||
self.addSubnode(self.backgroundDimNode)
|
||||
self.addSubnode(self.iconNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.inputFieldNode)
|
||||
@ -146,7 +158,10 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
|
||||
@objc private func deletePressed() {
|
||||
self.hapticFeedback.tap()
|
||||
self.inputFieldNode.delete()
|
||||
let result = self.inputFieldNode.delete()
|
||||
if result, let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
||||
gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), backwards: true)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func biometricsPressed() {
|
||||
@ -158,6 +173,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
func updatePresentationData(_ presentationData: PresentationData) {
|
||||
self.presentationData = presentationData
|
||||
self.theme = presentationData.theme
|
||||
self.strings = presentationData.strings
|
||||
self.wallpaper = presentationData.chatWallpaper
|
||||
@ -173,26 +189,42 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
|
||||
var size = validLayout.size
|
||||
let size = validLayout.size
|
||||
if let background = self.background, background.size == size {
|
||||
return
|
||||
}
|
||||
|
||||
switch self.wallpaper {
|
||||
case let .gradient(_, colors, _):
|
||||
self.background = CustomPasscodeBackground(size: size, colors: colors.compactMap { UIColor(rgb: $0) })
|
||||
case .image, .file:
|
||||
if let image = chatControllerBackgroundImage(theme: self.theme, wallpaper: self.wallpaper, mediaBox: self.accountManager.mediaBox, composed: false, knockoutMode: false) {
|
||||
self.background = ImageBasedPasscodeBackground(image: image, size: size)
|
||||
} else {
|
||||
self.background = GradientPasscodeBackground(size: size, backgroundColors: self.theme.passcode.backgroundColors.colors, buttonColor: self.theme.passcode.buttonColor)
|
||||
if case let .file(file) = self.wallpaper, !file.settings.colors.isEmpty {
|
||||
self.background = CustomPasscodeBackground(size: size, colors: file.settings.colors.compactMap { UIColor(rgb: $0) })
|
||||
} else {
|
||||
self.background = GradientPasscodeBackground(size: size, backgroundColors: self.theme.passcode.backgroundColors.colors, buttonColor: self.theme.passcode.buttonColor)
|
||||
}
|
||||
}
|
||||
default:
|
||||
self.background = GradientPasscodeBackground(size: size, backgroundColors: self.theme.passcode.backgroundColors.colors, buttonColor: self.theme.passcode.buttonColor)
|
||||
}
|
||||
|
||||
if let background = self.background {
|
||||
self.backgroundNode.image = background.backgroundImage
|
||||
self.keyboardNode.updateBackground(background)
|
||||
self.inputFieldNode.updateBackground(background.foregroundImage, size: background.size)
|
||||
self.backgroundCustomNode?.removeFromSupernode()
|
||||
self.backgroundCustomNode = nil
|
||||
|
||||
if let backgroundImage = background.backgroundImage {
|
||||
self.backgroundImageNode.image = backgroundImage
|
||||
self.backgroundDimNode.isHidden = true
|
||||
} else if let customBackgroundNode = background.makeBackgroundNode() {
|
||||
self.backgroundCustomNode = customBackgroundNode
|
||||
self.insertSubnode(customBackgroundNode, aboveSubnode: self.backgroundImageNode)
|
||||
self.backgroundDimNode.isHidden = false
|
||||
}
|
||||
self.keyboardNode.updateBackground(self.presentationData, background)
|
||||
self.inputFieldNode.updateBackground(background)
|
||||
}
|
||||
}
|
||||
|
||||
@ -263,7 +295,12 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
self.effectView.alpha = 1.0
|
||||
}
|
||||
})
|
||||
self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
self.backgroundImageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
||||
gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
self.backgroundDimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring), extendAnimation: true)
|
||||
}
|
||||
}
|
||||
self.titleNode.setAttributedText(NSAttributedString(string: self.strings.EnterPasscode_EnterPasscode, font: titleFont, textColor: .white), animation: .none)
|
||||
}
|
||||
@ -277,15 +314,17 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
self.effectView.alpha = 1.0
|
||||
}
|
||||
})
|
||||
self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
self.backgroundImageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
||||
gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
gradientNode.animateEvent(transition: .animated(duration: 0.35, curve: .spring))
|
||||
self.backgroundDimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
}
|
||||
if !iconFrame.isEmpty {
|
||||
self.iconNode.animateIn(fromScale: 0.416)
|
||||
self.iconNode.layer.animatePosition(from: iconFrame.center.offsetBy(dx: 6.0, dy: 6.0), to: self.iconNode.layer.position, duration: 0.45)
|
||||
}
|
||||
|
||||
self.statusBar.layer.removeAnimation(forKey: "opacity")
|
||||
self.statusBar.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
|
||||
self.subtitleNode.isHidden = true
|
||||
self.inputFieldNode.isHidden = true
|
||||
self.keyboardNode.isHidden = true
|
||||
@ -303,6 +342,9 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
|
||||
self.subtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
|
||||
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
||||
gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring))
|
||||
}
|
||||
self.inputFieldNode.animateIn()
|
||||
self.keyboardNode.animateIn()
|
||||
var biometricDelay = 0.3
|
||||
@ -323,7 +365,6 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
func animateOut(down: Bool = false, completion: @escaping () -> Void = {}) {
|
||||
self.statusBar.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
||||
self.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: down ? self.bounds.size.height : -self.bounds.size.height), duration: 0.2, removeOnCompletion: false, additive: true, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
@ -340,6 +381,10 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
self.iconNode.layer.addShakeAnimation(amplitude: -8.0, duration: 0.5, count: 6, decay: true)
|
||||
|
||||
self.hapticFeedback.error()
|
||||
|
||||
if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode {
|
||||
gradientNode.animateEvent(transition: .animated(duration: 1.5, curve: .spring), extendAnimation: true, backwards: true)
|
||||
}
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
@ -348,7 +393,14 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||
self.updateBackground()
|
||||
|
||||
let bounds = CGRect(origin: CGPoint(), size: layout.size)
|
||||
transition.updateFrame(node: self.backgroundNode, frame: bounds)
|
||||
transition.updateFrame(node: self.backgroundImageNode, frame: bounds)
|
||||
transition.updateFrame(node: self.backgroundDimNode, frame: bounds)
|
||||
if let backgroundCustomNode = self.backgroundCustomNode {
|
||||
transition.updateFrame(node: backgroundCustomNode, frame: bounds)
|
||||
if let gradientBackgroundNode = backgroundCustomNode as? GradientBackgroundNode {
|
||||
gradientBackgroundNode.updateLayout(size: bounds.size, transition: transition)
|
||||
}
|
||||
}
|
||||
transition.updateFrame(view: self.effectView, frame: bounds)
|
||||
|
||||
switch self.passcodeType {
|
||||
|
@ -3,6 +3,7 @@ import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
|
||||
private let regularTitleFont = Font.regular(36.0)
|
||||
private let regularSubtitleFont: UIFont = {
|
||||
@ -35,8 +36,9 @@ private func generateButtonImage(background: PasscodeBackground, frame: CGRect,
|
||||
context.clip()
|
||||
|
||||
context.setAlpha(0.8)
|
||||
context.draw(background.foregroundImage.cgImage!, in: relativeFrame)
|
||||
|
||||
if let foregroundImage = background.foregroundImage {
|
||||
context.draw(foregroundImage.cgImage!, in: relativeFrame)
|
||||
}
|
||||
if highlighted {
|
||||
context.setFillColor(UIColor(white: 1.0, alpha: 0.65).cgColor)
|
||||
context.fillEllipse(in: bounds)
|
||||
@ -98,6 +100,7 @@ private func generateButtonImage(background: PasscodeBackground, frame: CGRect,
|
||||
}
|
||||
|
||||
final class PasscodeEntryButtonNode: HighlightTrackingButtonNode {
|
||||
private var presentationData: PresentationData
|
||||
private var background: PasscodeBackground
|
||||
let title: String
|
||||
private let subtitle: String
|
||||
@ -106,15 +109,24 @@ final class PasscodeEntryButtonNode: HighlightTrackingButtonNode {
|
||||
private var regularImage: UIImage?
|
||||
private var highlightedImage: UIImage?
|
||||
|
||||
private var blurredBackgroundNode: NavigationBackgroundNode?
|
||||
private let backgroundNode: ASImageNode
|
||||
|
||||
var action: (() -> Void)?
|
||||
|
||||
init(background: PasscodeBackground, title: String, subtitle: String) {
|
||||
init(presentationData: PresentationData, background: PasscodeBackground, title: String, subtitle: String) {
|
||||
self.presentationData = presentationData
|
||||
self.background = background
|
||||
self.title = title
|
||||
self.subtitle = subtitle
|
||||
|
||||
if background is CustomPasscodeBackground {
|
||||
let blurredBackgroundColor = (selectDateFillStaticColor(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper), dateFillNeedsBlur(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper))
|
||||
|
||||
let blurredBackgroundNode = NavigationBackgroundNode(color: blurredBackgroundColor.0, enableBlur: blurredBackgroundColor.1)
|
||||
self.blurredBackgroundNode = blurredBackgroundNode
|
||||
}
|
||||
|
||||
self.backgroundNode = ASImageNode()
|
||||
self.backgroundNode.displaysAsynchronously = false
|
||||
self.backgroundNode.displayWithoutProcessing = true
|
||||
@ -122,6 +134,9 @@ final class PasscodeEntryButtonNode: HighlightTrackingButtonNode {
|
||||
|
||||
super.init()
|
||||
|
||||
if let blurredBackgroundNode = self.blurredBackgroundNode {
|
||||
self.addSubnode(blurredBackgroundNode)
|
||||
}
|
||||
self.addSubnode(self.backgroundNode)
|
||||
|
||||
self.highligthedChanged = { [weak self] highlighted in
|
||||
@ -146,7 +161,8 @@ final class PasscodeEntryButtonNode: HighlightTrackingButtonNode {
|
||||
}
|
||||
}
|
||||
|
||||
func updateBackground(_ background: PasscodeBackground) {
|
||||
func updateBackground(_ presentationData: PresentationData, _ background: PasscodeBackground) {
|
||||
self.presentationData = presentationData
|
||||
self.background = background
|
||||
self.updateGraphics()
|
||||
}
|
||||
@ -175,6 +191,10 @@ final class PasscodeEntryButtonNode: HighlightTrackingButtonNode {
|
||||
override func layout() {
|
||||
super.layout()
|
||||
|
||||
if let blurredBackgroundNode = self.blurredBackgroundNode {
|
||||
blurredBackgroundNode.frame = self.bounds
|
||||
blurredBackgroundNode.update(size: blurredBackgroundNode.bounds.size, cornerRadius: blurredBackgroundNode.bounds.height / 2.0, transition: .immediate)
|
||||
}
|
||||
self.backgroundNode.frame = self.bounds
|
||||
}
|
||||
|
||||
@ -199,22 +219,23 @@ private let buttonsData = [
|
||||
]
|
||||
|
||||
final class PasscodeEntryKeyboardNode: ASDisplayNode {
|
||||
private var presentationData: PresentationData?
|
||||
private var background: PasscodeBackground?
|
||||
|
||||
var charactedEntered: ((String) -> Void)?
|
||||
|
||||
private func updateButtons() {
|
||||
guard let background = self.background else {
|
||||
guard let presentationData = self.presentationData, let background = self.background else {
|
||||
return
|
||||
}
|
||||
|
||||
if let subnodes = self.subnodes, !subnodes.isEmpty {
|
||||
for case let button as PasscodeEntryButtonNode in subnodes {
|
||||
button.updateBackground(background)
|
||||
button.updateBackground(presentationData, background)
|
||||
}
|
||||
} else {
|
||||
for (title, subtitle) in buttonsData {
|
||||
let buttonNode = PasscodeEntryButtonNode(background: background, title: title, subtitle: subtitle)
|
||||
let buttonNode = PasscodeEntryButtonNode(presentationData: presentationData, background: background, title: title, subtitle: subtitle)
|
||||
buttonNode.action = { [weak self] in
|
||||
self?.charactedEntered?(title)
|
||||
}
|
||||
@ -223,7 +244,8 @@ final class PasscodeEntryKeyboardNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
func updateBackground(_ background: PasscodeBackground) {
|
||||
func updateBackground(_ presentationData: PresentationData, _ background: PasscodeBackground) {
|
||||
self.presentationData = presentationData
|
||||
self.background = background
|
||||
self.updateButtons()
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ final class PasscodeEntryLabelNode: ASDisplayNode {
|
||||
self.textNode = ASTextNode()
|
||||
self.textNode.isLayerBacked = false
|
||||
self.textNode.textAlignment = .center
|
||||
self.textNode.displaysAsynchronously = false
|
||||
|
||||
super.init()
|
||||
|
||||
|
@ -24,20 +24,23 @@ private func generateDotImage(color: UIColor, filled: Bool) -> UIImage? {
|
||||
})
|
||||
}
|
||||
|
||||
private func generateFieldBackgroundImage(backgroundImage: UIImage, backgroundSize: CGSize, frame: CGRect) -> UIImage? {
|
||||
private func generateFieldBackgroundImage(backgroundImage: UIImage?, backgroundSize: CGSize?, frame: CGRect) -> UIImage? {
|
||||
return generateImage(frame.size, contextGenerator: { size, context in
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
context.clear(bounds)
|
||||
|
||||
let relativeFrame = CGRect(x: -frame.minX, y: frame.minY - backgroundSize.height + frame.size.height
|
||||
, width: backgroundSize.width, height: backgroundSize.height)
|
||||
|
||||
let path = UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height), cornerRadius: 6.0)
|
||||
context.addPath(path.cgPath)
|
||||
context.clip()
|
||||
|
||||
context.draw(backgroundImage.cgImage!, in: relativeFrame)
|
||||
|
||||
if let backgroundImage = backgroundImage, let backgroundSize = backgroundSize {
|
||||
let relativeFrame = CGRect(x: -frame.minX, y: frame.minY - backgroundSize.height + frame.size.height
|
||||
, width: backgroundSize.width, height: backgroundSize.height)
|
||||
context.draw(backgroundImage.cgImage!, in: relativeFrame)
|
||||
} else {
|
||||
context.setFillColor(UIColor(rgb: 0xffffff, alpha: 1.0).cgColor)
|
||||
context.fill(bounds)
|
||||
}
|
||||
context.setBlendMode(.clear)
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
|
||||
@ -129,7 +132,7 @@ private class PasscodeEntryDotNode: ASImageNode {
|
||||
}
|
||||
|
||||
public final class PasscodeInputFieldNode: ASDisplayNode, UITextFieldDelegate {
|
||||
private var background: (UIImage, CGSize)?
|
||||
private var background: PasscodeBackground?
|
||||
private var color: UIColor
|
||||
private var accentColor: UIColor
|
||||
private var fieldType: PasscodeEntryFieldType
|
||||
@ -207,8 +210,8 @@ public final class PasscodeInputFieldNode: ASDisplayNode, UITextFieldDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func updateBackground(_ image: UIImage, size: CGSize) {
|
||||
self.background = (image, size)
|
||||
func updateBackground(_ background: PasscodeBackground) {
|
||||
self.background = background
|
||||
if let (size, topOffset) = self.validLayout {
|
||||
let _ = self.updateLayout(size: size, topOffset: topOffset, transition: .immediate)
|
||||
}
|
||||
@ -276,14 +279,15 @@ public final class PasscodeInputFieldNode: ASDisplayNode, UITextFieldDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func delete() {
|
||||
func delete() -> Bool {
|
||||
var text = self.textFieldNode.textField.text ?? ""
|
||||
guard !text.isEmpty else {
|
||||
return
|
||||
return false
|
||||
}
|
||||
text = String(text[text.startIndex ..< text.index(text.endIndex, offsetBy: -1)])
|
||||
self.textFieldNode.textField.text = text
|
||||
self.updateDots(count: text.count, animated: true)
|
||||
return true
|
||||
}
|
||||
|
||||
func updateDots(count: Int, animated: Bool) {
|
||||
@ -346,9 +350,8 @@ public final class PasscodeInputFieldNode: ASDisplayNode, UITextFieldDelegate {
|
||||
let fieldFrame = CGRect(x: inset, y: origin.y, width: size.width - inset * 2.0, height: fieldHeight)
|
||||
transition.updateFrame(node: self.borderNode, frame: fieldFrame)
|
||||
transition.updateFrame(node: self.textFieldNode, frame: fieldFrame.insetBy(dx: 13.0, dy: 0.0))
|
||||
if let (backgroundImage, backgroundSize) = self.background {
|
||||
self.borderNode.image = generateFieldBackgroundImage(backgroundImage: backgroundImage, backgroundSize: backgroundSize, frame: fieldFrame)
|
||||
}
|
||||
|
||||
self.borderNode.image = generateFieldBackgroundImage(backgroundImage: self.background?.foregroundImage, backgroundSize: self.background?.size, frame: fieldFrame)
|
||||
|
||||
return fieldFrame
|
||||
}
|
||||
|
@ -469,7 +469,7 @@ public func passcodeEntryController(context: AccountContext, animateIn: Bool = t
|
||||
biometrics = .none
|
||||
}
|
||||
#endif
|
||||
let controller = PasscodeEntryController(applicationBindings: context.sharedContext.applicationBindings, accountManager: context.sharedContext.accountManager, appLockContext: context.sharedContext.appLockContext, presentationData: context.sharedContext.currentPresentationData.with { $0 }, presentationDataSignal: context.sharedContext.presentationData, challengeData: challenge, biometrics: biometrics, arguments: PasscodeEntryControllerPresentationArguments(animated: false, fadeIn: true, cancel: {
|
||||
let controller = PasscodeEntryController(applicationBindings: context.sharedContext.applicationBindings, accountManager: context.sharedContext.accountManager, appLockContext: context.sharedContext.appLockContext, presentationData: context.sharedContext.currentPresentationData.with { $0 }, presentationDataSignal: context.sharedContext.presentationData, statusBarHost: context.sharedContext.mainWindow?.statusBarHost, challengeData: challenge, biometrics: biometrics, arguments: PasscodeEntryControllerPresentationArguments(animated: false, fadeIn: true, cancel: {
|
||||
completion(false)
|
||||
}, modalPresentation: modalPresentation))
|
||||
controller.presentationCompleted = { [weak controller] in
|
||||
|
Loading…
x
Reference in New Issue
Block a user