Skip to content

Commit b0084ae

Browse files
committedMar 23, 2023
update media picker example
1 parent 744fb25 commit b0084ae

File tree

13 files changed

+326
-65
lines changed

13 files changed

+326
-65
lines changed
 

‎README.md

+33-9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
A collection of Swift, SwiftUI and iOS goodies.
44

5+
Feel free to request features or suggestions for improvements.
6+
7+
[![Developer](https://img.shields.io/badge/Maintainer-ImaginativeShohag-green)](https://github.com/ImaginativeShohag)
8+
59
## What we have hare!
610

711
### Jailbroken checker
@@ -29,17 +33,17 @@ A simple example to demonstrate separate views for iPhone and iPad. Users will s
2933

3034
### Component: `CoolProgress`
3135

32-
<img src="images/cool-progress.gif" width=250>
36+
<img src="images/cool-progress.gif" width=250/>
3337

3438
### Component: `CoolToast`
3539

3640
A cool "Android Toast" like implementation for SwiftUI.
3741

38-
<img src="images/cool-toast.gif" width=250>
39-
40-
### `ImageViewCapturer` Example
42+
<img src="images/cool-toast.gif" width=250/>
4143

44+
### Component: `ImageViewCapturer`: Media Capture & Select Example
4245

46+
Example for capturing image, recording video and selecting media from library.
4347

4448
### Component: `LabelToggle`
4549

@@ -55,12 +59,15 @@ On going...
5559

5660
SwiftUI modifier for `UIAlertController`. This is created to be able to change the Alert button colors.
5761

58-
<img src="images/native-alert.gif" width=250>
62+
<img src="images/native-alert.gif" width=250/>
5963

60-
### `RingChart`
64+
### Component: `RingChart`
6165

66+
<img src="images/ring-chart.gif" width=250/>
6267

63-
### `TextField` validation
68+
### Component: `CustomTextFieldView` with validation example
69+
70+
<img src="images/custom-text-field.gif" width=250/>
6471

6572
### Typography: Custom Font
6673

@@ -72,9 +79,8 @@ Add fonts to the [project](https://github.com/ImaginativeShohag/Why-Not-SwiftUI/
7279
## TODO
7380

7481
- [ ] MetricKit crash report example (WIP)
75-
- [ ] Update `CoolToast` code
7682
- [ ] Full app custom font
77-
- [ ] Custom Sidebar: Finalize it
83+
- [ ] Custom Sidebar: Finalize it (WIP)
7884
- [ ] Custom Build variant
7985
- [ ] Add documentation to the extensions
8086
- [ ] Add best ways to create preview with ViewModel
@@ -94,3 +100,21 @@ Add fonts to the [project](https://github.com/ImaginativeShohag/Why-Not-SwiftUI/
94100
### `Array` (`Array+.swift`)
95101

96102
- `commaSeparatedString(emptyValue:)`
103+
104+
## Licence
105+
106+
```
107+
Copyright 2021 Md. Mahmudul Hasan Shohagm
108+
109+
Licensed under the Apache License, Version 2.0 (the "License");
110+
you may not use this file except in compliance with the License.
111+
You may obtain a copy of the License at
112+
113+
http://www.apache.org/licenses/LICENSE-2.0
114+
115+
Unless required by applicable law or agreed to in writing, software
116+
distributed under the License is distributed on an "AS IS" BASIS,
117+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
118+
See the License for the specific language governing permissions and
119+
limitations under the License.
120+
```

‎Why Not SwiftUI.xcodeproj/project.pbxproj

+40-8
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
458E623E28EC706B005A4E54 /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 458E623D28EC706B005A4E54 /* Screen.swift */; };
4545
459837D228EB361D00AB5433 /* #001 KeyPath class.swift in Sources */ = {isa = PBXBuildFile; fileRef = 459837D128EB361D00AB5433 /* #001 KeyPath class.swift */; };
4646
459837D628EB4B7100AB5433 /* Fruit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 459837D528EB4B7100AB5433 /* Fruit.swift */; };
47-
459837D928EB5C1400AB5433 /* ImageVideoCapturerExampleScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 459837D828EB5C1400AB5433 /* ImageVideoCapturerExampleScreen.swift */; };
47+
459837D928EB5C1400AB5433 /* MediaSelectScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 459837D828EB5C1400AB5433 /* MediaSelectScreen.swift */; };
4848
459837DB28EB5C3D00AB5433 /* ImageVideoCapturer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 459837DA28EB5C3D00AB5433 /* ImageVideoCapturer.swift */; };
4949
459CA62728E702D400FC6F42 /* FitnessRingCardScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 459CA62628E702D400FC6F42 /* FitnessRingCardScreen.swift */; };
5050
459CA62B28E70E2B00FC6F42 /* Typography.swift in Sources */ = {isa = PBXBuildFile; fileRef = 459CA62A28E70E2B00FC6F42 /* Typography.swift */; };
@@ -65,6 +65,10 @@
6565
45BE892228A063A5008DF51F /* CombineMoya in Frameworks */ = {isa = PBXBuildFile; productRef = 45BE892128A063A5008DF51F /* CombineMoya */; };
6666
45BE892528A06D24008DF51F /* GoRestAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45BE892428A06D24008DF51F /* GoRestAPI.swift */; };
6767
45BE892728A06FF8008DF51F /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45BE892628A06FF8008DF51F /* HomeViewModel.swift */; };
68+
45CD36AD29CBC34C00DB24BD /* MediaSelectViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CD36AC29CBC34C00DB24BD /* MediaSelectViewModel.swift */; };
69+
45CD36B029CBC54400DB24BD /* UIAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CD36AF29CBC54400DB24BD /* UIAttachment.swift */; };
70+
45CD36B229CBC60700DB24BD /* URL+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CD36B129CBC60700DB24BD /* URL+.swift */; };
71+
45CD36B429CBD87E00DB24BD /* UIImage+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45CD36B329CBD87E00DB24BD /* UIImage+.swift */; };
6872
45F1C98F296F128C00386645 /* CoolToast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F1C98E296F128C00386645 /* CoolToast.swift */; };
6973
45F1C991296F129900386645 /* CoolToastScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F1C990296F129900386645 /* CoolToastScreen.swift */; };
7074
45F1C993296F13D000386645 /* Color+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F1C992296F13D000386645 /* Color+.swift */; };
@@ -149,7 +153,7 @@
149153
458E623D28EC706B005A4E54 /* Screen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Screen.swift; sourceTree = "<group>"; };
150154
459837D128EB361D00AB5433 /* #001 KeyPath class.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "#001 KeyPath class.swift"; sourceTree = "<group>"; };
151155
459837D528EB4B7100AB5433 /* Fruit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fruit.swift; sourceTree = "<group>"; };
152-
459837D828EB5C1400AB5433 /* ImageVideoCapturerExampleScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageVideoCapturerExampleScreen.swift; sourceTree = "<group>"; };
156+
459837D828EB5C1400AB5433 /* MediaSelectScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaSelectScreen.swift; sourceTree = "<group>"; };
153157
459837DA28EB5C3D00AB5433 /* ImageVideoCapturer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageVideoCapturer.swift; sourceTree = "<group>"; };
154158
459837DC28EB614700AB5433 /* Why-Not-SwiftUI-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "Why-Not-SwiftUI-Info.plist"; sourceTree = SOURCE_ROOT; };
155159
459CA62628E702D400FC6F42 /* FitnessRingCardScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FitnessRingCardScreen.swift; sourceTree = "<group>"; };
@@ -171,6 +175,10 @@
171175
45BE891928A05F76008DF51F /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
172176
45BE892428A06D24008DF51F /* GoRestAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoRestAPI.swift; sourceTree = "<group>"; };
173177
45BE892628A06FF8008DF51F /* HomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = "<group>"; };
178+
45CD36AC29CBC34C00DB24BD /* MediaSelectViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaSelectViewModel.swift; sourceTree = "<group>"; };
179+
45CD36AF29CBC54400DB24BD /* UIAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAttachment.swift; sourceTree = "<group>"; };
180+
45CD36B129CBC60700DB24BD /* URL+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+.swift"; sourceTree = "<group>"; };
181+
45CD36B329CBD87E00DB24BD /* UIImage+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+.swift"; sourceTree = "<group>"; };
174182
45F1C98E296F128C00386645 /* CoolToast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoolToast.swift; sourceTree = "<group>"; };
175183
45F1C990296F129900386645 /* CoolToastScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoolToastScreen.swift; sourceTree = "<group>"; };
176184
45F1C992296F13D000386645 /* Color+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Color+.swift"; sourceTree = "<group>"; };
@@ -268,6 +276,8 @@
268276
45B2D05B29031202006A631E /* ExtUIGestureRecognizerDelegate.swift */,
269277
45513A9129911EEA00BF0A0D /* String+.swift */,
270278
45513A932993D69800BF0A0D /* Array+.swift */,
279+
45CD36B129CBC60700DB24BD /* URL+.swift */,
280+
45CD36B329CBD87E00DB24BD /* UIImage+.swift */,
271281
);
272282
path = Extensions;
273283
sourceTree = "<group>";
@@ -353,13 +363,14 @@
353363
path = Home;
354364
sourceTree = "<group>";
355365
};
356-
459837D328EB45A800AB5433 /* VideoRecordAndUpload */ = {
366+
459837D328EB45A800AB5433 /* ImageVideoSelection */ = {
357367
isa = PBXGroup;
358368
children = (
359-
459837D828EB5C1400AB5433 /* ImageVideoCapturerExampleScreen.swift */,
360-
459837DA28EB5C3D00AB5433 /* ImageVideoCapturer.swift */,
369+
45CD36AE29CBC53700DB24BD /* Models */,
370+
459837D828EB5C1400AB5433 /* MediaSelectScreen.swift */,
371+
45CD36AC29CBC34C00DB24BD /* MediaSelectViewModel.swift */,
361372
);
362-
path = VideoRecordAndUpload;
373+
path = ImageVideoSelection;
363374
sourceTree = "<group>";
364375
};
365376
459837D428EB4B5F00AB5433 /* Model */ = {
@@ -397,7 +408,7 @@
397408
4568D1A628F8950D00FFDC5E /* Splash */,
398409
454539F0298130EE00E3D9D0 /* TextFieldValidation */,
399410
45513A972998F1BD00BF0A0D /* TextPreview */,
400-
459837D328EB45A800AB5433 /* VideoRecordAndUpload */,
411+
459837D328EB45A800AB5433 /* ImageVideoSelection */,
401412
);
402413
path = UIs;
403414
sourceTree = "<group>";
@@ -542,13 +553,30 @@
542553
45F1C99F29709B8500386645 /* CoolProgress */,
543554
45F1C98D296F126900386645 /* CoolToast */,
544555
45513A8E29911BFE00BF0A0D /* CustomTextField */,
556+
45CD36A929CBC23100DB24BD /* ImageVideoCapturer */,
545557
4557C0F229C4B0900030530B /* LabelToggle */,
546558
45F1C99B297098E700386645 /* NativeAlert */,
547559
459CA63728E76E6A00FC6F42 /* RingChart */,
548560
);
549561
path = Components;
550562
sourceTree = "<group>";
551563
};
564+
45CD36A929CBC23100DB24BD /* ImageVideoCapturer */ = {
565+
isa = PBXGroup;
566+
children = (
567+
459837DA28EB5C3D00AB5433 /* ImageVideoCapturer.swift */,
568+
);
569+
path = ImageVideoCapturer;
570+
sourceTree = "<group>";
571+
};
572+
45CD36AE29CBC53700DB24BD /* Models */ = {
573+
isa = PBXGroup;
574+
children = (
575+
45CD36AF29CBC54400DB24BD /* UIAttachment.swift */,
576+
);
577+
path = Models;
578+
sourceTree = "<group>";
579+
};
552580
45F1C98C296F123400386645 /* CoolToast */ = {
553581
isa = PBXGroup;
554582
children = (
@@ -734,12 +762,13 @@
734762
459EC11029007BF90019C555 /* View+ConditionalModifier.swift in Sources */,
735763
4557C0F429C4B0A20030530B /* LabelToggle.swift in Sources */,
736764
459837DB28EB5C3D00AB5433 /* ImageVideoCapturer.swift in Sources */,
765+
45CD36AD29CBC34C00DB24BD /* MediaSelectViewModel.swift in Sources */,
737766
45F1C993296F13D000386645 /* Color+.swift in Sources */,
738767
457B0ADC28FB70730019ED01 /* MenuRow.swift in Sources */,
739768
45BE892528A06D24008DF51F /* GoRestAPI.swift in Sources */,
740769
459CA62728E702D400FC6F42 /* FitnessRingCardScreen.swift in Sources */,
741770
4557C0F629C4B0AF0030530B /* LabelToggleScreen.swift in Sources */,
742-
459837D928EB5C1400AB5433 /* ImageVideoCapturerExampleScreen.swift in Sources */,
771+
459837D928EB5C1400AB5433 /* MediaSelectScreen.swift in Sources */,
743772
45513A9229911EEA00BF0A0D /* String+.swift in Sources */,
744773
45513A942993D69800BF0A0D /* Array+.swift in Sources */,
745774
45BE891328A05F73008DF51F /* Why_Not_SwiftUIApp.swift in Sources */,
@@ -760,6 +789,7 @@
760789
45ACAF5028F9F61B00C5467F /* CustomSideBarScreen.swift in Sources */,
761790
458E623E28EC706B005A4E54 /* Screen.swift in Sources */,
762791
4568D1A228F8235700FFDC5E /* UIDevice+JailBroken.swift in Sources */,
792+
45CD36B229CBC60700DB24BD /* URL+.swift in Sources */,
763793
4554D9C829B259F700063323 /* AccessibilityScreen.swift in Sources */,
764794
4568D1AC28F8975D00FFDC5E /* MainScreen.swift in Sources */,
765795
459EC10C290059620019C555 /* Color.swift in Sources */,
@@ -781,6 +811,8 @@
781811
45513A9029911C1E00BF0A0D /* CustomTextFieldView.swift in Sources */,
782812
457E604B29354156005595B7 /* NetworkError.swift in Sources */,
783813
457B0ADA28FB70340019ED01 /* MenuModels.swift in Sources */,
814+
45CD36B429CBD87E00DB24BD /* UIImage+.swift in Sources */,
815+
45CD36B029CBC54400DB24BD /* UIAttachment.swift in Sources */,
784816
459CA63328E70F5600FC6F42 /* RingChart.swift in Sources */,
785817
);
786818
runOnlyForDeploymentPostprocessing = 0;

‎Why Not SwiftUI/Model/Screen.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ struct Screen: Identifiable {
1414
Screen(name: "Typography", showTitle: true, destination: AnyView(TextPreviewScreen())),
1515
Screen(name: "Ring Chart: Overview", showTitle: true, destination: AnyView(OverviewRingCardScreen())),
1616
Screen(name: "Ring Chart: Fitness", showTitle: true, destination: AnyView(FitnessRingCardScreen())),
17-
Screen(name: "ImageViewCapturer Example", showTitle: true, destination: AnyView(ImageVideoCapturerExampleScreen())),
17+
Screen(name: "Media Capture & Select", showTitle: true, destination: AnyView(MediaSelectScreen())),
1818
Screen(name: "BottomNav vs SideBar", showTitle: false, destination: AnyView(BottomNavVsSideBarScreen())),
1919
Screen(name: "MetricKit", showTitle: true, destination: AnyView(MetricKitScreen())),
2020
Screen(name: "Cool Toast", showTitle: true, destination: AnyView(CoolToastScreen())),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
//
2+
// Copyright © 2022 Md. Mahmudul Hasan Shohag. All rights reserved.
3+
//
4+
5+
import PhotosUI
6+
import SwiftUI
7+
8+
// TODO: #1 Add icon to the image for showing source/type
9+
// TODO: #2 Add remove button
10+
// TODO: #3 Add overlay loading
11+
12+
struct MediaSelectScreen: View {
13+
@ObservedObject var viewModel = MediaSelectViewModel()
14+
15+
@State private var showAttachmentAddDialog: Bool = false
16+
@State private var showImageCapturer: Bool = false
17+
@State private var showVideoCapturer: Bool = false
18+
@State private var showPhotoLibrary: Bool = false
19+
20+
var body: some View {
21+
NavigationView {
22+
VStack(spacing: 0) {
23+
ScrollView {
24+
VStack {
25+
LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: 3)) {
26+
ForEach(viewModel.attachmentItems) { item in
27+
GeometryReader { geometry in
28+
Image(uiImage: item.image ?? UIImage(named: "jean-philippe-delberghe-75xPHEQBmvA-unsplash")!)
29+
.resizable()
30+
.scaledToFill()
31+
.frame(height: geometry.size.width)
32+
}
33+
.clipped()
34+
.aspectRatio(1, contentMode: .fit)
35+
.background(Color.systemGroupedBackground)
36+
.cornerRadius(16)
37+
}
38+
}
39+
}
40+
.padding()
41+
}
42+
43+
VStack {
44+
Button {
45+
showAttachmentAddDialog = true
46+
} label: {
47+
Text("Add Attachment")
48+
}
49+
.buttonStyle(.borderedProminent)
50+
.padding()
51+
}
52+
.frame(maxWidth: .infinity)
53+
.background(Color.systemBackground)
54+
.shadow(radius: 5)
55+
}
56+
.frame(maxWidth: .infinity, maxHeight: .infinity)
57+
.confirmationDialog(
58+
"Add Attachment",
59+
isPresented: $showAttachmentAddDialog
60+
) {
61+
Button {
62+
showImageCapturer = true
63+
} label: {
64+
Text("Take New Photo")
65+
.foregroundColor(Color.label)
66+
}
67+
68+
Button {
69+
showVideoCapturer = true
70+
} label: {
71+
Text("Take New Video")
72+
.foregroundColor(Color.label)
73+
}
74+
75+
Button {
76+
showPhotoLibrary = true
77+
} label: {
78+
Text("Choose Photo")
79+
.foregroundColor(Color.label)
80+
}
81+
82+
Button("Cancel", role: .cancel) {}
83+
}
84+
.fullScreenCover(isPresented: $showImageCapturer) {
85+
ImageVideoCapturer(defaultCaptureMode: .photo) { image, videoUrl in
86+
viewModel.addAttachment(image: image, videoUrl: videoUrl)
87+
}
88+
}
89+
.fullScreenCover(isPresented: $showVideoCapturer) {
90+
ImageVideoCapturer(defaultCaptureMode: .video) { image, videoUrl in
91+
viewModel.addAttachment(image: image, videoUrl: videoUrl)
92+
}
93+
}
94+
.photosPicker(
95+
isPresented: $showPhotoLibrary,
96+
selection: $viewModel.selectedItems,
97+
matching: .any(of: [.images, .videos])
98+
)
99+
.onChange(of: viewModel.selectedItems, perform: { _ in
100+
viewModel.addAttachments()
101+
})
102+
}
103+
.navigationViewStyle(.stack)
104+
}
105+
}
106+
107+
#if DEBUG
108+
109+
struct MediaSelectScreen_Previews: PreviewProvider {
110+
static var previews: some View {
111+
MediaSelectScreen(
112+
viewModel: MediaSelectViewModel(forPreview: true)
113+
)
114+
}
115+
}
116+
117+
#endif

0 commit comments

Comments
 (0)
Failed to load comments.