1+ //
2+ // Copyright (c) 2021 Muukii <
[email protected] >
3+ //
4+ // Permission is hereby granted, free of charge, to any person obtaining a copy
5+ // of this software and associated documentation files (the "Software"), to deal
6+ // in the Software without restriction, including without limitation the rights
7+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ // copies of the Software, and to permit persons to whom the Software is
9+ // furnished to do so, subject to the following conditions:
10+ //
11+ // The above copyright notice and this permission notice shall be included in
12+ // all copies or substantial portions of the Software.
13+ //
14+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ // THE SOFTWARE.
21+
22+ import SwiftUI
23+ import Combine
24+ #if !COCOAPODS
25+ import BrightroomEngine
26+ #endif
27+
28+ public struct ClassicImageEditView : View {
29+
30+ @StateObject private var viewModel : ClassicImageEditSwiftUIViewModel
31+
32+ public var onEndEditing : ( EditingStack ) -> Void = { _ in }
33+ public var onCancelEditing : ( ) -> Void = { }
34+
35+ public init (
36+ imageProvider: ImageProvider ,
37+ options: ClassicImageEditOptions = . default,
38+ localizedStrings: ClassicImageEditViewController . LocalizedStrings = . init( )
39+ ) {
40+ let editingStack = EditingStack ( imageProvider: imageProvider)
41+ self . _viewModel = StateObject ( wrappedValue: ClassicImageEditSwiftUIViewModel (
42+ editingStack: editingStack,
43+ options: options,
44+ localizedStrings: localizedStrings
45+ ) )
46+ }
47+
48+ public init (
49+ editingStack: EditingStack ,
50+ options: ClassicImageEditOptions = . default,
51+ localizedStrings: ClassicImageEditViewController . LocalizedStrings = . init( )
52+ ) {
53+ self . _viewModel = StateObject ( wrappedValue: ClassicImageEditSwiftUIViewModel (
54+ editingStack: editingStack,
55+ options: options,
56+ localizedStrings: localizedStrings
57+ ) )
58+ }
59+
60+ public var body : some View {
61+ NavigationView {
62+ VStack ( spacing: 0 ) {
63+ editArea
64+ . background ( Color . white)
65+
66+ controlArea
67+ . background ( Color ( viewModel. style. control. backgroundColor) )
68+ }
69+ . ignoresSafeArea ( . container, edges: . bottom)
70+ . navigationBarTitleDisplayMode ( . inline)
71+ . navigationTitle ( viewModel. title)
72+ . toolbar {
73+ toolbarContent
74+ }
75+ }
76+ . onAppear {
77+ viewModel. editingStack. start ( )
78+ }
79+ }
80+
81+ @ViewBuilder
82+ private var editArea : some View {
83+ GeometryReader { geometry in
84+ let squareSize = min ( geometry. size. width, geometry. size. height)
85+
86+ ZStack {
87+ switch viewModel. mode {
88+ case . crop:
89+ CropViewWrapper (
90+ editingStack: viewModel. editingStack,
91+ croppingAspectRatio: viewModel. options. croppingAspectRatio
92+ )
93+
94+ case . masking:
95+ ImagePreviewViewWrapper ( editingStack: viewModel. editingStack)
96+ BlurryMaskingViewWrapper (
97+ editingStack: viewModel. editingStack,
98+ brushSize: viewModel. maskingBrushSize,
99+ isBlurryImageViewHidden: false
100+ )
101+
102+ case . editing:
103+ ImagePreviewViewWrapper ( editingStack: viewModel. editingStack)
104+
105+ case . preview:
106+ ImagePreviewViewWrapper ( editingStack: viewModel. editingStack)
107+ BlurryMaskingViewWrapper (
108+ editingStack: viewModel. editingStack,
109+ brushSize: viewModel. maskingBrushSize,
110+ isBlurryImageViewHidden: false
111+ )
112+ . allowsHitTesting ( false )
113+ }
114+
115+ if viewModel. isLoading {
116+ ZStack {
117+ Color . white. opacity ( 0.5 )
118+ ProgressView ( )
119+ . progressViewStyle ( CircularProgressViewStyle ( tint: . gray) )
120+ . scaleEffect ( 1.5 )
121+ }
122+ }
123+ }
124+ . frame ( width: squareSize, height: squareSize)
125+ . frame ( maxWidth: . infinity, maxHeight: . infinity)
126+ }
127+ }
128+
129+ @ViewBuilder
130+ private var controlArea : some View {
131+ ClassicImageEditControlStackSwiftUIView ( viewModel: viewModel)
132+ }
133+
134+ @ToolbarContentBuilder
135+ private var toolbarContent : some ToolbarContent {
136+ switch viewModel. mode {
137+ case . preview:
138+ ToolbarItem ( placement: . navigationBarLeading) {
139+ Button ( viewModel. localizedStrings. cancel) {
140+ onCancelEditing ( )
141+ }
142+ }
143+
144+ ToolbarItem ( placement: . navigationBarTrailing) {
145+ Button ( viewModel. localizedStrings. done) {
146+ onEndEditing ( viewModel. editingStack)
147+ }
148+ . fontWeight ( . semibold)
149+ . disabled ( viewModel. isLoading)
150+ }
151+
152+ default :
153+ ToolbarItem ( placement: . navigationBarLeading) {
154+ EmptyView ( )
155+ }
156+ }
157+ }
158+ }
159+
160+ struct CropViewWrapper : UIViewRepresentable {
161+ let editingStack : EditingStack
162+ let croppingAspectRatio : PixelAspectRatio ?
163+
164+ func makeUIView( context: Context ) -> CropView {
165+ let cropView = CropView ( editingStack: editingStack, contentInset: . zero)
166+ cropView. setCropOutsideOverlay (
167+ . init( ) &> . do {
168+ $0. backgroundColor = . white
169+ }
170+ )
171+ cropView. setCropInsideOverlay ( nil )
172+ cropView. isGuideInteractionEnabled = false
173+ cropView. isAutoApplyEditingStackEnabled = false
174+ cropView. setCroppingAspectRatio ( croppingAspectRatio)
175+ return cropView
176+ }
177+
178+ func updateUIView( _ uiView: CropView , context: Context ) {
179+ }
180+ }
181+
182+ struct ImagePreviewViewWrapper : UIViewRepresentable {
183+ let editingStack : EditingStack
184+
185+ func makeUIView( context: Context ) -> ImagePreviewView {
186+ return ImagePreviewView ( editingStack: editingStack)
187+ }
188+
189+ func updateUIView( _ uiView: ImagePreviewView , context: Context ) {
190+ }
191+ }
192+
193+ struct BlurryMaskingViewWrapper : UIViewRepresentable {
194+ let editingStack : EditingStack
195+ let brushSize : CanvasView . BrushSize
196+ let isBlurryImageViewHidden : Bool
197+
198+ func makeUIView( context: Context ) -> BlurryMaskingView {
199+ let view = BlurryMaskingView ( editingStack: editingStack)
200+ view. isBackdropImageViewHidden = true
201+ return view
202+ }
203+
204+ func updateUIView( _ uiView: BlurryMaskingView , context: Context ) {
205+ uiView. setBrushSize ( brushSize)
206+ uiView. isBlurryImageViewHidden = isBlurryImageViewHidden
207+ }
208+ }
209+
210+ #Preview {
211+ ClassicImageEditView (
212+ imageProvider: . init( image: UIImage ( systemName: " photo " ) !)
213+ )
214+ }
0 commit comments