Attachment menu improvements

This commit is contained in:
Ilya Laktyushin 2022-03-05 00:16:34 +04:00
parent f0e55a0e0a
commit a354c45ccf
4 changed files with 334 additions and 34 deletions

View File

@ -74,17 +74,17 @@ private func generateShadowImage() -> UIImage? {
}
private func generateMaskImage() -> UIImage? {
return generateImage(CGSize(width: 390.0, height: 620.0), rotatedContext: { size, context in
return generateImage(CGSize(width: 390.0, height: 220.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(UIColor.white.cgColor)
let path = UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: 390.0, height: 609.0), cornerRadius: 10.0).cgPath
let path = UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: 390.0, height: 209.0), cornerRadius: 10.0).cgPath
context.addPath(path)
context.fillPath()
try? drawSvgPath(context, path: "M183.219,608.889 H206.781 C205.648,608.889 204.567,609.37 203.808,610.213 L197.23,617.522 C196.038,618.847 193.962,618.847 192.77,617.522 L186.192,610.213 C185.433,609.37 184.352,608.889 183.219,608.889 Z ")
})
try? drawSvgPath(context, path: "M183.219,208.89 H206.781 C205.648,208.89 204.567,209.371 203.808,210.214 L197.23,217.523 C196.038,218.848 193.962,218.848 192.77,217.523 L186.192,210.214 C185.433,209.371 184.352,208.89 183.219,208.89 Z ")
})?.stretchableImage(withLeftCapWidth: 195, topCapHeight: 110)
}
public class AttachmentController: ViewController {
@ -368,6 +368,8 @@ public class AttachmentController: ViewController {
})
}
})
} else {
strongSelf.animating = false
}
snapshotView?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { [weak snapshotView] _ in
@ -457,7 +459,9 @@ public class AttachmentController: ViewController {
var containerLayout = layout
let containerRect: CGRect
if case .regular = layout.metrics.widthClass {
let size = CGSize(width: 390.0, height: 620.0)
let availableHeight = layout.size.height - (layout.inputHeight ?? 0.0) - 60.0
let size = CGSize(width: 390.0, height: min(620.0, availableHeight))
let insets = layout.insets(options: [.input])
let masterWidth = min(max(320.0, floor(layout.size.width / 3.0)), floor(layout.size.width / 2.0))
@ -471,9 +475,12 @@ public class AttachmentController: ViewController {
if self.wrapperNode.view.mask == nil {
let maskView = UIImageView()
maskView.image = generateMaskImage()
maskView.frame = CGRect(origin: CGPoint(), size: maskView.image?.size ?? CGSize())
maskView.contentMode = .scaleToFill
self.wrapperNode.view.mask = maskView
}
if let maskView = self.wrapperNode.view.mask {
transition.updateFrame(view: maskView, frame: CGRect(origin: CGPoint(), size: size))
}
self.shadowNode.alpha = 1.0
if self.shadowNode.image == nil {

View File

@ -114,13 +114,26 @@ final class MediaPickerGridItemNode: GridItemNode {
}
}
var _cachedTag: Int32?
var tag: Int32? {
if let tag = self._cachedTag {
return tag
} else if let asset = self.asset, let localTimestamp = asset.creationDate?.timeIntervalSince1970 {
let tag = Month(localTimestamp: Int32(localTimestamp)).packedValue
self._cachedTag = tag
return tag
} else {
return nil
}
}
func updateSelectionState(animated: Bool = false) {
if self.checkNode == nil, let _ = self.interaction?.selectionState, let theme = self.theme {
let checkNode = InteractiveCheckNode(theme: CheckNodeTheme(theme: theme, style: .overlay))
checkNode.valueChanged = { [weak self] value in
if let strongSelf = self, let asset = strongSelf.asset, let interaction = strongSelf.interaction {
if let legacyAsset = TGMediaAsset(phAsset: asset) {
interaction.toggleSelection(legacyAsset, value)
interaction.toggleSelection(legacyAsset, value, false)
}
}
}
@ -164,6 +177,8 @@ final class MediaPickerGridItemNode: GridItemNode {
self.interaction = interaction
self.theme = theme
self.backgroundColor = theme.list.mediaPlaceholderColor
if self.currentState == nil || self.currentState!.0 !== fetchResult || self.currentState!.1 != index {
let editingContext = interaction.editingState
let asset = fetchResult.object(at: index)

View File

@ -17,13 +17,15 @@ import LegacyMediaPickerUI
import AttachmentUI
import ContextUI
import WebSearchUI
import SparseItemGrid
import UndoUI
let overflowInset: CGFloat = 70.0
let overflowInset: CGFloat = 0.0
final class MediaPickerInteraction {
let openMedia: (PHFetchResult<PHAsset>, Int, UIImage?) -> Void
let openSelectedMedia: (TGMediaSelectableItem, UIImage?) -> Void
let toggleSelection: (TGMediaSelectableItem, Bool) -> Void
let toggleSelection: (TGMediaSelectableItem, Bool, Bool) -> Void
let sendSelected: (TGMediaSelectableItem?, Bool, Int32?, Bool) -> Void
let schedule: () -> Void
let dismissInput: () -> Void
@ -31,7 +33,7 @@ final class MediaPickerInteraction {
let editingState: TGMediaEditingContext
var hiddenMediaId: String?
init(openMedia: @escaping (PHFetchResult<PHAsset>, Int, UIImage?) -> Void, openSelectedMedia: @escaping (TGMediaSelectableItem, UIImage?) -> Void, toggleSelection: @escaping (TGMediaSelectableItem, Bool) -> Void, sendSelected: @escaping (TGMediaSelectableItem?, Bool, Int32?, Bool) -> Void, schedule: @escaping () -> Void, dismissInput: @escaping () -> Void, selectionState: TGMediaSelectionContext?, editingState: TGMediaEditingContext) {
init(openMedia: @escaping (PHFetchResult<PHAsset>, Int, UIImage?) -> Void, openSelectedMedia: @escaping (TGMediaSelectableItem, UIImage?) -> Void, toggleSelection: @escaping (TGMediaSelectableItem, Bool, Bool) -> Void, sendSelected: @escaping (TGMediaSelectableItem?, Bool, Int32?, Bool) -> Void, schedule: @escaping () -> Void, dismissInput: @escaping () -> Void, selectionState: TGMediaSelectionContext?, editingState: TGMediaEditingContext) {
self.openMedia = openMedia
self.openSelectedMedia = openSelectedMedia
self.toggleSelection = toggleSelection
@ -73,6 +75,33 @@ private struct MediaPickerGridTransaction {
}
}
struct Month: Equatable {
var packedValue: Int32
init(packedValue: Int32) {
self.packedValue = packedValue
}
init(localTimestamp: Int32) {
var time: time_t = time_t(localTimestamp)
var timeinfo: tm = tm()
gmtime_r(&time, &timeinfo)
let year = UInt32(timeinfo.tm_year)
let month = UInt32(timeinfo.tm_mon)
self.packedValue = Int32(bitPattern: year | (month << 16))
}
var year: Int32 {
return Int32(bitPattern: (UInt32(bitPattern: self.packedValue) >> 0) & 0xffff)
}
var month: Int32 {
return Int32(bitPattern: (UInt32(bitPattern: self.packedValue) >> 16) & 0xffff)
}
}
public final class MediaPickerScreen: ViewController, AttachmentContainable {
private let context: AccountContext
private var presentationData: PresentationData
@ -131,6 +160,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
fileprivate var cameraView: TGAttachmentCameraView?
private var placeholderNode: MediaPickerPlaceholderNode?
private var manageNode: MediaPickerManageNode?
private var scrollingArea: SparseItemGridScrollingArea
private var selectionNode: MediaPickerSelectedListNode?
@ -167,6 +197,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self.backgroundNode = NavigationBackgroundNode(color: self.presentationData.theme.rootController.tabBar.backgroundColor)
self.backgroundNode.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
self.gridNode = GridNode()
self.scrollingArea = SparseItemGridScrollingArea()
super.init()
@ -177,7 +208,8 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self.addSubnode(self.containerNode)
self.containerNode.addSubnode(self.backgroundNode)
self.containerNode.addSubnode(self.gridNode)
self.containerNode.addSubnode(self.scrollingArea)
let collection = controller.collection
let preloadPromise = self.preloadPromise
let updatedState = combineLatest(mediaAssetsContext.mediaAccess(), mediaAssetsContext.cameraAccess())
@ -215,6 +247,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self.gridNode.visibleContentOffsetChanged = { [weak self] _ in
self?.updateNavigation(transition: .immediate)
self?.updateScrollingArea()
}
self.hiddenMediaDisposable = (self.hiddenMediaId.get()
@ -295,6 +328,21 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
}
}
self.scrollingArea.beginScrolling = { [weak self] in
guard let strongSelf = self else {
return nil
}
return strongSelf.gridNode.scrollView
}
self.scrollingArea.setContentOffset = { [weak self] offset in
guard let strongSelf = self else {
return
}
// strongSelf.isFastScrolling = true
strongSelf.gridNode.scrollView.setContentOffset(offset, animated: false)
// strongSelf.isFastScrolling = false
}
if self.controller?.collection == nil {
let cameraView = TGAttachmentCameraView(forSelfPortrait: false)!
cameraView.clipsToBounds = true
@ -347,6 +395,44 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self.view.window?.endEditing(true)
}
private func scrollerTextForTag(tag: Int32) -> String {
let month = Month(packedValue: tag)
return stringForMonth(strings: self.presentationData.strings, month: month.month, ofYear: month.year)
}
private var currentScrollingTag: Int32?
private func updateScrollingArea() {
guard let (layout, _) = self.validLayout else {
return
}
var tag: Int32?
self.gridNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? MediaPickerGridItemNode {
tag = itemNode.tag
}
}
let dateString = tag.flatMap { self.scrollerTextForTag(tag: $0) }
if self.currentScrollingTag != tag {
self.currentScrollingTag = tag
if self.scrollingArea.isDragging {
self.scrollingArea.feedbackTap()
}
}
self.scrollingArea.update(
containerSize: layout.size,
containerInsets: self.gridNode.gridLayout.insets,
contentHeight: self.gridNode.scrollView.contentSize.height,
contentOffset: self.gridNode.scrollView.bounds.minY,
isScrolling: self.gridNode.scrollView.isDragging || self.gridNode.scrollView.isDecelerating,
date: (dateString ?? "", tag ?? 0),
theme: self.presentationData.theme,
transition: .immediate
)
}
private func updateState(_ state: State) {
guard let controller = self.controller, let interaction = controller.interaction else {
return
@ -442,7 +528,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
}
private var currentDisplayMode: DisplayMode = .all
func updateMode(_ displayMode: DisplayMode) {
func updateDisplayMode(_ displayMode: DisplayMode) {
let updated = self.currentDisplayMode != displayMode
self.currentDisplayMode = displayMode
@ -452,6 +538,19 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
selectionNode.alpha = 0.0
selectionNode.isUserInteractionEnabled = false
selectionNode.interaction = self.controller?.interaction
selectionNode.getTransitionView = { [weak self] identifier in
if let strongSelf = self {
var view: UIView?
strongSelf.gridNode.forEachItemNode { itemNode in
if let itemNode = itemNode as? MediaPickerGridItemNode, itemNode.asset?.localIdentifier == identifier {
view = itemNode.view
}
}
return view
} else {
return nil
}
}
self.containerNode.insertSubnode(selectionNode, aboveSubnode: self.gridNode)
self.selectionNode = selectionNode
@ -460,25 +559,26 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
}
}
let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
self.gridNode.isUserInteractionEnabled = displayMode == .all
self.selectionNode?.isUserInteractionEnabled = displayMode == .selected
var completion: () -> Void = {}
if updated && displayMode == .all {
completion = {
self.updateNavigation(transition: .animated(duration: 0.1, curve: .easeInOut))
self.selectionNode?.alpha = 0.0
}
}
if let selectionNode = self.selectionNode {
transition.updateAlpha(node: selectionNode, alpha: displayMode == .selected ? 1.0 : 0.0, completion: { _ in
completion()
})
selectionNode.isUserInteractionEnabled = displayMode == .selected
}
if updated && displayMode == .selected {
self.updateNavigation(transition: .immediate)
if updated {
switch displayMode {
case .selected:
self.selectionNode?.alpha = 1.0
self.selectionNode?.animateIn(completion: completion)
self.updateNavigation(transition: .immediate)
case .all:
self.selectionNode?.animateOut(completion: completion)
}
}
}
@ -570,7 +670,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
}
let proceed: (Bool) -> Void = { convertToJpeg in
guard let signals = TGMediaAssetsController.resultSignals(for: self.controller?.interaction?.selectionState, editingContext: self.controller?.interaction?.editingState, intent: asFile ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent, currentItem: nil, storeAssets: true, convertToJpeg: false, descriptionGenerator: legacyAssetPickerItemGenerator(), saveEditedPhotos: true) else {
guard let signals = TGMediaAssetsController.resultSignals(for: self.controller?.interaction?.selectionState, editingContext: self.controller?.interaction?.editingState, intent: asFile ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent, currentItem: nil, storeAssets: true, convertToJpeg: convertToJpeg, descriptionGenerator: legacyAssetPickerItemGenerator(), saveEditedPhotos: true) else {
return
}
self.controller?.legacyCompletion(signals, silently, scheduleTime)
@ -804,6 +904,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
let cleanGridInsets = UIEdgeInsets(top: insets.top, left: layout.safeInsets.left, bottom: layout.intrinsicInsets.bottom, right: layout.safeInsets.right)
let gridInsets = UIEdgeInsets(top: insets.top + manageHeight, left: layout.safeInsets.left, bottom: layout.intrinsicInsets.bottom, right: layout.safeInsets.right)
transition.updateFrame(node: self.gridNode, frame: innerBounds)
self.scrollingArea.frame = innerBounds
transition.updateFrame(node: self.backgroundNode, frame: innerBounds)
self.backgroundNode.update(size: bounds.size, transition: transition)
@ -834,7 +935,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
}
if selectedItems.count < 1 && self.currentDisplayMode == .selected {
self.updateMode(.all)
self.updateDisplayMode(.all)
Queue.mainQueue().after(0.3, updateSelectionNode)
} else {
updateSelectionNode()
@ -949,7 +1050,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self.titleView.indexUpdated = { [weak self] index in
if let strongSelf = self {
strongSelf.controllerNode.updateMode(index == 0 ? .all : .selected)
strongSelf.controllerNode.updateDisplayMode(index == 0 ? .all : .selected)
}
}
@ -994,9 +1095,23 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
self?.controllerNode.openMedia(fetchResult: fetchResult, index: index, immediateThumbnail: immediateThumbnail)
}, openSelectedMedia: { [weak self] item, immediateThumbnail in
self?.controllerNode.openSelectedMedia(item: item, immediateThumbnail: immediateThumbnail)
}, toggleSelection: { [weak self] item, value in
if let strongSelf = self {
strongSelf.interaction?.selectionState?.setItem(item, selected: value)
}, toggleSelection: { [weak self] item, value, suggestUndo in
if let strongSelf = self, let selectionState = strongSelf.interaction?.selectionState {
var showUndo = false
if suggestUndo {
if !value {
selectionState.saveState()
showUndo = true
} else {
selectionState.clearSavedState()
}
}
selectionState.setItem(item, selected: value)
if showUndo {
strongSelf.showSelectionUndo(item: item, count: Int32(selectionState.savedStateDifference()))
}
}
}, sendSelected: { [weak self] currentItem, silently, scheduleTime, animated in
if let strongSelf = self, let selectionState = strongSelf.interaction?.selectionState {
@ -1037,6 +1152,51 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
super.displayNodeDidLoad()
}
private weak var undoController: UndoOverlayController?
private func showSelectionUndo(item: TGMediaSelectableItem, count: Int32) {
var asset: PHAsset?
if let item = item as? TGMediaAsset {
asset = item.backingAsset
} else if let item = item as? TGCameraCapturedVideo {
asset = item.originalAsset.backingAsset
}
guard let asset = asset else {
return
}
let scale = min(2.0, UIScreenScale)
let targetSize = CGSize(width: 64.0 * scale, height: 64.0 * scale)
let _ = (assetImage(asset: asset, targetSize: targetSize, exact: false)
|> deliverOnMainQueue).start(next: { [weak self] image in
guard let strongSelf = self else {
return
}
let presentationData = strongSelf.presentationData
if let undoController = strongSelf.undoController {
let text = presentationData.strings.Attachment_DeselectedItems(count)
undoController.content = .image(image: image ?? UIImage(), text: text)
} else {
let text = presentationData.strings.Attachment_DeselectedItems(count)
let undoController = UndoOverlayController(presentationData: presentationData, content: .image(image: image ?? UIImage(), text: text), elevatedLayout: true, action: { [weak self] action in
guard let strongSelf = self else {
return true
}
switch action {
case .undo:
strongSelf.interaction?.selectionState?.restoreState()
default:
strongSelf.interaction?.selectionState?.clearSavedState()
}
return true
})
strongSelf.present(undoController, in: .window(.root))
strongSelf.undoController = undoController
}
})
}
private var selectionCount: Int32 = 0
fileprivate func updateSelectionState(count: Int32) {
self.selectionCount = count

View File

@ -118,7 +118,7 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
let checkNode = InteractiveCheckNode(theme: CheckNodeTheme(theme: theme, style: .overlay))
checkNode.valueChanged = { [weak self] value in
if let strongSelf = self, let interaction = strongSelf.interaction {
interaction.toggleSelection(strongSelf.asset, value)
interaction.toggleSelection(strongSelf.asset, value, true)
}
}
self.addSubnode(checkNode)
@ -189,6 +189,41 @@ private class MediaPickerSelectedItemNode: ASDisplayNode {
view.frame = self.convert(self.bounds, to: nil)
return view
}
func animateFrom(_ view: UIView) {
view.alpha = 0.0
let frame = view.convert(view.bounds, to: self.supernode?.view)
let targetFrame = self.frame
self.updateLayout(size: frame.size, transition: .immediate)
self.updateLayout(size: targetFrame.size, transition: .animated(duration: 0.25, curve: .spring))
self.layer.animateFrame(from: frame, to: targetFrame, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak view] _ in
view?.alpha = 1.0
})
}
func animateTo(_ view: UIView) {
view.alpha = 0.0
let frame = self.frame
let targetFrame = view.convert(view.bounds, to: self.supernode?.view)
let corners = self.corners
self.corners = []
self.updateLayout(size: targetFrame.size, transition: .animated(duration: 0.25, curve: .spring))
self.layer.animateFrame(from: frame, to: targetFrame, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { [weak view, weak self] _ in
view?.alpha = 1.0
self?.corners = corners
self?.updateLayout(size: frame.size, transition: .immediate)
Queue.mainQueue().after(0.01) {
self?.layer.removeAllAnimations()
}
})
}
}
private class MessageBackgroundNode: ASDisplayNode {
@ -288,7 +323,7 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
}
return (false, false, nil)
}, willBegin: { _ in
// self?.willBeginReorder(point)
}, began: { [weak self] itemNode in
self?.beginReordering(itemNode: itemNode)
}, ended: { [weak self] point in
@ -309,6 +344,74 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
})
}
var getTransitionView: (String) -> UIView? = { _ in return nil }
func animateIn(completion: @escaping () -> Void = {}) {
self.wallpaperBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, completion: { _ in
completion()
})
self.wallpaperBackgroundNode.layer.animateScale(from: 1.2, to: 1.0, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
for (_, backgroundNode) in self.backgroundNodes {
backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, delay: 0.1)
}
for (identifier, itemNode) in self.itemNodes {
if let transitionView = self.getTransitionView(identifier) {
itemNode.animateFrom(transitionView)
} else {
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
}
}
if let topNode = self.messageNodes?.first, !topNode.alpha.isZero {
topNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, delay: 0.1)
topNode.layer.animatePosition(from: CGPoint(x: 0.0, y: -30.0), to: CGPoint(), duration: 0.4, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
}
if let bottomNode = self.messageNodes?.last, !bottomNode.alpha.isZero {
bottomNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, delay: 0.1)
bottomNode.layer.animatePosition(from: CGPoint(x: 0.0, y: 30.0), to: CGPoint(), duration: 0.4, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
}
}
func animateOut(completion: @escaping () -> Void = {}) {
self.wallpaperBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { [weak self] _ in
completion()
if let strongSelf = self {
Queue.mainQueue().after(0.01) {
for (_, backgroundNode) in strongSelf.backgroundNodes {
backgroundNode.layer.removeAllAnimations()
}
strongSelf.messageNodes?.first?.layer.removeAllAnimations()
strongSelf.messageNodes?.last?.layer.removeAllAnimations()
strongSelf.wallpaperBackgroundNode.layer.removeAllAnimations()
}
}
})
self.wallpaperBackgroundNode.layer.animateScale(from: 1.0, to: 1.2, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
for (_, backgroundNode) in self.backgroundNodes {
backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false)
}
for (identifier, itemNode) in self.itemNodes {
if let transitionView = self.getTransitionView(identifier) {
itemNode.animateTo(transitionView)
}
}
self.messageNodes?.first?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
self.messageNodes?.first?.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -30.0), duration: 0.4, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
self.messageNodes?.last?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
self.messageNodes?.last?.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: 30.0), duration: 0.4, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
@ -556,9 +659,14 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
self.backgroundNodes[groupIndex] = groupBackgroundNode
self.scrollNode.insertSubnode(groupBackgroundNode, at: 0)
}
var itemTransition = transition
if groupBackgroundNode.frame.width.isZero {
itemTransition = .immediate
}
transition.updateFrame(node: groupBackgroundNode, frame: groupRect.insetBy(dx: -5.0, dy: -2.0).offsetBy(dx: 3.0, dy: 0.0))
groupBackgroundNode.update(size: groupBackgroundNode.frame.size, theme: theme, wallpaper: wallpaper, graphics: graphics, wallpaperBackgroundNode: self.wallpaperBackgroundNode, transition: transition)
itemTransition.updateFrame(node: groupBackgroundNode, frame: groupRect.insetBy(dx: -5.0, dy: -2.0).offsetBy(dx: 3.0, dy: 0.0))
groupBackgroundNode.update(size: groupBackgroundNode.frame.size, theme: theme, wallpaper: wallpaper, graphics: graphics, wallpaperBackgroundNode: self.wallpaperBackgroundNode, transition: itemTransition)
for (item, itemRect, itemPosition) in items {
if let identifier = item.uniqueIdentifier, let itemNode = self.itemNodes[identifier] {
@ -578,8 +686,18 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI
itemNode.corners = corners
itemNode.radius = bubbleCorners.mainRadius
itemNode.updateLayout(size: itemRect.size, transition: transition)
transition.updateFrame(node: itemNode, frame: itemRect.offsetBy(dx: groupRect.minX, dy: groupRect.minY))
var itemTransition = itemTransition
if itemNode.frame.width.isZero {
itemTransition = .immediate
}
itemNode.updateLayout(size: itemRect.size, transition: itemTransition)
itemTransition.updateFrame(node: itemNode, frame: itemRect.offsetBy(dx: groupRect.minX, dy: groupRect.minY))
if case .immediate = itemTransition, case .animated = transition {
transition.animateTransformScale(node: itemNode, from: 0.01)
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
}