mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
610 lines
29 KiB
Swift
610 lines
29 KiB
Swift
import Foundation
|
|
import UIKit
|
|
import Display
|
|
import AsyncDisplayKit
|
|
import Postbox
|
|
import TelegramCore
|
|
import SwiftSignalKit
|
|
import AccountContext
|
|
import TelegramPresentationData
|
|
import SolidRoundedButtonNode
|
|
import PresentationDataUtils
|
|
|
|
private let accentColor: UIColor = UIColor(rgb: 0x007aff)
|
|
|
|
final class VoiceChatRecordingSetupController: ViewController {
|
|
private var controllerNode: VoiceChatRecordingSetupControllerNode {
|
|
return self.displayNode as! VoiceChatRecordingSetupControllerNode
|
|
}
|
|
|
|
private let context: AccountContext
|
|
private let completion: (Bool?) -> Void
|
|
|
|
private var animatedIn = false
|
|
|
|
private var presentationDataDisposable: Disposable?
|
|
|
|
init(context: AccountContext, completion: @escaping (Bool?) -> Void) {
|
|
self.context = context
|
|
self.completion = completion
|
|
|
|
super.init(navigationBarPresentationData: nil)
|
|
|
|
self.statusBar.statusBarStyle = .Ignore
|
|
|
|
self.blocksBackgroundWhenInOverlay = true
|
|
|
|
self.presentationDataDisposable = (context.sharedContext.presentationData
|
|
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
|
if let strongSelf = self {
|
|
strongSelf.controllerNode.updatePresentationData(presentationData)
|
|
}
|
|
})
|
|
|
|
self.statusBar.statusBarStyle = .Ignore
|
|
}
|
|
|
|
required init(coder aDecoder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
deinit {
|
|
self.presentationDataDisposable?.dispose()
|
|
}
|
|
|
|
override public func loadDisplayNode() {
|
|
self.displayNode = VoiceChatRecordingSetupControllerNode(controller: self, context: self.context)
|
|
self.controllerNode.completion = { [weak self] videoOrientation in
|
|
self?.completion(videoOrientation)
|
|
}
|
|
self.controllerNode.dismiss = { [weak self] in
|
|
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
|
}
|
|
self.controllerNode.cancel = { [weak self] in
|
|
self?.dismiss()
|
|
}
|
|
}
|
|
|
|
override public func loadView() {
|
|
super.loadView()
|
|
}
|
|
|
|
override public func viewDidAppear(_ animated: Bool) {
|
|
super.viewDidAppear(animated)
|
|
|
|
if !self.animatedIn {
|
|
self.animatedIn = true
|
|
self.controllerNode.animateIn()
|
|
}
|
|
}
|
|
|
|
override public func dismiss(completion: (() -> Void)? = nil) {
|
|
self.controllerNode.animateOut(completion: completion)
|
|
}
|
|
|
|
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
|
super.containerLayoutUpdated(layout, transition: transition)
|
|
|
|
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
|
}
|
|
}
|
|
|
|
private class VoiceChatRecordingSetupControllerNode: ViewControllerTracingNode, UIScrollViewDelegate {
|
|
enum MediaMode {
|
|
case videoAndAudio
|
|
case audioOnly
|
|
}
|
|
|
|
enum VideoMode {
|
|
case portrait
|
|
case landscape
|
|
}
|
|
|
|
private weak var controller: VoiceChatRecordingSetupController?
|
|
private let context: AccountContext
|
|
private var presentationData: PresentationData
|
|
|
|
private let dimNode: ASDisplayNode
|
|
private let wrappingScrollNode: ASScrollNode
|
|
private let contentContainerNode: ASDisplayNode
|
|
private let effectNode: ASDisplayNode
|
|
private let backgroundNode: ASDisplayNode
|
|
private let contentBackgroundNode: ASDisplayNode
|
|
private let titleNode: ASTextNode
|
|
private let doneButton: VoiceChatActionButton
|
|
private let cancelButton: SolidRoundedButtonNode
|
|
private let modeContainerNode: ASDisplayNode
|
|
|
|
private let modeSeparatorNode: ASDisplayNode
|
|
private let videoAudioButton: HighlightTrackingButtonNode
|
|
private let videoAudioTitleNode: ImmediateTextNode
|
|
private let videoAudioCheckNode: ASImageNode
|
|
|
|
private let audioButton: HighlightTrackingButtonNode
|
|
private let audioTitleNode: ImmediateTextNode
|
|
private let audioCheckNode: ASImageNode
|
|
|
|
private let portraitButton: HighlightTrackingButtonNode
|
|
private let portraitIconNode: PreviewIconNode
|
|
private let portraitTitleNode: ImmediateTextNode
|
|
|
|
private let landscapeButton: HighlightTrackingButtonNode
|
|
private let landscapeIconNode: PreviewIconNode
|
|
private let landscapeTitleNode: ImmediateTextNode
|
|
|
|
private let selectionNode: ASImageNode
|
|
|
|
private var containerLayout: (ContainerViewLayout, CGFloat)?
|
|
|
|
private let hapticFeedback = HapticFeedback()
|
|
|
|
private let readyDisposable = MetaDisposable()
|
|
|
|
private var mediaMode: MediaMode = .videoAndAudio
|
|
private var videoMode: VideoMode = .portrait
|
|
|
|
var completion: ((Bool?) -> Void)?
|
|
var dismiss: (() -> Void)?
|
|
var cancel: (() -> Void)?
|
|
|
|
init(controller: VoiceChatRecordingSetupController, context: AccountContext) {
|
|
self.controller = controller
|
|
self.context = context
|
|
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
|
|
|
self.wrappingScrollNode = ASScrollNode()
|
|
self.wrappingScrollNode.view.alwaysBounceVertical = true
|
|
self.wrappingScrollNode.view.delaysContentTouches = false
|
|
self.wrappingScrollNode.view.canCancelContentTouches = true
|
|
|
|
self.dimNode = ASDisplayNode()
|
|
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
|
|
|
|
self.contentContainerNode = ASDisplayNode()
|
|
self.contentContainerNode.isOpaque = false
|
|
|
|
self.backgroundNode = ASDisplayNode()
|
|
self.backgroundNode.clipsToBounds = true
|
|
self.backgroundNode.cornerRadius = 16.0
|
|
|
|
let backgroundColor = UIColor(rgb: 0x1c1c1e)
|
|
let textColor: UIColor = .white
|
|
let buttonColor: UIColor = UIColor(rgb: 0x2b2b2f)
|
|
let buttonTextColor: UIColor = .white
|
|
let blurStyle: UIBlurEffect.Style = .dark
|
|
|
|
self.effectNode = ASDisplayNode(viewBlock: {
|
|
return UIVisualEffectView(effect: UIBlurEffect(style: blurStyle))
|
|
})
|
|
|
|
self.contentBackgroundNode = ASDisplayNode()
|
|
self.contentBackgroundNode.backgroundColor = backgroundColor
|
|
|
|
let title = "Record Voice Chat"
|
|
|
|
self.titleNode = ASTextNode()
|
|
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.bold(17.0), textColor: textColor)
|
|
|
|
self.doneButton = VoiceChatActionButton()
|
|
|
|
self.cancelButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(backgroundColor: buttonColor, foregroundColor: buttonTextColor), font: .regular, height: 52.0, cornerRadius: 11.0, gloss: false)
|
|
self.cancelButton.title = self.presentationData.strings.Common_Cancel
|
|
|
|
self.modeContainerNode = ASDisplayNode()
|
|
self.modeContainerNode.clipsToBounds = true
|
|
self.modeContainerNode.cornerRadius = 11.0
|
|
self.modeContainerNode.backgroundColor = UIColor(rgb: 0x303032)
|
|
|
|
self.modeSeparatorNode = ASDisplayNode()
|
|
self.modeSeparatorNode.backgroundColor = UIColor(rgb: 0x404041)
|
|
|
|
self.videoAudioButton = HighlightTrackingButtonNode()
|
|
self.videoAudioTitleNode = ImmediateTextNode()
|
|
self.videoAudioTitleNode.attributedText = NSAttributedString(string: "Video and Audio", font: Font.regular(17.0), textColor: .white, paragraphAlignment: .left)
|
|
self.videoAudioCheckNode = ASImageNode()
|
|
self.videoAudioCheckNode.displaysAsynchronously = false
|
|
self.videoAudioCheckNode.image = UIImage(bundleImageName: "Call/Check")
|
|
|
|
self.audioButton = HighlightTrackingButtonNode()
|
|
self.audioTitleNode = ImmediateTextNode()
|
|
self.audioTitleNode.attributedText = NSAttributedString(string: "Only Audio", font: Font.regular(17.0), textColor: .white, paragraphAlignment: .left)
|
|
self.audioCheckNode = ASImageNode()
|
|
self.audioCheckNode.displaysAsynchronously = false
|
|
self.audioCheckNode.image = UIImage(bundleImageName: "Call/Check")
|
|
|
|
self.portraitButton = HighlightTrackingButtonNode()
|
|
self.portraitButton.backgroundColor = UIColor(rgb: 0x303032)
|
|
self.portraitButton.cornerRadius = 11.0
|
|
self.portraitIconNode = PreviewIconNode()
|
|
self.portraitTitleNode = ImmediateTextNode()
|
|
self.portraitTitleNode.attributedText = NSAttributedString(string: "Portrait", font: Font.semibold(15.0), textColor: UIColor(rgb: 0x8e8e93), paragraphAlignment: .left)
|
|
|
|
self.landscapeButton = HighlightTrackingButtonNode()
|
|
self.landscapeButton.backgroundColor = UIColor(rgb: 0x303032)
|
|
self.landscapeButton.cornerRadius = 11.0
|
|
self.landscapeIconNode = PreviewIconNode()
|
|
self.landscapeTitleNode = ImmediateTextNode()
|
|
self.landscapeTitleNode.attributedText = NSAttributedString(string: "Landscape", font: Font.semibold(15.0), textColor: UIColor(rgb: 0x8e8e93), paragraphAlignment: .left)
|
|
|
|
self.selectionNode = ASImageNode()
|
|
self.selectionNode.displaysAsynchronously = false
|
|
self.selectionNode.image = generateImage(CGSize(width: 174.0, height: 140.0), rotatedContext: { size, context in
|
|
let bounds = CGRect(origin: CGPoint(), size: size)
|
|
context.clear(bounds)
|
|
|
|
let lineWidth: CGFloat = 2.0
|
|
|
|
let path = UIBezierPath(roundedRect: bounds.insetBy(dx: lineWidth / 2.0, dy: lineWidth / 2.0), cornerRadius: 11.0)
|
|
let cgPath = path.cgPath.copy(strokingWithWidth: lineWidth, lineCap: .round, lineJoin: .round, miterLimit: 10.0)
|
|
context.addPath(cgPath)
|
|
context.clip()
|
|
|
|
let colors: [CGColor] = [UIColor(rgb: 0x5064fd).cgColor, UIColor(rgb: 0xe76598).cgColor]
|
|
var locations: [CGFloat] = [0.0, 1.0]
|
|
let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colors as CFArray, locations: &locations)!
|
|
|
|
context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: size.width, y: 0.0), options: CGGradientDrawingOptions())
|
|
})
|
|
self.selectionNode.isUserInteractionEnabled = false
|
|
|
|
super.init()
|
|
|
|
self.backgroundColor = nil
|
|
self.isOpaque = false
|
|
|
|
self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
|
|
self.addSubnode(self.dimNode)
|
|
|
|
self.wrappingScrollNode.view.delegate = self
|
|
self.addSubnode(self.wrappingScrollNode)
|
|
|
|
self.wrappingScrollNode.addSubnode(self.backgroundNode)
|
|
self.wrappingScrollNode.addSubnode(self.contentContainerNode)
|
|
|
|
self.backgroundNode.addSubnode(self.effectNode)
|
|
self.backgroundNode.addSubnode(self.contentBackgroundNode)
|
|
self.contentContainerNode.addSubnode(self.titleNode)
|
|
self.contentContainerNode.addSubnode(self.doneButton)
|
|
self.contentContainerNode.addSubnode(self.cancelButton)
|
|
self.contentContainerNode.addSubnode(self.modeContainerNode)
|
|
|
|
self.contentContainerNode.addSubnode(self.videoAudioTitleNode)
|
|
self.contentContainerNode.addSubnode(self.videoAudioCheckNode)
|
|
self.contentContainerNode.addSubnode(self.videoAudioButton)
|
|
|
|
self.contentContainerNode.addSubnode(self.modeSeparatorNode)
|
|
|
|
self.contentContainerNode.addSubnode(self.audioTitleNode)
|
|
self.contentContainerNode.addSubnode(self.audioCheckNode)
|
|
self.contentContainerNode.addSubnode(self.audioButton)
|
|
|
|
self.contentContainerNode.addSubnode(self.portraitButton)
|
|
self.contentContainerNode.addSubnode(self.portraitIconNode)
|
|
self.contentContainerNode.addSubnode(self.portraitTitleNode)
|
|
|
|
self.contentContainerNode.addSubnode(self.landscapeButton)
|
|
self.contentContainerNode.addSubnode(self.landscapeIconNode)
|
|
self.contentContainerNode.addSubnode(self.landscapeTitleNode)
|
|
|
|
self.contentContainerNode.addSubnode(self.selectionNode)
|
|
|
|
self.videoAudioButton.addTarget(self, action: #selector(self.videoAudioPressed), forControlEvents: .touchUpInside)
|
|
self.audioButton.addTarget(self, action: #selector(self.audioPressed), forControlEvents: .touchUpInside)
|
|
|
|
self.portraitButton.addTarget(self, action: #selector(self.portraitPressed), forControlEvents: .touchUpInside)
|
|
self.landscapeButton.addTarget(self, action: #selector(self.landscapePressed), forControlEvents: .touchUpInside)
|
|
|
|
self.doneButton.addTarget(self, action: #selector(self.donePressed), forControlEvents: .touchUpInside)
|
|
|
|
self.cancelButton.pressed = { [weak self] in
|
|
if let strongSelf = self {
|
|
strongSelf.cancel?()
|
|
}
|
|
}
|
|
}
|
|
|
|
@objc private func donePressed() {
|
|
let videoOrientation: Bool?
|
|
switch self.mediaMode {
|
|
case .audioOnly:
|
|
videoOrientation = nil
|
|
case .videoAndAudio:
|
|
switch self.videoMode {
|
|
case .portrait:
|
|
videoOrientation = true
|
|
case .landscape:
|
|
videoOrientation = false
|
|
}
|
|
}
|
|
self.completion?(videoOrientation)
|
|
self.dismiss?()
|
|
}
|
|
|
|
@objc private func videoAudioPressed() {
|
|
self.mediaMode = .videoAndAudio
|
|
|
|
if let (layout, navigationHeight) = self.containerLayout {
|
|
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .animated(duration: 0.2, curve: .easeInOut))
|
|
}
|
|
}
|
|
|
|
@objc private func audioPressed() {
|
|
self.mediaMode = .audioOnly
|
|
|
|
if let (layout, navigationHeight) = self.containerLayout {
|
|
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .animated(duration: 0.2, curve: .easeInOut))
|
|
}
|
|
}
|
|
|
|
@objc private func portraitPressed() {
|
|
self.mediaMode = .videoAndAudio
|
|
self.videoMode = .portrait
|
|
|
|
if let (layout, navigationHeight) = self.containerLayout {
|
|
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .animated(duration: 0.2, curve: .easeInOut))
|
|
}
|
|
}
|
|
|
|
@objc private func landscapePressed() {
|
|
self.mediaMode = .videoAndAudio
|
|
self.videoMode = .landscape
|
|
|
|
if let (layout, navigationHeight) = self.containerLayout {
|
|
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .animated(duration: 0.2, curve: .easeInOut))
|
|
}
|
|
}
|
|
|
|
func updatePresentationData(_ presentationData: PresentationData) {
|
|
self.presentationData = presentationData
|
|
}
|
|
|
|
override func didLoad() {
|
|
super.didLoad()
|
|
|
|
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
|
|
self.wrappingScrollNode.view.contentInsetAdjustmentBehavior = .never
|
|
}
|
|
}
|
|
|
|
@objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
|
|
if case .ended = recognizer.state {
|
|
self.cancel?()
|
|
}
|
|
}
|
|
|
|
func animateIn() {
|
|
self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
|
|
|
|
let offset = self.bounds.size.height - self.contentBackgroundNode.frame.minY
|
|
let dimPosition = self.dimNode.layer.position
|
|
|
|
let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring)
|
|
let targetBounds = self.bounds
|
|
self.bounds = self.bounds.offsetBy(dx: 0.0, dy: -offset)
|
|
self.dimNode.position = CGPoint(x: dimPosition.x, y: dimPosition.y - offset)
|
|
transition.animateView({
|
|
self.bounds = targetBounds
|
|
self.dimNode.position = dimPosition
|
|
})
|
|
}
|
|
|
|
func animateOut(completion: (() -> Void)? = nil) {
|
|
var dimCompleted = false
|
|
var offsetCompleted = false
|
|
|
|
let internalCompletion: () -> Void = { [weak self] in
|
|
if let strongSelf = self, dimCompleted && offsetCompleted {
|
|
strongSelf.dismiss?()
|
|
}
|
|
completion?()
|
|
}
|
|
|
|
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in
|
|
dimCompleted = true
|
|
internalCompletion()
|
|
})
|
|
|
|
let offset = self.bounds.size.height - self.contentBackgroundNode.frame.minY
|
|
let dimPosition = self.dimNode.layer.position
|
|
self.dimNode.layer.animatePosition(from: dimPosition, to: CGPoint(x: dimPosition.x, y: dimPosition.y - offset), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
|
self.layer.animateBoundsOriginYAdditive(from: 0.0, to: -offset, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
|
|
offsetCompleted = true
|
|
internalCompletion()
|
|
})
|
|
}
|
|
|
|
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
|
if self.bounds.contains(point) {
|
|
if !self.contentBackgroundNode.bounds.contains(self.convert(point, to: self.contentBackgroundNode)) {
|
|
return self.dimNode.view
|
|
}
|
|
}
|
|
return super.hitTest(point, with: event)
|
|
}
|
|
|
|
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
|
let contentOffset = scrollView.contentOffset
|
|
let additionalTopHeight = max(0.0, -contentOffset.y)
|
|
|
|
if additionalTopHeight >= 30.0 {
|
|
self.cancel?()
|
|
}
|
|
}
|
|
|
|
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
|
self.containerLayout = (layout, navigationBarHeight)
|
|
|
|
let isLandscape: Bool
|
|
if layout.size.width > layout.size.height, case .compact = layout.metrics.widthClass {
|
|
isLandscape = true
|
|
} else {
|
|
isLandscape = false
|
|
}
|
|
|
|
|
|
var insets = layout.insets(options: [.statusBar, .input])
|
|
let cleanInsets = layout.insets(options: [.statusBar])
|
|
insets.top = max(10.0, insets.top)
|
|
|
|
let buttonOffset: CGFloat = 60.0
|
|
|
|
let bottomInset: CGFloat = 10.0 + cleanInsets.bottom
|
|
let titleHeight: CGFloat = 54.0
|
|
var contentHeight = titleHeight + bottomInset + 52.0 + 17.0
|
|
let innerContentHeight: CGFloat = 287.0
|
|
var width = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: layout.safeInsets.left)
|
|
if isLandscape {
|
|
contentHeight = layout.size.height
|
|
width = layout.size.width
|
|
} else {
|
|
contentHeight = titleHeight + bottomInset + 52.0 + 17.0 + innerContentHeight + buttonOffset
|
|
}
|
|
|
|
let inset: CGFloat = 16.0
|
|
let sideInset = floor((layout.size.width - width) / 2.0)
|
|
let contentContainerFrame = CGRect(origin: CGPoint(x: sideInset, y: layout.size.height - contentHeight), size: CGSize(width: width, height: contentHeight))
|
|
let contentFrame = contentContainerFrame
|
|
|
|
var backgroundFrame = CGRect(origin: CGPoint(x: contentFrame.minX, y: contentFrame.minY), size: CGSize(width: contentFrame.width, height: contentFrame.height + 2000.0))
|
|
if backgroundFrame.minY < contentFrame.minY {
|
|
backgroundFrame.origin.y = contentFrame.minY
|
|
}
|
|
transition.updateAlpha(node: self.titleNode, alpha: isLandscape ? 0.0 : 1.0)
|
|
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
|
|
transition.updateFrame(node: self.effectNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
|
transition.updateFrame(node: self.contentBackgroundNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
|
transition.updateFrame(node: self.wrappingScrollNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
|
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
|
|
|
let titleSize = self.titleNode.measure(CGSize(width: width, height: titleHeight))
|
|
let titleFrame = CGRect(origin: CGPoint(x: floor((contentFrame.width - titleSize.width) / 2.0), y: 18.0), size: titleSize)
|
|
transition.updateFrame(node: self.titleNode, frame: titleFrame)
|
|
|
|
let itemHeight: CGFloat = 44.0
|
|
|
|
transition.updateFrame(node: self.modeContainerNode, frame: CGRect(x: inset, y: 56.0, width: contentFrame.width - inset * 2.0, height: itemHeight * 2.0))
|
|
|
|
transition.updateFrame(node: self.videoAudioButton, frame: CGRect(x: inset, y: 56.0, width: contentFrame.width - inset * 2.0, height: itemHeight))
|
|
transition.updateFrame(node: self.videoAudioCheckNode, frame: CGRect(x: contentFrame.width - inset - 16.0 - 20.0, y: 56.0 + floorToScreenPixels((itemHeight - 16.0) / 2.0), width: 16.0, height: 16.0))
|
|
self.videoAudioCheckNode.isHidden = self.mediaMode != .videoAndAudio
|
|
|
|
let videoAudioSize = self.videoAudioTitleNode.updateLayout(CGSize(width: contentFrame.width - inset * 2.0, height: itemHeight))
|
|
transition.updateFrame(node: self.videoAudioTitleNode, frame: CGRect(x: inset + 16.0, y: 56.0 + floorToScreenPixels((itemHeight - videoAudioSize.height) / 2.0), width: videoAudioSize.width, height: videoAudioSize.height))
|
|
|
|
transition.updateFrame(node: self.audioButton, frame: CGRect(x: inset, y: 56.0 + itemHeight, width: contentFrame.width - inset * 2.0, height: itemHeight))
|
|
transition.updateFrame(node: self.audioCheckNode, frame: CGRect(x: contentFrame.width - inset - 16.0 - 20.0, y: 56.0 + itemHeight + floorToScreenPixels((itemHeight - 16.0) / 2.0), width: 16.0, height: 16.0))
|
|
self.audioCheckNode.isHidden = self.mediaMode != .audioOnly
|
|
|
|
let audioSize = self.audioTitleNode.updateLayout(CGSize(width: contentFrame.width - inset * 2.0, height: itemHeight))
|
|
transition.updateFrame(node: self.audioTitleNode, frame: CGRect(x: inset + 16.0, y: 56.0 + itemHeight + floorToScreenPixels((itemHeight - audioSize.height) / 2.0), width: audioSize.width, height: audioSize.height))
|
|
|
|
transition.updateFrame(node: self.modeSeparatorNode, frame: CGRect(x: inset + 16.0, y: 56.0 + itemHeight, width: contentFrame.width - inset * 2.0 - 16.0, height: UIScreenPixel))
|
|
|
|
var buttonsAlpha: CGFloat = 1.0
|
|
if case .audioOnly = self.mediaMode {
|
|
buttonsAlpha = 0.3
|
|
}
|
|
|
|
transition.updateAlpha(node: self.portraitButton, alpha: buttonsAlpha)
|
|
transition.updateAlpha(node: self.portraitIconNode, alpha: buttonsAlpha)
|
|
transition.updateAlpha(node: self.portraitTitleNode, alpha: buttonsAlpha)
|
|
|
|
transition.updateAlpha(node: self.landscapeButton, alpha: buttonsAlpha)
|
|
transition.updateAlpha(node: self.landscapeIconNode, alpha: buttonsAlpha)
|
|
transition.updateAlpha(node: self.landscapeTitleNode, alpha: buttonsAlpha)
|
|
|
|
transition.updateAlpha(node: self.selectionNode, alpha: buttonsAlpha)
|
|
|
|
self.portraitTitleNode.attributedText = NSAttributedString(string: "Portrait", font: Font.semibold(15.0), textColor: self.videoMode == .portrait ? UIColor(rgb: 0xb56df4) : UIColor(rgb: 0x8e8e93), paragraphAlignment: .left)
|
|
self.landscapeTitleNode.attributedText = NSAttributedString(string: "Landscape", font: Font.semibold(15.0), textColor: self.videoMode == .landscape ? UIColor(rgb: 0xb56df4) : UIColor(rgb: 0x8e8e93), paragraphAlignment: .left)
|
|
|
|
let buttonWidth = floorToScreenPixels((contentFrame.width - inset * 2.0 - 11.0) / 2.0)
|
|
let portraitButtonFrame = CGRect(x: inset, y: 56.0 + itemHeight * 2.0 + 25.0, width: buttonWidth, height: 140.0)
|
|
transition.updateFrame(node: self.portraitButton, frame: portraitButtonFrame)
|
|
transition.updateFrame(node: self.portraitIconNode, frame: CGRect(x: portraitButtonFrame.minX + floorToScreenPixels((portraitButtonFrame.width - 72.0) / 2.0), y: portraitButtonFrame.minY + floorToScreenPixels((portraitButtonFrame.height - 122.0) / 2.0), width: 76.0, height: 122.0))
|
|
self.portraitIconNode.updateLayout(landscape: false)
|
|
let portraitSize = self.portraitTitleNode.updateLayout(CGSize(width: buttonWidth, height: 30.0))
|
|
transition.updateFrame(node: self.portraitTitleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels(portraitButtonFrame.center.x - portraitSize.width / 2.0), y: portraitButtonFrame.maxY + 7.0), size: portraitSize))
|
|
|
|
let landscapeButtonFrame = CGRect(x: portraitButtonFrame.maxX + 11.0, y: portraitButtonFrame.minY, width: portraitButtonFrame.width, height: portraitButtonFrame.height)
|
|
transition.updateFrame(node: self.landscapeButton, frame: landscapeButtonFrame)
|
|
transition.updateFrame(node: self.landscapeIconNode, frame: CGRect(x: landscapeButtonFrame.minX + floorToScreenPixels((landscapeButtonFrame.width - 122.0) / 2.0), y: landscapeButtonFrame.minY + floorToScreenPixels((landscapeButtonFrame.height - 76.0) / 2.0), width: 122.0, height: 76.0))
|
|
self.landscapeIconNode.updateLayout(landscape: true)
|
|
let landscapeSize = self.landscapeTitleNode.updateLayout(CGSize(width: buttonWidth, height: 30.0))
|
|
transition.updateFrame(node: self.landscapeTitleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels(landscapeButtonFrame.center.x - landscapeSize.width / 2.0), y: landscapeButtonFrame.maxY + 7.0), size: landscapeSize))
|
|
|
|
let centralButtonSide = min(contentFrame.width, layout.size.height) - 32.0
|
|
let centralButtonSize = CGSize(width: centralButtonSide, height: centralButtonSide)
|
|
|
|
let buttonInset: CGFloat = 16.0
|
|
let doneButtonPreFrame = CGRect(x: buttonInset, y: contentHeight - 50.0 - insets.bottom - 16.0 - buttonOffset, width: contentFrame.width - buttonInset * 2.0, height: 50.0)
|
|
let doneButtonFrame = CGRect(origin: CGPoint(x: floor(doneButtonPreFrame.midX - centralButtonSize.width / 2.0), y: floor(doneButtonPreFrame.midY - centralButtonSize.height / 2.0)), size: centralButtonSize)
|
|
transition.updateFrame(node: self.doneButton, frame: doneButtonFrame)
|
|
|
|
if self.videoMode == .portrait {
|
|
self.selectionNode.frame = portraitButtonFrame.insetBy(dx: -1.0, dy: -1.0)
|
|
} else {
|
|
self.selectionNode.frame = landscapeButtonFrame.insetBy(dx: -1.0, dy: -1.0)
|
|
}
|
|
|
|
self.doneButton.update(size: centralButtonSize, buttonSize: CGSize(width: 112.0, height: 112.0), state: .button(text: "Start Recording"), title: "", subtitle: "", dark: false, small: false)
|
|
|
|
let cancelButtonHeight = self.cancelButton.updateLayout(width: contentFrame.width - buttonInset * 2.0, transition: transition)
|
|
transition.updateFrame(node: self.cancelButton, frame: CGRect(x: buttonInset, y: contentHeight - cancelButtonHeight - insets.bottom - 16.0, width: contentFrame.width, height: cancelButtonHeight))
|
|
|
|
transition.updateFrame(node: self.contentContainerNode, frame: contentContainerFrame)
|
|
}
|
|
}
|
|
|
|
private class PreviewIconNode: ASDisplayNode {
|
|
private let avatar1Node: ASImageNode
|
|
private let avatar2Node: ASImageNode
|
|
private let avatar3Node: ASImageNode
|
|
private let avatar4Node: ASImageNode
|
|
|
|
override init() {
|
|
self.avatar1Node = ASImageNode()
|
|
self.avatar1Node.cornerRadius = 4.0
|
|
self.avatar1Node.displaysAsynchronously = false
|
|
self.avatar1Node.backgroundColor = UIColor(rgb: 0x834fff)
|
|
|
|
self.avatar2Node = ASImageNode()
|
|
self.avatar2Node.cornerRadius = 4.0
|
|
self.avatar2Node.displaysAsynchronously = false
|
|
self.avatar2Node.backgroundColor = UIColor(rgb: 0x63d5c9)
|
|
|
|
self.avatar3Node = ASImageNode()
|
|
self.avatar3Node.cornerRadius = 4.0
|
|
self.avatar3Node.displaysAsynchronously = false
|
|
self.avatar3Node.backgroundColor = UIColor(rgb: 0xccff60)
|
|
|
|
self.avatar4Node = ASImageNode()
|
|
self.avatar4Node.cornerRadius = 4.0
|
|
self.avatar4Node.displaysAsynchronously = false
|
|
self.avatar4Node.backgroundColor = UIColor(rgb: 0xf5512a)
|
|
|
|
super.init()
|
|
|
|
self.isUserInteractionEnabled = false
|
|
|
|
self.addSubnode(self.avatar1Node)
|
|
self.addSubnode(self.avatar2Node)
|
|
self.addSubnode(self.avatar3Node)
|
|
self.addSubnode(self.avatar4Node)
|
|
}
|
|
|
|
func updateLayout(landscape: Bool) {
|
|
if landscape {
|
|
self.avatar1Node.frame = CGRect(x: 0.0, y: 0.0, width: 96.0, height: 76.0)
|
|
self.avatar2Node.frame = CGRect(x: 98.0, y: 0.0, width: 24.0, height: 24.0)
|
|
self.avatar3Node.frame = CGRect(x: 98.0, y: 26.0, width: 24.0, height: 24.0)
|
|
self.avatar4Node.frame = CGRect(x: 98.0, y: 52.0, width: 24.0, height: 24.0)
|
|
} else {
|
|
self.avatar1Node.frame = CGRect(x: 0.0, y: 0.0, width: 76.0, height: 96.0)
|
|
self.avatar2Node.frame = CGRect(x: 0.0, y: 98.0, width: 24.0, height: 24.0)
|
|
self.avatar3Node.frame = CGRect(x: 26.0, y: 98.0, width: 24.0, height: 24.0)
|
|
self.avatar4Node.frame = CGRect(x: 52.0, y: 98.0, width: 24.0, height: 24.0)
|
|
}
|
|
}
|
|
}
|