Various improvements

This commit is contained in:
Ilya Laktyushin
2025-04-03 17:06:20 +04:00
parent 81d23edd72
commit c5168b8905
38 changed files with 1730 additions and 154 deletions

View File

@@ -1668,15 +1668,18 @@ public class CameraScreenImpl: ViewController, CameraScreen {
public weak var sourceView: UIView?
public let sourceRect: CGRect
public let sourceCornerRadius: CGFloat
public let useFillAnimation: Bool
public init(
sourceView: UIView,
sourceRect: CGRect,
sourceCornerRadius: CGFloat
sourceCornerRadius: CGFloat,
useFillAnimation: Bool
) {
self.sourceView = sourceView
self.sourceRect = sourceRect
self.sourceCornerRadius = sourceCornerRadius
self.useFillAnimation = useFillAnimation
}
}
@@ -2505,55 +2508,100 @@ public class CameraScreenImpl: ViewController, CameraScreen {
if let transitionIn = self.controller?.transitionIn, let sourceView = transitionIn.sourceView {
let sourceLocalFrame = sourceView.convert(transitionIn.sourceRect, to: self.view)
if case .story = controller.mode {
let sourceScale = sourceLocalFrame.width / self.previewContainerView.frame.width
if transitionIn.useFillAnimation {
self.backgroundView.alpha = 1.0
self.backgroundView.layer.removeAllAnimations()
self.previewContainerView.layer.animatePosition(from: sourceLocalFrame.center, to: self.previewContainerView.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
self.requestUpdateLayout(hasAppeared: true, transition: .immediate)
})
self.previewContainerView.layer.animateScale(from: sourceScale, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
self.transitionDimView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, delay: 0.15)
let minSide = min(self.previewContainerView.bounds.width, self.previewContainerView.bounds.height)
self.previewContainerView.layer.animateBounds(from: CGRect(origin: CGPoint(x: (self.previewContainerView.bounds.width - minSide) / 2.0, y: (self.previewContainerView.bounds.height - minSide) / 2.0), size: CGSize(width: minSide, height: minSide)), to: self.previewContainerView.bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
self.previewContainerView.layer.animate(
from: minSide / 2.0 as NSNumber,
to: self.previewContainerView.layer.cornerRadius as NSNumber,
keyPath: "cornerRadius",
timingFunction: kCAMediaTimingFunctionSpring,
duration: 0.3
)
} else {
self.mainPreviewAnimationWrapperView.bounds = self.mainPreviewView.bounds
self.mainPreviewAnimationWrapperView.center = CGPoint(x: self.previewContainerView.frame.width / 2.0, y: self.previewContainerView.frame.height / 2.0)
let transitionMaskView = UIView()
transitionMaskView.frame = self.view.bounds
self.view.mask = transitionMaskView
self.mainPreviewView.layer.position = CGPoint(x: self.previewContainerView.frame.width / 2.0, y: self.previewContainerView.frame.height / 2.0)
let transitionCircleLayer = SimpleShapeLayer()
transitionCircleLayer.path = CGPath(ellipseIn: CGRect(origin: .zero, size: CGSize(width: 320.0, height: 320.0)), transform: nil)
transitionCircleLayer.fillColor = UIColor.white.cgColor
transitionCircleLayer.frame = CGSize(width: 320.0, height: 320.0).centered(in: sourceLocalFrame)
transitionMaskView.layer.addSublayer(transitionCircleLayer)
let sourceInnerFrame = sourceView.convert(transitionIn.sourceRect, to: self.previewContainerView)
let sourceCenter = sourceInnerFrame.center
self.mainPreviewAnimationWrapperView.layer.animatePosition(from: sourceCenter, to: self.mainPreviewAnimationWrapperView.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
self.requestUpdateLayout(hasAppeared: true, transition: .immediate)
})
let colorFillView = UIView()
colorFillView.backgroundColor = self.presentationData.theme.list.itemCheckColors.fillColor
colorFillView.frame = self.view.bounds
colorFillView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
self.view.addSubview(colorFillView)
var sourceBounds = self.mainPreviewView.bounds
if let holder = controller.holder {
sourceBounds = CGRect(origin: .zero, size: holder.parentView.frame.size.aspectFitted(sourceBounds.size))
let iconLayer = SimpleLayer()
iconLayer.contents = generateAddIcon(color: self.presentationData.theme.list.itemCheckColors.foregroundColor)?.cgImage
iconLayer.bounds = CGRect(origin: .zero, size: CGSize(width: 30.0, height: 30.0))
iconLayer.position = sourceLocalFrame.center
colorFillView.layer.addSublayer(iconLayer)
let labelLayer = SimpleLayer()
if let image = generateAddLabel(strings: self.presentationData.strings, color: self.presentationData.theme.list.itemCheckColors.foregroundColor) {
labelLayer.contents = image.cgImage
labelLayer.bounds = CGRect(origin: .zero, size: image.size)
labelLayer.position = CGPoint(x: sourceLocalFrame.center.x, y: sourceLocalFrame.center.y + 43.0 - UIScreenPixel)
colorFillView.layer.addSublayer(labelLayer)
}
self.mainPreviewAnimationWrapperView.layer.animateBounds(from: sourceBounds, to: self.mainPreviewView.bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
let sourceScale = max(sourceInnerFrame.width / self.previewContainerView.frame.width, sourceInnerFrame.height / self.previewContainerView.frame.height)
self.mainPreviewView.transform = CGAffineTransform.identity
self.mainPreviewAnimationWrapperView.layer.animateScale(from: sourceScale, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
self.mainPreviewContainerView.addSubview(self.mainPreviewView)
Queue.mainQueue().justDispatch {
self.animatedIn = true
}
iconLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, delay: 0.1, removeOnCompletion: false)
labelLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, delay: 0.1, removeOnCompletion: false)
transitionCircleLayer.animateScale(from: sourceLocalFrame.width / 320.0, to: 6.0, duration: 0.6, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
self.view.mask = nil
colorFillView.removeFromSuperview()
})
}
if let view = self.componentHost.view {
view.layer.animatePosition(from: sourceLocalFrame.center, to: view.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
view.layer.animateScale(from: 0.1, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
} else {
if case .story = controller.mode {
let sourceScale = sourceLocalFrame.width / self.previewContainerView.frame.width
self.previewContainerView.layer.animatePosition(from: sourceLocalFrame.center, to: self.previewContainerView.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
self.requestUpdateLayout(hasAppeared: true, transition: .immediate)
})
self.previewContainerView.layer.animateScale(from: sourceScale, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
let minSide = min(self.previewContainerView.bounds.width, self.previewContainerView.bounds.height)
self.previewContainerView.layer.animateBounds(from: CGRect(origin: CGPoint(x: (self.previewContainerView.bounds.width - minSide) / 2.0, y: (self.previewContainerView.bounds.height - minSide) / 2.0), size: CGSize(width: minSide, height: minSide)), to: self.previewContainerView.bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
self.previewContainerView.layer.animate(
from: minSide / 2.0 as NSNumber,
to: self.previewContainerView.layer.cornerRadius as NSNumber,
keyPath: "cornerRadius",
timingFunction: kCAMediaTimingFunctionSpring,
duration: 0.3
)
} else {
self.mainPreviewAnimationWrapperView.bounds = self.mainPreviewView.bounds
self.mainPreviewAnimationWrapperView.center = CGPoint(x: self.previewContainerView.frame.width / 2.0, y: self.previewContainerView.frame.height / 2.0)
self.mainPreviewView.layer.position = CGPoint(x: self.previewContainerView.frame.width / 2.0, y: self.previewContainerView.frame.height / 2.0)
let sourceInnerFrame = sourceView.convert(transitionIn.sourceRect, to: self.previewContainerView)
let sourceCenter = sourceInnerFrame.center
self.mainPreviewAnimationWrapperView.layer.animatePosition(from: sourceCenter, to: self.mainPreviewAnimationWrapperView.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
self.requestUpdateLayout(hasAppeared: true, transition: .immediate)
})
var sourceBounds = self.mainPreviewView.bounds
if let holder = controller.holder {
sourceBounds = CGRect(origin: .zero, size: holder.parentView.frame.size.aspectFitted(sourceBounds.size))
}
self.mainPreviewAnimationWrapperView.layer.animateBounds(from: sourceBounds, to: self.mainPreviewView.bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
let sourceScale = max(sourceInnerFrame.width / self.previewContainerView.frame.width, sourceInnerFrame.height / self.previewContainerView.frame.height)
self.mainPreviewView.transform = CGAffineTransform.identity
self.mainPreviewAnimationWrapperView.layer.animateScale(from: sourceScale, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
self.mainPreviewContainerView.addSubview(self.mainPreviewView)
Queue.mainQueue().justDispatch {
self.animatedIn = true
}
})
}
if let view = self.componentHost.view {
view.layer.animatePosition(from: sourceLocalFrame.center, to: view.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
view.layer.animateScale(from: 0.1, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
}
}
}
@@ -3767,6 +3815,10 @@ public class CameraScreenImpl: ViewController, CameraScreen {
self.dismiss(animated: false)
}
}
public func animateIn() {
self.node.animateIn()
}
public func updateTransitionProgress(_ transitionFraction: CGFloat, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void = {}) {
if let layout = self.validLayout, layout.metrics.isTablet {
@@ -4009,3 +4061,37 @@ private func pipPositionForLocation(layout: ContainerViewLayout, position: CGPoi
return position
}
private func generateAddIcon(color: UIColor) -> UIImage? {
return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in
context.clear(CGRect(origin: .zero, size: size))
context.setStrokeColor(color.cgColor)
context.setLineWidth(3.0)
context.setLineCap(.round)
context.move(to: CGPoint(x: 15.0, y: 5.5))
context.addLine(to: CGPoint(x: 15.0, y: 24.5))
context.strokePath()
context.move(to: CGPoint(x: 5.5, y: 15.0))
context.addLine(to: CGPoint(x: 24.5, y: 15.0))
context.strokePath()
})
}
private func generateAddLabel(strings: PresentationStrings, color: UIColor) -> UIImage? {
let titleString = NSAttributedString(string: strings.StoryFeed_AddStory, font: Font.regular(11.0), textColor: color, paragraphAlignment: .center)
var textRect = titleString.boundingRect(with: CGSize(width: 200.0, height: 20.0), options: .usesLineFragmentOrigin, context: nil)
textRect.size.width = ceil(textRect.size.width)
textRect.size.height = ceil(textRect.size.height)
return generateImage(textRect.size, rotatedContext: { size, context in
let bounds = CGRect(origin: CGPoint(), size: size)
context.clear(bounds)
UIGraphicsPushContext(context)
titleString.draw(in: textRect)
UIGraphicsPopContext()
})
}