mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-16 19:30:29 +00:00
Camera and editor improvements
This commit is contained in:
parent
3c39e2c0ce
commit
ae32ccca68
@ -223,6 +223,10 @@ private final class CameraContext {
|
||||
self.device.setZoomLevel(zoomLevel)
|
||||
}
|
||||
|
||||
func setZoomDelta(_ zoomDelta: CGFloat) {
|
||||
self.device.setZoomDelta(zoomDelta)
|
||||
}
|
||||
|
||||
func takePhoto() -> Signal<PhotoCaptureResult, NoError> {
|
||||
return self.output.takePhoto(orientation: self.videoOrientation ?? .portrait, flashMode: self._flashMode)
|
||||
}
|
||||
@ -414,6 +418,15 @@ public final class Camera {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func setZoomDelta(_ zoomDelta: CGFloat) {
|
||||
self.queue.async {
|
||||
if let context = self.contextRef?.takeUnretainedValue() {
|
||||
context.setZoomDelta(zoomDelta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func setTorchActive(_ active: Bool) {
|
||||
self.queue.async {
|
||||
if let context = self.contextRef?.takeUnretainedValue() {
|
||||
|
||||
@ -220,4 +220,13 @@ final class CameraDevice {
|
||||
device.videoZoomFactor = max(1.0, min(10.0, zoomLevel))
|
||||
}
|
||||
}
|
||||
|
||||
func setZoomDelta(_ zoomDelta: CGFloat) {
|
||||
guard let device = self.videoDevice else {
|
||||
return
|
||||
}
|
||||
self.transaction(device) { device in
|
||||
device.videoZoomFactor = max(1.0, min(10.0, device.videoZoomFactor * zoomDelta))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -873,11 +873,10 @@ public class CameraScreen: ViewController {
|
||||
|
||||
@objc private func handlePinch(_ gestureRecognizer: UIPinchGestureRecognizer) {
|
||||
switch gestureRecognizer.state {
|
||||
case .began:
|
||||
gestureRecognizer.scale = 1.0
|
||||
case .changed:
|
||||
let scale = gestureRecognizer.scale
|
||||
self.camera.setZoomLevel(scale)
|
||||
self.camera.setZoomDelta(scale)
|
||||
gestureRecognizer.scale = 1.0
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
@ -387,6 +387,7 @@ public final class MediaEditor {
|
||||
self.setGradientColors([topColor, bottomColor])
|
||||
|
||||
if player == nil {
|
||||
self.updateRenderChain()
|
||||
self.maybeGeneratePersonSegmentation(image)
|
||||
}
|
||||
|
||||
@ -425,10 +426,20 @@ public final class MediaEditor {
|
||||
}
|
||||
|
||||
private var skipRendering = false
|
||||
private func updateValues(skipRendering: Bool = false, _ f: (MediaEditorValues) -> MediaEditorValues) {
|
||||
if skipRendering {
|
||||
self.skipRendering = true
|
||||
}
|
||||
self.values = f(self.values)
|
||||
if skipRendering {
|
||||
self.skipRendering = false
|
||||
}
|
||||
}
|
||||
|
||||
public func setCrop(offset: CGPoint, scale: CGFloat, rotation: CGFloat, mirroring: Bool) {
|
||||
self.skipRendering = true
|
||||
self.values = self.values.withUpdatedCrop(offset: offset, scale: scale, rotation: rotation, mirroring: mirroring)
|
||||
self.skipRendering = false
|
||||
self.updateValues(skipRendering: true) { values in
|
||||
return values.withUpdatedCrop(offset: offset, scale: scale, rotation: rotation, mirroring: mirroring)
|
||||
}
|
||||
}
|
||||
|
||||
public func getToolValue(_ key: EditorToolKey) -> Any? {
|
||||
@ -436,19 +447,24 @@ public final class MediaEditor {
|
||||
}
|
||||
|
||||
public func setToolValue(_ key: EditorToolKey, value: Any) {
|
||||
var updatedToolValues = self.values.toolValues
|
||||
updatedToolValues[key] = value
|
||||
self.values = self.values.withUpdatedToolValues(updatedToolValues)
|
||||
self.updateRenderChain()
|
||||
self.updateValues { values in
|
||||
var updatedToolValues = values.toolValues
|
||||
updatedToolValues[key] = value
|
||||
return values.withUpdatedToolValues(updatedToolValues)
|
||||
}
|
||||
}
|
||||
|
||||
public func setVideoIsMuted(_ videoIsMuted: Bool) {
|
||||
self.player?.isMuted = videoIsMuted
|
||||
self.values = self.values.withUpdatedVideoIsMuted(videoIsMuted)
|
||||
self.updateValues(skipRendering: true) { values in
|
||||
return values.withUpdatedVideoIsMuted(videoIsMuted)
|
||||
}
|
||||
}
|
||||
|
||||
public func setVideoIsFullHd(_ videoIsFullHd: Bool) {
|
||||
self.values = self.values.withUpdatedVideoIsFullHd(videoIsFullHd)
|
||||
self.updateValues(skipRendering: true) { values in
|
||||
return values.withUpdatedVideoIsFullHd(videoIsFullHd)
|
||||
}
|
||||
}
|
||||
|
||||
private var targetTimePosition: (CMTime, Bool)?
|
||||
@ -494,23 +510,29 @@ public final class MediaEditor {
|
||||
})
|
||||
}
|
||||
|
||||
public func setVideoTrimRange(_ trimRange: Range<Double>) {
|
||||
self.skipRendering = true
|
||||
self.values = self.values.withUpdatedVideoTrimRange(trimRange)
|
||||
self.skipRendering = false
|
||||
public func setVideoTrimRange(_ trimRange: Range<Double>, apply: Bool) {
|
||||
self.updateValues(skipRendering: true) { values in
|
||||
return values.withUpdatedVideoTrimRange(trimRange)
|
||||
}
|
||||
|
||||
self.player?.currentItem?.forwardPlaybackEndTime = CMTime(seconds: trimRange.upperBound, preferredTimescale: CMTimeScale(1000))
|
||||
if apply {
|
||||
self.player?.currentItem?.forwardPlaybackEndTime = CMTime(seconds: trimRange.upperBound, preferredTimescale: CMTimeScale(1000))
|
||||
}
|
||||
}
|
||||
|
||||
public func setDrawingAndEntities(data: Data?, image: UIImage?, entities: [CodableDrawingEntity]) {
|
||||
self.values = self.values.withUpdatedDrawingAndEntities(drawing: image, entities: entities)
|
||||
self.updateValues(skipRendering: true) { values in
|
||||
return values.withUpdatedDrawingAndEntities(drawing: image, entities: entities)
|
||||
}
|
||||
}
|
||||
|
||||
public func setGradientColors(_ gradientColors: [UIColor]) {
|
||||
self.values = self.values.withUpdatedGradientColors(gradientColors: gradientColors)
|
||||
self.updateValues(skipRendering: true) { values in
|
||||
return values.withUpdatedGradientColors(gradientColors: gradientColors)
|
||||
}
|
||||
}
|
||||
|
||||
private var previousUpdateTime = CACurrentMediaTime()
|
||||
private var previousUpdateTime: Double?
|
||||
private var scheduledUpdate = false
|
||||
private func updateRenderChain() {
|
||||
self.renderChain.update(values: self.values)
|
||||
@ -519,10 +541,9 @@ public final class MediaEditor {
|
||||
let currentTime = CACurrentMediaTime()
|
||||
if !self.scheduledUpdate {
|
||||
let delay = 0.03333
|
||||
let delta = currentTime - self.previousUpdateTime
|
||||
if delta < delay {
|
||||
if let previousUpdateTime = self.previousUpdateTime, currentTime - previousUpdateTime < delay {
|
||||
self.scheduledUpdate = true
|
||||
Queue.mainQueue().after(delay - delta) {
|
||||
Queue.mainQueue().after(delay - (currentTime - previousUpdateTime)) {
|
||||
self.scheduledUpdate = false
|
||||
self.previousUpdateTime = CACurrentMediaTime()
|
||||
self.renderer.willRenderFrame()
|
||||
|
||||
@ -200,6 +200,8 @@ final class MediaEditorScreenComponent: Component {
|
||||
private let textDoneButton = ComponentView<Empty>()
|
||||
private let textSize = ComponentView<Empty>()
|
||||
|
||||
private var isDismissed = false
|
||||
|
||||
private var component: MediaEditorScreenComponent?
|
||||
private weak var state: State?
|
||||
private var environment: ViewControllerComponentContainer.Environment?
|
||||
@ -274,6 +276,7 @@ final class MediaEditorScreenComponent: Component {
|
||||
}
|
||||
|
||||
func animateOut(to source: TransitionAnimationSource) {
|
||||
self.isDismissed = true
|
||||
let transition = Transition(animation: .curve(duration: 0.2, curve: .easeInOut))
|
||||
if let view = self.cancelButton.view {
|
||||
transition.setAlpha(view: view, alpha: 0.0)
|
||||
@ -398,6 +401,9 @@ final class MediaEditorScreenComponent: Component {
|
||||
}
|
||||
|
||||
func update(component: MediaEditorScreenComponent, availableSize: CGSize, state: State, environment: Environment<ViewControllerComponentContainer.Environment>, transition: Transition) -> CGSize {
|
||||
guard !self.isDismissed else {
|
||||
return availableSize
|
||||
}
|
||||
let environment = environment[ViewControllerComponentContainer.Environment.self].value
|
||||
self.environment = environment
|
||||
|
||||
@ -612,10 +618,9 @@ final class MediaEditorScreenComponent: Component {
|
||||
framesUpdateTimestamp: playerState.framesUpdateTimestamp,
|
||||
trimUpdated: { [weak mediaEditor] start, end, updatedEnd, done in
|
||||
if let mediaEditor {
|
||||
mediaEditor.setVideoTrimRange(start..<end, apply: done)
|
||||
if done {
|
||||
mediaEditor.seek(start, andPlay: true)
|
||||
|
||||
mediaEditor.setVideoTrimRange(start..<end)
|
||||
} else {
|
||||
mediaEditor.seek(updatedEnd ? end : start, andPlay: false)
|
||||
}
|
||||
@ -1106,9 +1111,10 @@ public final class MediaEditorScreen: ViewController {
|
||||
|
||||
private var isDisplayingTool = false
|
||||
private var isInteractingWithEntities = false
|
||||
private var isEnhacing = false
|
||||
private var isDismissing = false
|
||||
private var dismissOffset: CGFloat = 0.0
|
||||
private var isEnhacing = false
|
||||
private var isDismissed = false
|
||||
|
||||
private var presentationData: PresentationData
|
||||
private var validLayout: ContainerViewLayout?
|
||||
@ -1259,22 +1265,6 @@ public final class MediaEditorScreen: ViewController {
|
||||
return
|
||||
}
|
||||
|
||||
var hasSwipeToDismiss = false
|
||||
if let subject = self.subject {
|
||||
if case .asset = subject {
|
||||
hasSwipeToDismiss = true
|
||||
} else if case .draft = subject {
|
||||
hasSwipeToDismiss = true
|
||||
}
|
||||
}
|
||||
if hasSwipeToDismiss {
|
||||
let dismissPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handleDismissPan(_:)))
|
||||
dismissPanGestureRecognizer.delegate = self
|
||||
dismissPanGestureRecognizer.maximumNumberOfTouches = 1
|
||||
self.previewContainerView.addGestureRecognizer(dismissPanGestureRecognizer)
|
||||
self.dismissPanGestureRecognizer = dismissPanGestureRecognizer
|
||||
}
|
||||
|
||||
let mediaDimensions = subject.dimensions
|
||||
let maxSide: CGFloat = 1920.0 / UIScreen.main.scale
|
||||
let fittedSize = mediaDimensions.cgSize.fitted(CGSize(width: maxSide, height: maxSide))
|
||||
@ -1346,6 +1336,12 @@ public final class MediaEditorScreen: ViewController {
|
||||
self.view.disablesInteractiveModalDismiss = true
|
||||
self.view.disablesInteractiveKeyboardGestureRecognizer = true
|
||||
|
||||
let dismissPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handleDismissPan(_:)))
|
||||
dismissPanGestureRecognizer.delegate = self
|
||||
dismissPanGestureRecognizer.maximumNumberOfTouches = 1
|
||||
self.previewContainerView.addGestureRecognizer(dismissPanGestureRecognizer)
|
||||
self.dismissPanGestureRecognizer = dismissPanGestureRecognizer
|
||||
|
||||
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(_:)))
|
||||
panGestureRecognizer.delegate = self
|
||||
panGestureRecognizer.minimumNumberOfTouches = 2
|
||||
@ -1429,11 +1425,20 @@ public final class MediaEditorScreen: ViewController {
|
||||
return
|
||||
}
|
||||
|
||||
var hasSwipeToDismiss = false
|
||||
if let subject = self.subject {
|
||||
if case .asset = subject {
|
||||
hasSwipeToDismiss = true
|
||||
} else if case .draft = subject {
|
||||
hasSwipeToDismiss = true
|
||||
}
|
||||
}
|
||||
|
||||
let translation = gestureRecognizer.translation(in: self.view)
|
||||
let velocity = gestureRecognizer.velocity(in: self.view)
|
||||
switch gestureRecognizer.state {
|
||||
case .changed:
|
||||
if abs(translation.y) > 10.0 && !self.isEnhacing {
|
||||
if abs(translation.y) > 10.0 && !self.isEnhacing && hasSwipeToDismiss {
|
||||
if !self.isDismissing {
|
||||
self.isDismissing = true
|
||||
controller.requestLayout(transition: .animated(duration: 0.25, curve: .easeInOut))
|
||||
@ -1449,7 +1454,7 @@ public final class MediaEditorScreen: ViewController {
|
||||
} else if self.isEnhacing {
|
||||
if let mediaEditor = self.mediaEditor {
|
||||
let value = mediaEditor.getToolValue(.enhance) as? Float ?? 0.0
|
||||
let delta = Float((translation.x / self.frame.width) * 2.0)
|
||||
let delta = Float((translation.x / self.frame.width) * 1.5)
|
||||
let updatedValue = max(0.0, min(1.0, value + delta))
|
||||
mediaEditor.setToolValue(.enhance, value: updatedValue)
|
||||
}
|
||||
@ -1573,6 +1578,7 @@ public final class MediaEditorScreen: ViewController {
|
||||
guard let controller = self.controller else {
|
||||
return
|
||||
}
|
||||
self.isDismissed = true
|
||||
controller.statusBar.statusBarStyle = .Ignore
|
||||
|
||||
let previousDimAlpha = self.backgroundDimView.alpha
|
||||
@ -1825,7 +1831,7 @@ public final class MediaEditorScreen: ViewController {
|
||||
|
||||
private var drawingScreen: DrawingScreen?
|
||||
func containerLayoutUpdated(layout: ContainerViewLayout, forceUpdate: Bool = false, animateOut: Bool = false, transition: Transition) {
|
||||
guard let controller = self.controller else {
|
||||
guard let controller = self.controller, !self.isDismissed else {
|
||||
return
|
||||
}
|
||||
let isFirstTime = self.validLayout == nil
|
||||
@ -2800,6 +2806,8 @@ private final class ToolValueComponent: Component {
|
||||
private let title = ComponentView<Empty>()
|
||||
private let value = ComponentView<Empty>()
|
||||
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
|
||||
private var component: ToolValueComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
@ -2814,6 +2822,8 @@ private final class ToolValueComponent: Component {
|
||||
}
|
||||
|
||||
func update(component: ToolValueComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
|
||||
let previousValue = self.component?.value
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
@ -2860,6 +2870,10 @@ private final class ToolValueComponent: Component {
|
||||
transition.setPosition(view: valueView, position: valueFrame.center)
|
||||
transition.setBounds(view: valueView, bounds: CGRect(origin: .zero, size: valueFrame.size))
|
||||
}
|
||||
|
||||
if let previousValue, component.value != previousValue, self.alpha > 0.0 {
|
||||
self.hapticFeedback.impact(.click05)
|
||||
}
|
||||
|
||||
return availableSize
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user