mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 13:35:19 +00:00
[WIP] Stickers editor
This commit is contained in:
parent
c7035b2621
commit
ccb2415473
@ -156,6 +156,12 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.eachView { entityView in
|
||||
entityView.reset()
|
||||
}
|
||||
}
|
||||
|
||||
public override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
@ -501,6 +507,7 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
|
||||
self.hasSelectionChanged(false)
|
||||
view.selectionView?.removeFromSuperview()
|
||||
}
|
||||
view.reset()
|
||||
if animated {
|
||||
view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak view] _ in
|
||||
view?.removeFromSuperview()
|
||||
@ -539,6 +546,7 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
|
||||
if view.entity.isMedia {
|
||||
continue
|
||||
}
|
||||
view.reset()
|
||||
if let selectionView = view.selectionView {
|
||||
selectionView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak selectionView] _ in
|
||||
selectionView?.removeFromSuperview()
|
||||
@ -557,6 +565,7 @@ public final class DrawingEntitiesView: UIView, TGPhotoDrawingEntitiesView {
|
||||
if view.entity.isMedia {
|
||||
continue
|
||||
}
|
||||
view.reset()
|
||||
view.selectionView?.removeFromSuperview()
|
||||
view.removeFromSuperview()
|
||||
}
|
||||
@ -946,6 +955,12 @@ public class DrawingEntityView: UIView {
|
||||
return self.bounds
|
||||
}
|
||||
|
||||
func reset() {
|
||||
self.onSnapUpdated = { _, _ in }
|
||||
self.onPositionUpdated = { _ in }
|
||||
self.onInteractionUpdated = { _ in }
|
||||
}
|
||||
|
||||
func animateInsertion() {
|
||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
|
||||
|
@ -110,7 +110,6 @@ public final class DrawingLocationEntityView: DrawingEntityView, UITextViewDeleg
|
||||
|
||||
private var textSize: CGSize = .zero
|
||||
public override func sizeThatFits(_ size: CGSize) -> CGSize {
|
||||
self.textView.setNeedsLayersUpdate()
|
||||
var result = self.textView.sizeThatFits(CGSize(width: self.locationEntity.width, height: .greatestFiniteMagnitude))
|
||||
self.textSize = result
|
||||
|
||||
|
@ -27,7 +27,7 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate
|
||||
return self.entity as! DrawingTextEntity
|
||||
}
|
||||
|
||||
let blurredBackgroundView: BlurredBackgroundView
|
||||
// let blurredBackgroundView: BlurredBackgroundView
|
||||
let textView: DrawingTextView
|
||||
var customEmojiContainerView: CustomEmojiContainerView?
|
||||
var emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?
|
||||
@ -37,9 +37,9 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate
|
||||
var replaceWithAnimatedImage: (Data, UIImage) -> Void = { _, _ in }
|
||||
|
||||
init(context: AccountContext, entity: DrawingTextEntity) {
|
||||
self.blurredBackgroundView = BlurredBackgroundView(color: UIColor(white: 0.0, alpha: 0.25), enableBlur: true)
|
||||
self.blurredBackgroundView.clipsToBounds = true
|
||||
self.blurredBackgroundView.isHidden = true
|
||||
// self.blurredBackgroundView = BlurredBackgroundView(color: UIColor(white: 0.0, alpha: 0.25), enableBlur: true)
|
||||
// self.blurredBackgroundView.clipsToBounds = true
|
||||
// self.blurredBackgroundView.isHidden = true
|
||||
|
||||
self.textView = DrawingTextView(frame: .zero)
|
||||
self.textView.clipsToBounds = false
|
||||
@ -62,7 +62,7 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate
|
||||
super.init(context: context, entity: entity)
|
||||
|
||||
self.textView.delegate = self
|
||||
self.addSubview(self.blurredBackgroundView)
|
||||
// self.addSubview(self.blurredBackgroundView)
|
||||
self.addSubview(self.textView)
|
||||
|
||||
self.emojiViewProvider = { emoji in
|
||||
@ -420,7 +420,6 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate
|
||||
}
|
||||
|
||||
public override func sizeThatFits(_ size: CGSize) -> CGSize {
|
||||
self.textView.setNeedsLayersUpdate()
|
||||
var result = self.textView.sizeThatFits(CGSize(width: self.textEntity.width, height: .greatestFiniteMagnitude))
|
||||
result.width = max(224.0, ceil(result.width) + 20.0)
|
||||
result.height = ceil(result.height);
|
||||
@ -458,7 +457,6 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate
|
||||
let range = NSMakeRange(0, text.length)
|
||||
let fontSize = self.displayFontSize
|
||||
|
||||
self.textView.hasTextLayers = [.typing, .wiggle].contains(self.textEntity.animation)
|
||||
self.textView.drawingLayoutManager.textContainers.first?.lineFragmentPadding = floor(fontSize * 0.24)
|
||||
|
||||
if let (font, name) = availableFonts[text.string.lowercased()] {
|
||||
@ -498,11 +496,8 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate
|
||||
guard let visualText = text.mutableCopy() as? NSMutableAttributedString else {
|
||||
return
|
||||
}
|
||||
if self.textView.hasTextLayers {
|
||||
text.addAttribute(.foregroundColor, value: UIColor.clear, range: range)
|
||||
} else {
|
||||
text.addAttribute(.foregroundColor, value: textColor, range: range)
|
||||
}
|
||||
text.addAttribute(.foregroundColor, value: textColor, range: range)
|
||||
|
||||
visualText.addAttribute(.foregroundColor, value: textColor, range: range)
|
||||
|
||||
text.enumerateAttributes(in: range) { attributes, subrange, _ in
|
||||
@ -522,67 +517,8 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate
|
||||
if keepSelectedRange {
|
||||
self.textView.selectedRange = previousRange
|
||||
}
|
||||
|
||||
if self.textView.hasTextLayers {
|
||||
self.textView.onLayersUpdate = { [weak self] in
|
||||
self?.updateTextAnimations()
|
||||
}
|
||||
} else {
|
||||
self.updateTextAnimations()
|
||||
}
|
||||
}
|
||||
|
||||
func updateTextAnimations() {
|
||||
for layer in self.textView.characterLayers {
|
||||
layer.removeAllAnimations()
|
||||
}
|
||||
self.textView.layer.removeAllAnimations()
|
||||
|
||||
guard self.textView.characterLayers.count > 0 || self.textEntity.animation == .zoomIn else {
|
||||
return
|
||||
}
|
||||
|
||||
switch self.textEntity.animation {
|
||||
case .typing:
|
||||
let delta: CGFloat = 1.0 / CGFloat(self.textView.characterLayers.count + 3)
|
||||
let duration = Double(self.textView.characterLayers.count + 3) * 0.28
|
||||
var offset = delta
|
||||
for layer in self.textView.characterLayers {
|
||||
let animation = CAKeyframeAnimation(keyPath: "opacity")
|
||||
animation.calculationMode = .discrete
|
||||
animation.values = [0.0, 1.0]
|
||||
animation.keyTimes = [0.0, offset as NSNumber, 1.0]
|
||||
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
||||
animation.duration = duration
|
||||
animation.repeatCount = .infinity
|
||||
layer.add(animation, forKey: "opacity")
|
||||
offset += delta
|
||||
}
|
||||
case .wiggle:
|
||||
for layer in self.textView.characterLayers {
|
||||
let animation = CABasicAnimation(keyPath: "transform.rotation.z")
|
||||
animation.fromValue = (-.pi / 10.0) as NSNumber
|
||||
animation.toValue = (.pi / 10.0) as NSNumber
|
||||
animation.autoreverses = true
|
||||
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
||||
animation.duration = 0.6
|
||||
animation.repeatCount = .infinity
|
||||
layer.add(animation, forKey: "transform.rotation.z")
|
||||
}
|
||||
case .zoomIn:
|
||||
let animation = CABasicAnimation(keyPath: "transform.scale")
|
||||
animation.fromValue = 0.001 as NSNumber
|
||||
animation.toValue = 1.0 as NSNumber
|
||||
animation.autoreverses = true
|
||||
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
||||
animation.duration = 0.8
|
||||
animation.repeatCount = .infinity
|
||||
self.textView.layer.add(animation, forKey: "transform.scale")
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public override func update(animated: Bool = false) {
|
||||
self.update(animated: animated, keepSelectedRange: false, updateEditingPosition: true)
|
||||
}
|
||||
@ -687,30 +623,6 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate
|
||||
return image
|
||||
}
|
||||
|
||||
func getPresentationRenderImage() -> UIImage? {
|
||||
let rect = self.bounds
|
||||
UIGraphicsBeginImageContextWithOptions(rect.size, false, 1.0)
|
||||
if let context = UIGraphicsGetCurrentContext() {
|
||||
for layer in self.textView.characterLayers {
|
||||
if let presentation = layer.presentation() {
|
||||
context.saveGState()
|
||||
context.translateBy(x: presentation.position.x - presentation.bounds.width / 2.0, y: 0.0)
|
||||
if let rotation = (presentation.value(forKeyPath: "transform.rotation.z") as? NSNumber)?.floatValue {
|
||||
context.translateBy(x: presentation.bounds.width / 2.0, y: presentation.bounds.height)
|
||||
context.rotate(by: CGFloat(rotation))
|
||||
context.translateBy(x: -presentation.bounds.width / 2.0, y: -presentation.bounds.height)
|
||||
}
|
||||
presentation.render(in: context)
|
||||
context.restoreGState()
|
||||
}
|
||||
}
|
||||
}
|
||||
//self.textView.drawHierarchy(in: rect, afterScreenUpdates: true)
|
||||
let image = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
return image
|
||||
}
|
||||
|
||||
func getRenderSubEntities() -> [DrawingEntity] {
|
||||
let textSize = self.textView.bounds.size
|
||||
let textPosition = self.textEntity.position
|
||||
@ -1236,8 +1148,6 @@ final class SimpleTextLayer: CATextLayer {
|
||||
}
|
||||
|
||||
final class DrawingTextView: UITextView, NSLayoutManagerDelegate {
|
||||
var characterLayers: [CALayer] = []
|
||||
|
||||
var drawingLayoutManager: DrawingTextLayoutManager {
|
||||
return self.layoutManager as! DrawingTextLayoutManager
|
||||
}
|
||||
@ -1307,7 +1217,6 @@ final class DrawingTextView: UITextView, NSLayoutManagerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
var hasTextLayers = false
|
||||
var visualText: NSAttributedString?
|
||||
|
||||
init(frame: CGRect) {
|
||||
@ -1357,15 +1266,8 @@ final class DrawingTextView: UITextView, NSLayoutManagerDelegate {
|
||||
}
|
||||
|
||||
var onLayoutUpdate: (() -> Void)?
|
||||
var onLayersUpdate: (() -> Void)?
|
||||
|
||||
private var needsLayersUpdate = false
|
||||
func setNeedsLayersUpdate() {
|
||||
self.needsLayersUpdate = true
|
||||
}
|
||||
|
||||
|
||||
func layoutManager(_ layoutManager: NSLayoutManager, didCompleteLayoutFor textContainer: NSTextContainer?, atEnd layoutFinishedFlag: Bool) {
|
||||
self.updateCharLayers()
|
||||
if layoutFinishedFlag {
|
||||
if let onLayoutUpdate = self.onLayoutUpdate {
|
||||
self.onLayoutUpdate = nil
|
||||
@ -1374,42 +1276,6 @@ final class DrawingTextView: UITextView, NSLayoutManagerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func updateCharLayers() {
|
||||
for layer in self.characterLayers {
|
||||
layer.removeFromSuperlayer()
|
||||
}
|
||||
self.characterLayers = []
|
||||
|
||||
guard let attributedString = self.visualText, self.hasTextLayers else {
|
||||
return
|
||||
}
|
||||
|
||||
let wordRange = NSMakeRange(0, attributedString.length)
|
||||
|
||||
var index = wordRange.location
|
||||
while index < wordRange.location + wordRange.length {
|
||||
let glyphRange = NSMakeRange(index, 1)
|
||||
let characterRange = self.layoutManager.characterRange(forGlyphRange: glyphRange, actualGlyphRange:nil)
|
||||
var glyphRect = self.layoutManager.boundingRect(forGlyphRange: glyphRange, in: self.textContainer)
|
||||
//let location = self.layoutManager.location(forGlyphAt: index)
|
||||
|
||||
glyphRect.origin.y += glyphRect.height / 2.0 //location.y - (glyphRect.height / 2.0);
|
||||
let textLayer = SimpleTextLayer()
|
||||
textLayer.contentsScale = 1.0
|
||||
textLayer.frame = glyphRect
|
||||
textLayer.string = attributedString.attributedSubstring(from: characterRange)
|
||||
textLayer.anchorPoint = CGPoint(x: 0.5, y: 1.0)
|
||||
|
||||
self.layer.addSublayer(textLayer)
|
||||
self.characterLayers.append(textLayer)
|
||||
|
||||
let stepGlyphRange = self.layoutManager.glyphRange(forCharacterRange: characterRange, actualCharacterRange:nil)
|
||||
index += stepGlyphRange.length
|
||||
}
|
||||
|
||||
self.onLayersUpdate?()
|
||||
}
|
||||
|
||||
var onPaste: () -> Bool = { return true }
|
||||
override func paste(_ sender: Any?) {
|
||||
if !self.text.isEmpty || self.onPaste() {
|
||||
|
@ -2225,6 +2225,13 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
}
|
||||
}
|
||||
|
||||
if let camera = self.controllerNode.modernCamera {
|
||||
if let cameraView = self.controllerNode.modernCameraView {
|
||||
cameraView.isEnabled = false
|
||||
}
|
||||
camera.stopCapture(invalidate: true)
|
||||
}
|
||||
|
||||
super.dismiss(completion: completion)
|
||||
}
|
||||
|
||||
|
@ -578,7 +578,9 @@ final class EmojiStickerAccessoryNode: SparseNode, PeekControllerAccessoryNode {
|
||||
self.reactionContextNode.updateLayout(size: size, insets: UIEdgeInsets(top: 64.0, left: 0.0, bottom: 0.0, right: 0.0), anchorRect: anchorRect, centerAligned: true, isCoveredByInput: false, isAnimatingOut: false, forceUpdate: forceUpdate, transition: transition)
|
||||
|
||||
if isFirstTime {
|
||||
self.reactionContextNode.animateIn(from: anchorRect)
|
||||
Queue.mainQueue().justDispatch {
|
||||
self.reactionContextNode.animateIn(from: anchorRect)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6498,6 +6498,10 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
return
|
||||
}
|
||||
|
||||
if !isVideo {
|
||||
self.stickerResultController?.disappeared = nil
|
||||
}
|
||||
|
||||
let _ = (imagesReady.get()
|
||||
|> filter { $0 }
|
||||
|> take(1)
|
||||
@ -6508,7 +6512,6 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
if isVideo {
|
||||
self.uploadSticker(file, action: .send)
|
||||
} else {
|
||||
self.stickerResultController?.disappeared = nil
|
||||
self.completion(MediaEditorScreen.Result(
|
||||
media: .sticker(file: file, emoji: self.effectiveStickerEmoji()),
|
||||
mediaAreas: [],
|
||||
|
@ -1726,6 +1726,8 @@ extension ChatControllerImpl {
|
||||
}
|
||||
|
||||
func openStickerEditor() {
|
||||
self.chatDisplayNode.dismissInput()
|
||||
|
||||
var dismissImpl: (() -> Void)?
|
||||
let mainController = self.context.sharedContext.makeStickerMediaPickerScreen(
|
||||
context: self.context,
|
||||
|
Loading…
x
Reference in New Issue
Block a user