ios_client
This commit is contained in:
113
ios_client 0.8.5/Kecalek/Views/Chat/ImageViewerView.swift
Normal file
113
ios_client 0.8.5/Kecalek/Views/Chat/ImageViewerView.swift
Normal file
@@ -0,0 +1,113 @@
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import Photos
|
||||
|
||||
struct ImageViewerView: View {
|
||||
let imageData: Data
|
||||
@State private var scale: CGFloat = 1.0
|
||||
@State private var saved = false
|
||||
@State private var saveError: String?
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
GeometryReader { geo in
|
||||
if let uiImage = UIImage(data: imageData) {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.scaleEffect(scale)
|
||||
.gesture(
|
||||
MagnifyGesture()
|
||||
.onChanged { value in
|
||||
scale = value.magnification
|
||||
}
|
||||
.onEnded { _ in
|
||||
withAnimation {
|
||||
scale = max(1.0, min(scale, 5.0))
|
||||
}
|
||||
}
|
||||
)
|
||||
.onTapGesture(count: 2) {
|
||||
withAnimation {
|
||||
scale = scale > 1 ? 1 : 2
|
||||
}
|
||||
}
|
||||
.frame(width: geo.size.width, height: geo.size.height)
|
||||
}
|
||||
}
|
||||
.overlay(alignment: .bottom) {
|
||||
if let error = saveError {
|
||||
Text(error)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.white)
|
||||
.padding(8)
|
||||
.background(Capsule().fill(.red.opacity(0.8)))
|
||||
.padding(.bottom, 40)
|
||||
.transition(.move(edge: .bottom).combined(with: .opacity))
|
||||
}
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
Button {
|
||||
dismiss()
|
||||
} label: {
|
||||
Image(systemName: "xmark")
|
||||
.foregroundStyle(.white)
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
HStack(spacing: 16) {
|
||||
// Share
|
||||
if let uiImage = UIImage(data: imageData) {
|
||||
ShareLink(item: Image(uiImage: uiImage), preview: SharePreview("Image", image: Image(uiImage: uiImage))) {
|
||||
Image(systemName: "square.and.arrow.up")
|
||||
.foregroundStyle(.white)
|
||||
}
|
||||
}
|
||||
|
||||
// Save to Photos
|
||||
Button {
|
||||
saveToPhotos()
|
||||
} label: {
|
||||
Image(systemName: saved ? "checkmark.circle.fill" : "arrow.down.to.line")
|
||||
.foregroundStyle(saved ? .green : .white)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.toolbarBackground(.hidden, for: .navigationBar)
|
||||
.background(.black)
|
||||
}
|
||||
}
|
||||
|
||||
private func saveToPhotos() {
|
||||
guard let uiImage = UIImage(data: imageData) else {
|
||||
withAnimation { saveError = "Invalid image data" }
|
||||
return
|
||||
}
|
||||
|
||||
PHPhotoLibrary.requestAuthorization(for: .addOnly) { status in
|
||||
DispatchQueue.main.async {
|
||||
switch status {
|
||||
case .authorized, .limited:
|
||||
PHPhotoLibrary.shared().performChanges {
|
||||
PHAssetChangeRequest.creationRequestForAsset(from: uiImage)
|
||||
} completionHandler: { success, error in
|
||||
DispatchQueue.main.async {
|
||||
if success {
|
||||
withAnimation { saved = true; saveError = nil }
|
||||
} else {
|
||||
withAnimation { saveError = error?.localizedDescription ?? "Save failed" }
|
||||
}
|
||||
}
|
||||
}
|
||||
case .denied, .restricted:
|
||||
withAnimation { saveError = "Photo library access denied. Check Settings." }
|
||||
default:
|
||||
withAnimation { saveError = "Photo library access required" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user