mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-06 17:00:13 +00:00
Various fixes
This commit is contained in:
parent
be662de71d
commit
b317ba1ee7
@ -7544,3 +7544,6 @@ Sorry for the inconvenience.";
|
||||
"Premium.Reactions.Proceed" = "Unlock Additional Reactions";
|
||||
|
||||
"AccessDenied.LocationPreciseDenied" = "To share your specific location in this chat, please go to Settings > Privacy > Location Services > Telegram and set Precise Location to On.";
|
||||
|
||||
"Chat.MultipleTypingPair" = "%@ and %@ ";
|
||||
"Chat.MultipleTypingMore" = "%@ and %@ others";
|
||||
|
||||
@ -163,6 +163,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
|
||||
public var completed: (Bool) -> Void = { _ in }
|
||||
public var frameUpdated: (Int, Int) -> Void = { _, _ in }
|
||||
public private(set) var currentFrameIndex: Int = 0
|
||||
public private(set) var currentFrameCount: Int = 0
|
||||
private var playFromIndex: Int?
|
||||
|
||||
private let timer = Atomic<SwiftSignalKit.Timer?>(value: nil)
|
||||
@ -452,6 +453,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
|
||||
|
||||
strongSelf.frameUpdated(frame.index, frame.totalFrames)
|
||||
strongSelf.currentFrameIndex = frame.index
|
||||
strongSelf.currentFrameCount = frame.totalFrames
|
||||
|
||||
if frame.isLastFrame {
|
||||
var stopped = false
|
||||
@ -556,6 +558,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
|
||||
|
||||
strongSelf.frameUpdated(frame.index, frame.totalFrames)
|
||||
strongSelf.currentFrameIndex = frame.index
|
||||
strongSelf.currentFrameCount = frame.totalFrames;
|
||||
|
||||
if frame.isLastFrame {
|
||||
var stopped = false
|
||||
|
||||
@ -322,7 +322,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
guard let presentationInterfaceState = self.presentationInterfaceState else {
|
||||
return 0.0
|
||||
}
|
||||
return self.updateLayout(width: size.width, leftInset: sideInset, rightInset: sideInset, additionalSideInsets: UIEdgeInsets(), maxHeight: size.height, isSecondary: false, transition: .immediate, interfaceState: presentationInterfaceState, metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact))
|
||||
return self.updateLayout(width: size.width, leftInset: sideInset, rightInset: sideInset, bottomInset: 0.0, additionalSideInsets: UIEdgeInsets(), maxHeight: size.height, isSecondary: false, transition: .immediate, interfaceState: presentationInterfaceState, metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact))
|
||||
}
|
||||
|
||||
public func setCaption(_ caption: NSAttributedString?) {
|
||||
@ -466,7 +466,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
return minimalHeight
|
||||
}
|
||||
|
||||
public func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
public func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
let hadLayout = self.validLayout != nil
|
||||
let previousAdditionalSideInsets = self.validLayout?.3
|
||||
self.validLayout = (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, isSecondary)
|
||||
@ -1080,7 +1080,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
self.focusUpdated?(true)
|
||||
|
||||
if self.isCaption, let (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, isSecondary) = self.validLayout, let presentationInterfaceState = self.presentationInterfaceState {
|
||||
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .animated(duration: 0.3, curve: .easeInOut), interfaceState: presentationInterfaceState, metrics: metrics)
|
||||
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: 0.0, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .animated(duration: 0.3, curve: .easeInOut), interfaceState: presentationInterfaceState, metrics: metrics)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1091,7 +1091,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
||||
self.focusUpdated?(false)
|
||||
|
||||
if self.isCaption, let (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, isSecondary) = self.validLayout, let presentationInterfaceState = self.presentationInterfaceState {
|
||||
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .animated(duration: 0.3, curve: .easeInOut), interfaceState: presentationInterfaceState, metrics: metrics)
|
||||
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: 0.0, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .animated(duration: 0.3, curve: .easeInOut), interfaceState: presentationInterfaceState, metrics: metrics)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1096,7 +1096,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate {
|
||||
if textInputPanelNode.frame.width.isZero {
|
||||
panelTransition = .immediate
|
||||
}
|
||||
let panelHeight = textInputPanelNode.updateLayout(width: layout.size.width, leftInset: insets.left + layout.safeInsets.left, rightInset: insets.right + layout.safeInsets.right, additionalSideInsets: UIEdgeInsets(), maxHeight: layout.size.height / 2.0, isSecondary: false, transition: panelTransition, interfaceState: self.presentationInterfaceState, metrics: layout.metrics)
|
||||
let panelHeight = textInputPanelNode.updateLayout(width: layout.size.width, leftInset: insets.left + layout.safeInsets.left, rightInset: insets.right + layout.safeInsets.right, bottomInset: 0.0, additionalSideInsets: UIEdgeInsets(), maxHeight: layout.size.height / 2.0, isSecondary: false, transition: panelTransition, interfaceState: self.presentationInterfaceState, metrics: layout.metrics)
|
||||
let panelFrame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: panelHeight)
|
||||
if textInputPanelNode.frame.width.isZero {
|
||||
textInputPanelNode.frame = panelFrame
|
||||
|
||||
@ -22,6 +22,8 @@ final class ChatVideoGalleryItemScrubberView: UIView {
|
||||
private let infoNode: ASTextNode
|
||||
private let scrubberNode: MediaPlayerScrubbingNode
|
||||
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
|
||||
private var playbackStatus: MediaPlayerStatus?
|
||||
private var chapters: [MediaPlayerScrubbingChapter] = []
|
||||
|
||||
@ -33,6 +35,8 @@ final class ChatVideoGalleryItemScrubberView: UIView {
|
||||
private var rightTimestampNodePushed = false
|
||||
private var infoNodePushed = false
|
||||
|
||||
private var currentChapter: MediaPlayerScrubbingChapter?
|
||||
|
||||
var hideWhenDurationIsUnknown = false {
|
||||
didSet {
|
||||
if self.hideWhenDurationIsUnknown {
|
||||
@ -172,20 +176,51 @@ final class ChatVideoGalleryItemScrubberView: UIView {
|
||||
self.chapterDisposable.set((mappedStatus
|
||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||
if let strongSelf = self, status.duration > 1.0, strongSelf.chapters.count > 0 {
|
||||
var text: String = ""
|
||||
|
||||
let previousChapter = strongSelf.currentChapter
|
||||
var currentChapter: MediaPlayerScrubbingChapter?
|
||||
for chapter in strongSelf.chapters {
|
||||
if chapter.start > status.timestamp {
|
||||
break
|
||||
} else {
|
||||
text = chapter.title
|
||||
currentChapter = chapter
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.infoNode.attributedText = NSAttributedString(string: text, font: textFont, textColor: .white)
|
||||
|
||||
if let (size, leftInset, rightInset) = strongSelf.containerLayout {
|
||||
strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .immediate)
|
||||
if let chapter = currentChapter, chapter != previousChapter {
|
||||
strongSelf.currentChapter = chapter
|
||||
|
||||
if strongSelf.scrubberNode.isScrubbing {
|
||||
strongSelf.hapticFeedback.impact(.light)
|
||||
}
|
||||
|
||||
if let previousChapter = previousChapter, !strongSelf.infoNode.alpha.isZero {
|
||||
if let snapshotView = strongSelf.infoNode.view.snapshotView(afterScreenUpdates: false) {
|
||||
snapshotView.frame = strongSelf.infoNode.frame
|
||||
strongSelf.infoNode.view.superview?.addSubview(snapshotView)
|
||||
|
||||
let offset: CGFloat = 30.0
|
||||
let snapshotTargetPosition: CGPoint
|
||||
let nodeStartPosition: CGPoint
|
||||
if previousChapter.start < chapter.start {
|
||||
snapshotTargetPosition = CGPoint(x: -offset, y: 0.0)
|
||||
nodeStartPosition = CGPoint(x: offset, y: 0.0)
|
||||
} else {
|
||||
snapshotTargetPosition = CGPoint(x: offset, y: 0.0)
|
||||
nodeStartPosition = CGPoint(x: -offset, y: 0.0)
|
||||
}
|
||||
snapshotView.layer.animatePosition(from: CGPoint(), to: snapshotTargetPosition, duration: 0.2, removeOnCompletion: false, additive: true)
|
||||
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
||||
snapshotView?.removeFromSuperview()
|
||||
})
|
||||
strongSelf.infoNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
strongSelf.infoNode.layer.animatePosition(from: nodeStartPosition, to: CGPoint(), duration: 0.2, additive: true)
|
||||
}
|
||||
}
|
||||
strongSelf.infoNode.attributedText = NSAttributedString(string: chapter.title, font: textFont, textColor: .white)
|
||||
|
||||
if let (size, leftInset, rightInset) = strongSelf.containerLayout {
|
||||
strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
@ -20,6 +20,10 @@
|
||||
- (void)play;
|
||||
- (void)pause;
|
||||
- (void)resetToStart;
|
||||
|
||||
- (void)playFromFrame:(NSInteger)frameIndex;
|
||||
- (void)copyStickerView:(NSObject<TGPhotoPaintStickerRenderView> *)view;
|
||||
|
||||
- (int64_t)documentId;
|
||||
- (UIImage *)image;
|
||||
|
||||
|
||||
@ -298,9 +298,12 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f;
|
||||
else
|
||||
{
|
||||
bool wasHidden = referenceView.isHidden;
|
||||
CGRect previousFrame = referenceView.frame;
|
||||
referenceView.frame = CGRectOffset(referenceView.frame, -1000.0, 0.0);
|
||||
referenceView.hidden = false;
|
||||
toTransitionView = [referenceView snapshotViewAfterScreenUpdates:true];
|
||||
referenceView.hidden = wasHidden;
|
||||
referenceView.frame = previousFrame;
|
||||
}
|
||||
|
||||
[parentView addSubview:toTransitionView];
|
||||
|
||||
@ -191,7 +191,6 @@
|
||||
{
|
||||
TGPhotoStickerEntityView *stickerView = [[TGPhotoStickerEntityView alloc] initWithEntity:entity context:self.stickersContext];
|
||||
[self _commonEntityViewSetup:stickerView entity:entity];
|
||||
[self addSubview:stickerView];
|
||||
|
||||
return stickerView;
|
||||
}
|
||||
@ -202,7 +201,6 @@
|
||||
[textView sizeToFit];
|
||||
|
||||
[self _commonEntityViewSetup:textView entity:entity];
|
||||
[self addSubview:textView];
|
||||
|
||||
return textView;
|
||||
}
|
||||
|
||||
@ -97,6 +97,8 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
|
||||
UIView<TGPhotoPaintPanelView> *_settingsView;
|
||||
id<TGPhotoPaintStickersScreen> _stickersScreen;
|
||||
|
||||
double _stickerStartTime;
|
||||
|
||||
bool _appeared;
|
||||
bool _skipEntitiesSetup;
|
||||
bool _entitiesReady;
|
||||
@ -149,6 +151,8 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
|
||||
_context = context;
|
||||
_enableStickers = photoEditor.enableStickers;
|
||||
|
||||
_stickerStartTime = NAN;
|
||||
|
||||
_actionHandle = [[ASHandle alloc] initWithDelegate:self releaseOnMainThread:true];
|
||||
|
||||
self.photoEditor = photoEditor;
|
||||
@ -1196,41 +1200,65 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
|
||||
TGPhotoPaintStickerEntity *entity = [[TGPhotoPaintStickerEntity alloc] initWithDocument:document baseSize:[self _stickerBaseSizeForCurrentPainting] animated:animated];
|
||||
[self _setStickerEntityPosition:entity];
|
||||
|
||||
|
||||
TGPhotoStickerEntityView *stickerView = (TGPhotoStickerEntityView *)[_entitiesContainerView createEntityViewWithEntity:entity];
|
||||
|
||||
bool hasStickers = false;
|
||||
TGPhotoStickerEntityView *existingStickerView;
|
||||
for (TGPhotoPaintEntityView *view in _entitiesContainerView.subviews) {
|
||||
if ([view isKindOfClass:[TGPhotoStickerEntityView class]]) {
|
||||
hasStickers = true;
|
||||
|
||||
if (((TGPhotoStickerEntityView *)view).documentId == stickerView.documentId) {
|
||||
existingStickerView = (TGPhotoStickerEntityView *)view;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TGPhotoStickerEntityView *stickerView = (TGPhotoStickerEntityView *)[_entitiesContainerView createEntityViewWithEntity:entity];
|
||||
[_entitiesContainerView addSubview:stickerView];
|
||||
[self _commonEntityViewSetup:stickerView];
|
||||
|
||||
__weak TGPhotoPaintController *weakSelf = self;
|
||||
__weak TGPhotoStickerEntityView *weakStickerView = stickerView;
|
||||
stickerView.started = ^(double duration) {
|
||||
__strong TGPhotoPaintController *strongSelf = weakSelf;
|
||||
if (strongSelf != nil) {
|
||||
TGPhotoEditorController *editorController = (TGPhotoEditorController *)self.parentViewController;
|
||||
TGPhotoEditorController *editorController = (TGPhotoEditorController *)strongSelf.parentViewController;
|
||||
if (![editorController isKindOfClass:[TGPhotoEditorController class]])
|
||||
return;
|
||||
|
||||
if (!hasStickers) {
|
||||
[editorController setMinimalVideoDuration:duration];
|
||||
}
|
||||
|
||||
NSTimeInterval currentTime = editorController.currentTime;
|
||||
__strong TGPhotoStickerEntityView *strongStickerView = weakStickerView;
|
||||
if (strongStickerView != nil) {
|
||||
if (!isnan(currentTime)) {
|
||||
[strongStickerView seekTo:currentTime];
|
||||
[strongStickerView play];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
NSTimeInterval currentTime = NAN;
|
||||
NSTimeInterval stickerStartTime = _stickerStartTime;
|
||||
TGPhotoEditorController *editorController = (TGPhotoEditorController *)self.parentViewController;
|
||||
if ([editorController isKindOfClass:[TGPhotoEditorController class]]) {
|
||||
currentTime = editorController.currentTime;
|
||||
}
|
||||
|
||||
if (!isnan(currentTime)) {
|
||||
[stickerView seekTo:currentTime];
|
||||
[stickerView play];
|
||||
} else {
|
||||
NSTimeInterval currentTime = CACurrentMediaTime();
|
||||
if (!isnan(stickerStartTime)) {
|
||||
if (existingStickerView != nil) {
|
||||
[stickerView copyStickerView:existingStickerView];
|
||||
} else {
|
||||
NSTimeInterval position = currentTime - stickerStartTime;
|
||||
[stickerView seekTo:position];
|
||||
[stickerView play];
|
||||
}
|
||||
} else {
|
||||
_stickerStartTime = currentTime;
|
||||
[stickerView play];
|
||||
}
|
||||
}
|
||||
|
||||
[self selectEntityView:stickerView];
|
||||
_entitySelectionView.alpha = 0.0f;
|
||||
|
||||
@ -1261,6 +1289,7 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
|
||||
entity.angle = [self startRotation];
|
||||
|
||||
TGPhotoTextEntityView *textView = (TGPhotoTextEntityView *)[_entitiesContainerView createEntityViewWithEntity:entity];
|
||||
[_entitiesContainerView addSubview:textView];
|
||||
[self _commonEntityViewSetup:textView];
|
||||
|
||||
[self selectEntityView:textView];
|
||||
|
||||
@ -14,6 +14,8 @@
|
||||
@property (nonatomic, readonly) TGPhotoPaintStickerEntity *entity;
|
||||
@property (nonatomic, readonly) bool isMirrored;
|
||||
|
||||
@property (nonatomic, readonly) int64_t documentId;
|
||||
|
||||
- (instancetype)initWithEntity:(TGPhotoPaintStickerEntity *)entity context:(id<TGPhotoPaintStickersContext>)context;
|
||||
- (void)mirror;
|
||||
- (UIImage *)image;
|
||||
@ -23,6 +25,8 @@
|
||||
- (void)play;
|
||||
- (void)pause;
|
||||
- (void)resetToStart;
|
||||
- (void)playFromFrame:(NSInteger)frameIndex;
|
||||
- (void)copyStickerView:(TGPhotoStickerEntityView *)view;
|
||||
|
||||
- (CGRect)realBounds;
|
||||
|
||||
|
||||
@ -201,6 +201,18 @@ const CGFloat TGPhotoStickerSelectionViewHandleSide = 30.0f;
|
||||
[_stickerView resetToStart];
|
||||
}
|
||||
|
||||
- (void)playFromFrame:(NSInteger)frameIndex {
|
||||
[_stickerView playFromFrame:frameIndex];
|
||||
}
|
||||
|
||||
- (void)copyStickerView:(TGPhotoStickerEntityView *)view {
|
||||
[_stickerView copyStickerView:view->_stickerView];
|
||||
}
|
||||
|
||||
- (int64_t)documentId {
|
||||
return [_stickerView documentId];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@ -63,6 +63,7 @@ class LegacyPaintStickerView: UIView, TGPhotoPaintStickerRenderView {
|
||||
if self.file.isAnimatedSticker || self.file.isVideoSticker {
|
||||
if self.animationNode == nil {
|
||||
let animationNode = AnimatedStickerNode()
|
||||
animationNode.autoplay = false
|
||||
self.animationNode = animationNode
|
||||
animationNode.started = { [weak self, weak animationNode] in
|
||||
self?.imageNode.isHidden = true
|
||||
@ -79,7 +80,6 @@ class LegacyPaintStickerView: UIView, TGPhotoPaintStickerRenderView {
|
||||
}
|
||||
let dimensions = self.file.dimensions ?? PixelDimensions(width: 512, height: 512)
|
||||
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: self.context.account.postbox, file: self.file, small: false, size: dimensions.cgSize.aspectFitted(CGSize(width: 256.0, height: 256.0))))
|
||||
self.updateVisibility()
|
||||
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: self.context.account, fileReference: stickerPackFileReference(self.file), resource: self.file.resource).start())
|
||||
} else {
|
||||
if let animationNode = self.animationNode {
|
||||
@ -110,7 +110,6 @@ class LegacyPaintStickerView: UIView, TGPhotoPaintStickerRenderView {
|
||||
if self.isPlaying != isPlaying {
|
||||
self.isPlaying = isPlaying
|
||||
|
||||
self.animationNode?.visibility = isPlaying
|
||||
if isPlaying && !self.didSetUpAnimationNode {
|
||||
self.didSetUpAnimationNode = true
|
||||
let dimensions = self.file.dimensions ?? PixelDimensions(width: 512, height: 512)
|
||||
@ -121,6 +120,7 @@ class LegacyPaintStickerView: UIView, TGPhotoPaintStickerRenderView {
|
||||
self.cachedDisposable.set((source.cachedDataPath(width: 384, height: 384)
|
||||
|> deliverOn(Queue.concurrentDefaultQueue())).start())
|
||||
}
|
||||
self.animationNode?.visibility = isPlaying
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,8 +132,7 @@ class LegacyPaintStickerView: UIView, TGPhotoPaintStickerRenderView {
|
||||
|
||||
func play() {
|
||||
self.isVisible = true
|
||||
self.isPlaying = true
|
||||
self.animationNode?.play()
|
||||
self.updateVisibility()
|
||||
}
|
||||
|
||||
func pause() {
|
||||
@ -148,6 +147,21 @@ class LegacyPaintStickerView: UIView, TGPhotoPaintStickerRenderView {
|
||||
self.animationNode?.seekTo(.timestamp(0.0))
|
||||
}
|
||||
|
||||
func play(fromFrame frameIndex: Int) {
|
||||
self.isVisible = true
|
||||
self.updateVisibility()
|
||||
self.animationNode?.play(fromIndex: frameIndex)
|
||||
}
|
||||
|
||||
func copyStickerView(_ view: TGPhotoPaintStickerRenderView!) {
|
||||
guard let view = view as? LegacyPaintStickerView, let animationNode = view.animationNode else {
|
||||
return
|
||||
}
|
||||
self.animationNode?.cloneCurrentFrame(from: animationNode)
|
||||
self.animationNode?.play(fromIndex: animationNode.currentFrameIndex)
|
||||
self.updateVisibility()
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ private func generateHandleBackground(color: UIColor) -> UIImage? {
|
||||
})?.stretchableImage(withLeftCapWidth: 0, topCapHeight: 2)
|
||||
}
|
||||
|
||||
public struct MediaPlayerScrubbingChapter {
|
||||
public struct MediaPlayerScrubbingChapter: Equatable {
|
||||
public let title: String
|
||||
public let start: Double
|
||||
|
||||
@ -37,7 +37,7 @@ private final class MediaPlayerScrubbingNodeButton: ASDisplayNode, UIGestureReco
|
||||
var highlighted: ((Bool) -> Void)?
|
||||
|
||||
var verticalPanEnabled = false
|
||||
var hapticFeedback = HapticFeedback()
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
|
||||
private var scrubbingMultiplier: Double = 1.0
|
||||
private var scrubbingStartLocation: CGPoint?
|
||||
@ -287,6 +287,10 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
|
||||
return self._scrubbingPosition.get()
|
||||
}
|
||||
|
||||
public var isScrubbing: Bool {
|
||||
return self.scrubbingTimestampValue != nil
|
||||
}
|
||||
|
||||
public var ignoreSeekId: Int?
|
||||
|
||||
public var enableScrubbing: Bool = true {
|
||||
@ -853,20 +857,28 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
|
||||
|
||||
chapterNodesContainer.frame = backgroundFrame
|
||||
|
||||
var addedBeginning = false
|
||||
for i in 1 ..< node.chapterNodes.count {
|
||||
let (previousChapter, previousChapterNode) = node.chapterNodes[i - 1]
|
||||
let (chapter, chapterNode) = node.chapterNodes[i]
|
||||
|
||||
let lineWidth: CGFloat = 1.0 + UIScreenPixel * 2.0
|
||||
let startPosition: CGFloat
|
||||
if i == 1 {
|
||||
if !addedBeginning {
|
||||
startPosition = 0.0
|
||||
} else {
|
||||
startPosition = floor(backgroundFrame.width * CGFloat(previousChapter.start / duration)) + lineWidth / 2.0
|
||||
}
|
||||
let endPosition: CGFloat = max(startPosition, floor(backgroundFrame.width * CGFloat(chapter.start / duration)) - lineWidth / 2.0)
|
||||
let width = endPosition - startPosition
|
||||
if width < lineWidth * 2.0 {
|
||||
previousChapterNode.frame = CGRect()
|
||||
continue
|
||||
}
|
||||
previousChapterNode.frame = CGRect(x: startPosition, y: 0.0, width: endPosition - startPosition, height: backgroundFrame.size.height)
|
||||
|
||||
addedBeginning = true
|
||||
|
||||
if i == node.chapterNodes.count - 1 {
|
||||
let startPosition = endPosition + lineWidth
|
||||
chapterNode.frame = CGRect(x: startPosition, y: 0.0, width: backgroundFrame.size.width - startPosition, height: backgroundFrame.size.height)
|
||||
@ -877,7 +889,6 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if let handleNode = node.handleNode {
|
||||
var handleSize: CGSize = CGSize(width: 2.0, height: bounds.size.height)
|
||||
var handleOffset: CGFloat = 0.0
|
||||
|
||||
@ -89,7 +89,7 @@ final class ChatBotStartInputPanelNode: ChatInputPanelNode {
|
||||
self.interfaceInteraction?.sendBotStart(presentationInterfaceState.botStartPayload)
|
||||
}
|
||||
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
if self.presentationInterfaceState != interfaceState {
|
||||
self.presentationInterfaceState = interfaceState
|
||||
}
|
||||
|
||||
@ -127,7 +127,7 @@ final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
||||
|
||||
private var presentationInterfaceState: ChatPresentationInterfaceState?
|
||||
|
||||
private var layoutData: (CGFloat, CGFloat, CGFloat, UIEdgeInsets, CGFloat, Bool, LayoutMetrics)?
|
||||
private var layoutData: (CGFloat, CGFloat, CGFloat, CGFloat, UIEdgeInsets, CGFloat, Bool, LayoutMetrics)?
|
||||
|
||||
override init() {
|
||||
self.button = HighlightableButtonNode()
|
||||
@ -208,8 +208,8 @@ final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
||||
}
|
||||
|
||||
self.isJoining = true
|
||||
if let (width, leftInset, rightInset, additionalSideInsets, maxHeight, isSecondary, metrics) = self.layoutData, let presentationInterfaceState = self.presentationInterfaceState {
|
||||
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: presentationInterfaceState, metrics: metrics, force: true)
|
||||
if let (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, isSecondary, metrics) = self.layoutData, let presentationInterfaceState = self.presentationInterfaceState {
|
||||
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: presentationInterfaceState, metrics: metrics, force: true)
|
||||
}
|
||||
self.actionDisposable.set((context.peerChannelMemberCategoriesContextsManager.join(engine: context.engine, peerId: peer.id, hash: nil)
|
||||
|> afterDisposed { [weak self] in
|
||||
@ -265,12 +265,12 @@ final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
||||
}
|
||||
}
|
||||
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
return self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: transition, interfaceState: interfaceState, metrics: metrics, force: false)
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
return self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: transition, interfaceState: interfaceState, metrics: metrics, force: false)
|
||||
}
|
||||
|
||||
private func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, force: Bool) -> CGFloat {
|
||||
self.layoutData = (width, leftInset, rightInset, additionalSideInsets, maxHeight, isSecondary, metrics)
|
||||
private func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics, force: Bool) -> CGFloat {
|
||||
self.layoutData = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, isSecondary, metrics)
|
||||
|
||||
if self.presentationInterfaceState != interfaceState || force {
|
||||
let previousState = self.presentationInterfaceState
|
||||
|
||||
@ -10668,10 +10668,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
photoOnly = true
|
||||
}
|
||||
|
||||
let storeEditedPhotos = settings.storeEditedPhotos
|
||||
let storeCapturedMedia = peer.id.namespace != Namespaces.Peer.SecretChat
|
||||
let inputText = strongSelf.presentationInterfaceState.interfaceState.effectiveInputState.inputText
|
||||
|
||||
presentedLegacyCamera(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: nil, parentController: strongSelf, attachmentController: self?.attachmentController, editingMedia: false, saveCapturedPhotos: storeEditedPhotos, mediaGrouping: true, initialCaption: inputText, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, photoOnly: photoOnly, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in
|
||||
presentedLegacyCamera(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: nil, parentController: strongSelf, attachmentController: self?.attachmentController, editingMedia: false, saveCapturedPhotos: storeCapturedMedia, mediaGrouping: true, initialCaption: inputText, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, photoOnly: photoOnly, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in
|
||||
if let strongSelf = self {
|
||||
strongSelf.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil)
|
||||
if !inputText.string.isEmpty {
|
||||
@ -11276,7 +11276,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
photoOnly = true
|
||||
}
|
||||
|
||||
presentedLegacyCamera(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: menuController, parentController: strongSelf, editingMedia: editMediaOptions != nil, saveCapturedPhotos: settings.storeEditedPhotos, mediaGrouping: true, initialCaption: inputText, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, photoOnly: photoOnly, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in
|
||||
presentedLegacyCamera(context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, cameraView: cameraView, menuController: menuController, parentController: strongSelf, editingMedia: editMediaOptions != nil, saveCapturedPhotos: peer.id.namespace != Namespaces.Peer.SecretChat, mediaGrouping: true, initialCaption: inputText, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, photoOnly: photoOnly, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in
|
||||
if let strongSelf = self {
|
||||
if editMediaOptions != nil {
|
||||
strongSelf.editMessageMediaWithLegacySignals(signals!)
|
||||
|
||||
@ -990,7 +990,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
if inputTextPanelNode.isFocused {
|
||||
self.context.sharedContext.mainWindow?.simulateKeyboardDismiss(transition: .animated(duration: 0.5, curve: .spring))
|
||||
}
|
||||
let _ = inputTextPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - insets.bottom, isSecondary: false, transition: transition, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics)
|
||||
let _ = inputTextPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - insets.bottom, isSecondary: false, transition: transition, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics)
|
||||
}
|
||||
if let prevInputPanelNode = self.inputPanelNode, inputPanelNode.canHandleTransition(from: prevInputPanelNode) {
|
||||
inputPanelNodeHandlesTransition = true
|
||||
@ -1000,7 +1000,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
} else {
|
||||
dismissedInputPanelNode = self.inputPanelNode
|
||||
}
|
||||
let inputPanelHeight = inputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - insets.bottom, isSecondary: false, transition: inputPanelNode.supernode !== self ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics)
|
||||
let inputPanelHeight = inputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - insets.bottom, isSecondary: false, transition: inputPanelNode.supernode !== self ? .immediate : transition, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics)
|
||||
inputPanelSize = CGSize(width: layout.size.width, height: inputPanelHeight)
|
||||
self.inputPanelNode = inputPanelNode
|
||||
if inputPanelNode.supernode !== self {
|
||||
@ -1008,7 +1008,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.inputPanelContainerNode.insertSubnode(inputPanelNode, aboveSubnode: self.inputPanelBackgroundNode)
|
||||
}
|
||||
} else {
|
||||
let inputPanelHeight = inputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - insets.bottom - 120.0, isSecondary: false, transition: transition, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics)
|
||||
let inputPanelHeight = inputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - insets.bottom - 120.0, isSecondary: false, transition: transition, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics)
|
||||
inputPanelSize = CGSize(width: layout.size.width, height: inputPanelHeight)
|
||||
}
|
||||
} else {
|
||||
@ -1019,7 +1019,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
if let secondaryInputPanelNode = inputPanelNodes.secondary, !previewing {
|
||||
if secondaryInputPanelNode !== self.secondaryInputPanelNode {
|
||||
dismissedSecondaryInputPanelNode = self.secondaryInputPanelNode
|
||||
let inputPanelHeight = secondaryInputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - insets.bottom, isSecondary: true, transition: .immediate, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics)
|
||||
let inputPanelHeight = secondaryInputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - insets.bottom, isSecondary: true, transition: .immediate, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics)
|
||||
secondaryInputPanelSize = CGSize(width: layout.size.width, height: inputPanelHeight)
|
||||
self.secondaryInputPanelNode = secondaryInputPanelNode
|
||||
if secondaryInputPanelNode.supernode == nil {
|
||||
@ -1027,7 +1027,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.inputPanelContainerNode.insertSubnode(secondaryInputPanelNode, aboveSubnode: self.inputPanelBackgroundNode)
|
||||
}
|
||||
} else {
|
||||
let inputPanelHeight = secondaryInputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - insets.bottom, isSecondary: true, transition: transition, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics)
|
||||
let inputPanelHeight = secondaryInputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: layout.additionalInsets, maxHeight: layout.size.height - insets.top - insets.bottom, isSecondary: true, transition: transition, interfaceState: self.chatPresentationInterfaceState, metrics: layout.metrics)
|
||||
secondaryInputPanelSize = CGSize(width: layout.size.width, height: inputPanelHeight)
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -54,7 +54,7 @@ final class ChatFeedNavigationInputPanelNode: ChatInputPanelNode {
|
||||
self.interfaceInteraction?.navigateFeed()
|
||||
}
|
||||
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
if self.presentationInterfaceState != interfaceState {
|
||||
self.presentationInterfaceState = interfaceState
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ class ChatInputPanelNode: ASDisplayNode {
|
||||
func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
}
|
||||
|
||||
func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
|
||||
@ -1629,10 +1629,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
} else {
|
||||
let pathPrefix = item.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(resource.id)
|
||||
let additionalAnimationNode = AnimatedStickerNode()
|
||||
additionalAnimationNode.setup(source: source, width: Int(animationSize.width * 2.5), height: Int(animationSize.height * 2.5), playbackMode: .once, mode: .direct(cachePathPrefix: pathPrefix))
|
||||
additionalAnimationNode.setup(source: source, width: Int(animationSize.width * 2), height: Int(animationSize.height * 2), playbackMode: .once, mode: .direct(cachePathPrefix: pathPrefix))
|
||||
var animationFrame: CGRect
|
||||
if isStickerEffect {
|
||||
animationFrame = animationNode.frame.offsetBy(dx: incomingMessage ? animationNode.frame.width * 0.66 - 14.0 : -animationNode.frame.width * 0.66 + 14.0, dy: 35.0).insetBy(dx: -animationNode.frame.width * 0.66, dy: -animationNode.frame.height * 0.66)
|
||||
animationFrame = animationNode.frame.offsetBy(dx: incomingMessage ? animationNode.frame.width * 0.5 : -animationNode.frame.width * 0.5, dy: 35.0).insetBy(dx: -animationNode.frame.width * 0.5, dy: -animationNode.frame.height * 0.5)
|
||||
if incomingMessage {
|
||||
animationNode.transform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ final class ChatMessageReportInputPanelNode: ChatInputPanelNode {
|
||||
}
|
||||
}
|
||||
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
if self.presentationInterfaceState != interfaceState {
|
||||
self.presentationInterfaceState = interfaceState
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
|
||||
private let shareButton: HighlightableButtonNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
|
||||
private var validLayout: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, metrics: LayoutMetrics, isSecondary: Bool)?
|
||||
private var validLayout: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, metrics: LayoutMetrics, isSecondary: Bool)?
|
||||
private var presentationInterfaceState: ChatPresentationInterfaceState?
|
||||
private var actions: ChatAvailableMessageActions?
|
||||
|
||||
@ -33,8 +33,8 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
|
||||
|
||||
if self.selectedMessages.isEmpty {
|
||||
self.actions = nil
|
||||
if let (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, isSecondary) = self.validLayout, let interfaceState = self.presentationInterfaceState {
|
||||
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: interfaceState, metrics: metrics)
|
||||
if let (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary) = self.validLayout, let interfaceState = self.presentationInterfaceState {
|
||||
let _ = self.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: interfaceState, metrics: metrics)
|
||||
}
|
||||
self.canDeleteMessagesDisposable.set(nil)
|
||||
} else if let context = self.context {
|
||||
@ -42,8 +42,8 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
|
||||
|> deliverOnMainQueue).start(next: { [weak self] actions in
|
||||
if let strongSelf = self {
|
||||
strongSelf.actions = actions
|
||||
if let (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, isSecondary) = strongSelf.validLayout, let interfaceState = strongSelf.presentationInterfaceState {
|
||||
let _ = strongSelf.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: interfaceState, metrics: metrics)
|
||||
if let (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary) = strongSelf.validLayout, let interfaceState = strongSelf.presentationInterfaceState {
|
||||
let _ = strongSelf.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: interfaceState, metrics: metrics)
|
||||
}
|
||||
}
|
||||
}))
|
||||
@ -154,8 +154,8 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode {
|
||||
}
|
||||
}
|
||||
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
self.validLayout = (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, isSecondary)
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
self.validLayout = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary)
|
||||
|
||||
let panelHeight = defaultHeight(metrics: metrics)
|
||||
|
||||
|
||||
@ -135,7 +135,7 @@ final class ChatRecordingPreviewInputPanelNode: ChatInputPanelNode {
|
||||
}
|
||||
}
|
||||
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
if self.presentationInterfaceState != interfaceState {
|
||||
var updateWaveform = false
|
||||
if self.presentationInterfaceState?.recordedMediaPreview != interfaceState.recordedMediaPreview {
|
||||
|
||||
@ -23,7 +23,7 @@ final class ChatRestrictedInputPanelNode: ChatInputPanelNode {
|
||||
self.addSubnode(self.textNode)
|
||||
}
|
||||
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
if self.presentationInterfaceState != interfaceState {
|
||||
self.presentationInterfaceState = interfaceState
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
|
||||
|
||||
private var needsSearchResultsTooltip = true
|
||||
|
||||
private var validLayout: (CGFloat, CGFloat, CGFloat, UIEdgeInsets, CGFloat, LayoutMetrics, Bool)?
|
||||
private var validLayout: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, metrics: LayoutMetrics, isSecondary: Bool)?
|
||||
|
||||
override var interfaceInteraction: ChatPanelInterfaceInteraction? {
|
||||
didSet {
|
||||
@ -38,8 +38,8 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
|
||||
if let strongSelf = self, strongSelf.displayActivity != value {
|
||||
strongSelf.displayActivity = value
|
||||
strongSelf.activityIndicator.isHidden = !value
|
||||
if let interfaceState = strongSelf.presentationInterfaceState, let validLayout = strongSelf.validLayout {
|
||||
let _ = strongSelf.updateLayout(width: validLayout.0, leftInset: validLayout.1, rightInset: validLayout.2, additionalSideInsets: validLayout.3, maxHeight: validLayout.4, isSecondary: validLayout.6, transition: .immediate, interfaceState: interfaceState, metrics: validLayout.5)
|
||||
if let interfaceState = strongSelf.presentationInterfaceState, let (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary) = strongSelf.validLayout {
|
||||
let _ = strongSelf.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: interfaceState, metrics: metrics)
|
||||
}
|
||||
}
|
||||
}))
|
||||
@ -127,8 +127,8 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
|
||||
}
|
||||
}
|
||||
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
self.validLayout = (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, isSecondary)
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
self.validLayout = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary)
|
||||
|
||||
if self.presentationInterfaceState != interfaceState {
|
||||
let themeUpdated = self.presentationInterfaceState?.theme !== interfaceState.theme
|
||||
|
||||
@ -279,7 +279,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
|
||||
private var accessoryItemButtons: [(ChatTextInputAccessoryItem, AccessoryItemIconButtonNode)] = []
|
||||
|
||||
private var validLayout: (CGFloat, CGFloat, CGFloat, UIEdgeInsets, CGFloat, LayoutMetrics, Bool)?
|
||||
private var validLayout: (CGFloat, CGFloat, CGFloat, CGFloat, UIEdgeInsets, CGFloat, LayoutMetrics, Bool)?
|
||||
private var leftMenuInset: CGFloat = 0.0
|
||||
|
||||
var displayAttachmentMenu: () -> Void = { }
|
||||
@ -450,6 +450,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
|
||||
private var spoilersRevealed = false
|
||||
|
||||
private var touchDownGestureRecognizer: TouchDownGestureRecognizer?
|
||||
|
||||
init(presentationInterfaceState: ChatPresentationInterfaceState, presentationContext: ChatPresentationContext?, presentController: @escaping (ViewController) -> Void) {
|
||||
self.presentationInterfaceState = presentationInterfaceState
|
||||
|
||||
@ -584,15 +586,15 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
}
|
||||
self.actionButtons.micButton.offsetRecordingControls = { [weak self] in
|
||||
if let strongSelf = self, let presentationInterfaceState = strongSelf.presentationInterfaceState {
|
||||
if let (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, isSecondary) = strongSelf.validLayout {
|
||||
let _ = strongSelf.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: presentationInterfaceState, metrics: metrics)
|
||||
if let (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary) = strongSelf.validLayout {
|
||||
let _ = strongSelf.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: presentationInterfaceState, metrics: metrics)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.actionButtons.micButton.updateCancelTranslation = { [weak self] in
|
||||
if let strongSelf = self, let presentationInterfaceState = strongSelf.presentationInterfaceState {
|
||||
if let (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, isSecondary) = strongSelf.validLayout {
|
||||
let _ = strongSelf.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: presentationInterfaceState, metrics: metrics)
|
||||
if let (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary) = strongSelf.validLayout {
|
||||
let _ = strongSelf.updateLayout(width: width, leftInset: leftInset, rightInset: rightInset, bottomInset: bottomInset, additionalSideInsets: additionalSideInsets, maxHeight: maxHeight, isSecondary: isSecondary, transition: .immediate, interfaceState: presentationInterfaceState, metrics: metrics)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -739,6 +741,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
}
|
||||
}
|
||||
textInputNode.view.addGestureRecognizer(recognizer)
|
||||
self.touchDownGestureRecognizer = recognizer
|
||||
|
||||
textInputNode.textView.accessibilityHint = self.textPlaceholderNode.attributedText?.string
|
||||
}
|
||||
@ -824,9 +827,17 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
let previousAdditionalSideInsets = self.validLayout?.3
|
||||
self.validLayout = (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, isSecondary)
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
let previousAdditionalSideInsets = self.validLayout?.4
|
||||
self.validLayout = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, metrics, isSecondary)
|
||||
|
||||
let textFieldWaitsForTouchUp: Bool
|
||||
if case .regular = metrics.widthClass, bottomInset.isZero {
|
||||
textFieldWaitsForTouchUp = true
|
||||
} else {
|
||||
textFieldWaitsForTouchUp = false
|
||||
}
|
||||
self.touchDownGestureRecognizer?.waitForTouchUp = textFieldWaitsForTouchUp
|
||||
|
||||
var transition = transition
|
||||
var additionalOffset: CGFloat = 0.0
|
||||
@ -2026,7 +2037,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
self.counterTextNode.attributedText = NSAttributedString(string: "", font: counterFont, textColor: .black)
|
||||
}
|
||||
|
||||
if let (width, leftInset, rightInset, _, maxHeight, metrics, _) = self.validLayout {
|
||||
if let (width, leftInset, rightInset, _, _, maxHeight, metrics, _) = self.validLayout {
|
||||
var composeButtonsOffset: CGFloat = 0.0
|
||||
if self.extendedSearchLayout {
|
||||
composeButtonsOffset = 44.0
|
||||
@ -2217,7 +2228,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
||||
}
|
||||
|
||||
private func updateTextHeight(animated: Bool) {
|
||||
if let (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, _) = self.validLayout {
|
||||
if let (width, leftInset, rightInset, _, additionalSideInsets, maxHeight, metrics, _) = self.validLayout {
|
||||
let (_, textFieldHeight) = self.calculateTextFieldMetrics(width: width - leftInset - rightInset - additionalSideInsets.right - self.leftMenuInset, maxHeight: maxHeight, metrics: metrics)
|
||||
let panelHeight = self.panelHeight(textFieldHeight: textFieldHeight, metrics: metrics)
|
||||
if !self.bounds.size.height.isEqual(to: panelHeight) {
|
||||
|
||||
@ -364,6 +364,17 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
stringValue = ""
|
||||
}
|
||||
} else {
|
||||
if inputActivities.count > 1 {
|
||||
let peerTitle = EnginePeer(inputActivities[0].0).compactDisplayTitle
|
||||
if inputActivities.count == 2 {
|
||||
let secondPeerTitle = EnginePeer(inputActivities[1].0).compactDisplayTitle
|
||||
stringValue = strings.Chat_MultipleTypingPair(peerTitle, secondPeerTitle).string
|
||||
} else {
|
||||
stringValue = strings.Chat_MultipleTypingMore(peerTitle, String(inputActivities.count - 1)).string
|
||||
}
|
||||
} else if let (peer, _) = inputActivities.first {
|
||||
stringValue = EnginePeer(peer).compactDisplayTitle
|
||||
}
|
||||
for (peer, _) in inputActivities {
|
||||
let title = EnginePeer(peer).compactDisplayTitle
|
||||
if !title.isEmpty {
|
||||
|
||||
@ -82,7 +82,7 @@ final class ChatUnblockInputPanelNode: ChatInputPanelNode {
|
||||
self.interfaceInteraction?.unblockPeer()
|
||||
}
|
||||
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
if self.presentationInterfaceState != interfaceState {
|
||||
self.presentationInterfaceState = interfaceState
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ final class DeleteChatInputPanelNode: ChatInputPanelNode {
|
||||
self.interfaceInteraction?.deleteChat()
|
||||
}
|
||||
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
if self.presentationInterfaceState != interfaceState {
|
||||
self.presentationInterfaceState = interfaceState
|
||||
|
||||
|
||||
@ -366,7 +366,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
|
||||
self.separatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor
|
||||
|
||||
let interfaceState = ChatPresentationInterfaceState(chatWallpaper: .color(0), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, limitsConfiguration: .defaultValue, fontSize: .regular, bubbleCorners: PresentationChatBubbleCorners(mainRadius: 16.0, auxiliaryRadius: 8.0, mergeBubbleCorners: true), accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(id: self.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil)
|
||||
let panelHeight = self.selectionPanel.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: UIEdgeInsets(), maxHeight: 0.0, isSecondary: false, transition: transition, interfaceState: interfaceState, metrics: layout.metrics)
|
||||
let panelHeight = self.selectionPanel.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: UIEdgeInsets(), maxHeight: 0.0, isSecondary: false, transition: transition, interfaceState: interfaceState, metrics: layout.metrics)
|
||||
|
||||
transition.updateFrame(node: self.selectionPanel, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: panelHeight)))
|
||||
|
||||
|
||||
@ -489,7 +489,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
if textInputPanelNode.frame.width.isZero {
|
||||
panelTransition = .immediate
|
||||
}
|
||||
var panelHeight = textInputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, additionalSideInsets: UIEdgeInsets(), maxHeight: layout.size.height / 2.0, isSecondary: false, transition: panelTransition, interfaceState: self.presentationInterfaceState, metrics: layout.metrics)
|
||||
var panelHeight = textInputPanelNode.updateLayout(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: layout.intrinsicInsets.bottom, additionalSideInsets: UIEdgeInsets(), maxHeight: layout.size.height / 2.0, isSecondary: false, transition: panelTransition, interfaceState: self.presentationInterfaceState, metrics: layout.metrics)
|
||||
if self.searchDisplayController == nil {
|
||||
panelHeight += insets.bottom
|
||||
} else {
|
||||
|
||||
@ -44,7 +44,7 @@ final class SecretChatHandshakeStatusInputPanelNode: ChatInputPanelNode {
|
||||
self.interfaceInteraction?.unblockPeer()
|
||||
}
|
||||
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat {
|
||||
if self.presentationInterfaceState != interfaceState {
|
||||
self.presentationInterfaceState = interfaceState
|
||||
|
||||
|
||||
@ -5,6 +5,9 @@ import UIKit.UIGestureRecognizerSubclass
|
||||
public class TouchDownGestureRecognizer: UIGestureRecognizer, UIGestureRecognizerDelegate {
|
||||
public var touchDown: (() -> Void)?
|
||||
|
||||
private var touchLocation: CGPoint?
|
||||
public var waitForTouchUp = false
|
||||
|
||||
override public init(target: Any?, action: Selector?) {
|
||||
super.init(target: target, action: action)
|
||||
|
||||
@ -18,8 +21,42 @@ public class TouchDownGestureRecognizer: UIGestureRecognizer, UIGestureRecognize
|
||||
override public func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
super.touchesBegan(touches, with: event)
|
||||
|
||||
if let touchDown = self.touchDown {
|
||||
if self.waitForTouchUp {
|
||||
if let touch = touches.first {
|
||||
self.touchLocation = touch.location(in: self.view)
|
||||
}
|
||||
} else if let touchDown = self.touchDown {
|
||||
touchDown()
|
||||
}
|
||||
}
|
||||
|
||||
override public func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
super.touchesMoved(touches, with: event)
|
||||
|
||||
guard let touch = touches.first else {
|
||||
return
|
||||
}
|
||||
|
||||
if let touchLocation = self.touchLocation {
|
||||
let location = touch.location(in: self.view)
|
||||
let distance = CGPoint(x: location.x - touchLocation.x, y: location.y - touchLocation.y)
|
||||
if distance.x * distance.x + distance.y * distance.y > 4.0 {
|
||||
self.state = .cancelled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
super.touchesEnded(touches, with: event)
|
||||
|
||||
if let touchDown = self.touchDown, self.waitForTouchUp {
|
||||
touchDown()
|
||||
}
|
||||
}
|
||||
|
||||
override public func reset() {
|
||||
self.touchLocation = nil
|
||||
|
||||
super.reset()
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user