I’ve a Dwelling display screen that, when a button is pressed, opens a mannequin display screen with a type for sending knowledge to the database, inside this mannequin window, as I stated, there’s a type by way of NavigationView {, Kind { …and so on
Inside this manner, there’s one other mannequin window that permits you to choose a number of photographs to add.
The issue is that due to all this salad, one thing broke and I simply can’t perceive what precisely, perhaps somebody from the skin will see what the issue is.
I see an error [Missing arguments for parameters ‘onEnd’, ‘onSelect’ in call] right here:
struct AddItemView_Previews: PreviewProvider {
static var previews: some View {
AddItemView()
}
}
After I change it to PhotosView it causes different errors – (additionally associated to needing to vary AddItemView to PhotosView) on the Dwelling display screen (which has a button that opens AddItemView which is the issue)
Huge Thanks!
All Stuff:
AddItemView
enum Mode {
case new
case edit
}
enum Motion {
case delete
case performed
case cancel
}
struct AddItemView: View {
@Surroundings(.presentationMode) non-public var presentationMode
@State var presentActionSheet = false
// MARK: - State (Initialiser-modifiable)
@ObservedObject var viewModel = NewItemView()
var mode: Mode = .new
var completionHandler: ((Consequence<Motion, Error>) -> Void)?
// MARK: - UI Parts
var cancelButton: some View {
Button(motion: { self.handleCancelTapped() }) {
Textual content("Cancel")
}
}
var saveButton: some View {
Button(motion: { self.handleDoneTapped() }) {
Textual content(mode == .new ? "Performed" : "Save")
}
.disabled(!viewModel.modified)
}
// MARK: Connecting image-picker View Mannequin
@StateObject var imagePickerModel: ImagePickerViewModel = .init()
@Surroundings(.self) var env
// MARK: Callbacks
var onEnd: ()->()
var onSelect: ([PHAsset])->()
var physique: some View {
NavigationView {
Kind {
Part(header: Textual content("New Merchandise")) {
TextField("Title", textual content: $viewModel.singleitem.title)
TextField("Description", textual content: $viewModel.singleitem.description)
}
Part(header: Textual content("Creator")) {
TextField("Creator", textual content: $viewModel.singleitem.writer)
}
if mode == .edit {
Part {
Button("Delete merchandise") { self.presentActionSheet.toggle() }
.foregroundColor(.pink)
}
}
let deviceSize = UIScreen.primary.bounds.dimension
VStack(spacing: 0) {
HStack {
Textual content("Choose Picture")
.font(.callout.daring())
.body(maxWidth: .infinity, alignment: .main)
Button {
onEnd()
} label: {
Picture(systemName: "xmark.circle.fill")
.font(.title3)
.foregroundColor(.major)
}
}
.padding([.horizontal, .top])
.padding(.backside, 10)
ScrollView(.vertical, showsIndicators: false) {
LazyVGrid(columns: Array(repeating: GridItem(.versatile(), spacing: 10), depend: 4),
spacing: 12) {
ForEach($imagePickerModel.fetchedImages){$imageAsset
in
// MARK: Grid Content material
GridContent(imageAsset: imageAsset)
.onAppear {
if imageAsset.thumbnail == nil {
// MARK: Fetching thumbnail picture
let supervisor = PHCachingImageManager.default()
supervisor.requestImage(for: imageAsset.asset, targetSize: CGSize(width: 100, top: 100), contentMode: .aspectFill, choices: nil) { picture,
_ in
imageAsset.thumbnail = picture
}
}
}
}
}
.padding()
}
.safeAreaInset(edge: .backside) {
// MARK: Add button
Button {
let imageAssets = imagePickerModel.selectedImages.compactMap {
ImageAsset -> PHAsset? in
return ImageAsset.asset
}
onSelect(imageAssets)
} label: {
Textual content("Add(imagePickerModel.selectedImages.isEmpty ? "" : "(imagePickerModel.selectedImages.depend)Photographs")")
.font(.callout)
.fontWeight(.semibold)
.foregroundColor(.white)
.padding(.horizontal, 30)
.padding(.vertical, 10)
.background {
Capsule()
.fill(.blue)
}
}
.disabled(imagePickerModel.selectedImages.isEmpty)
.opacity(imagePickerModel.selectedImages.isEmpty ? 0.6 : 1)
.padding(.vertical)
}
}
.body(top: deviceSize.top / 1.8)
.body(maxWidth: (deviceSize.width - 40) > 350 ? 350 : (deviceSize.width - 40))
.background {
RoundedRectangle(cornerRadius: 10, model: .steady)
.fill(env.colorScheme == .darkish ? .black : .white)
}
// MARK: Since its an overlay view
// Making it to take full display screen area
.body(width: deviceSize.width, top: deviceSize.top, alignment: .middle)
}
}
// MARK: Grid Picture Content material
.navigationTitle(mode == .new ? "Merchandise" : viewModel.singleitem.title)
.navigationBarTitleDisplayMode(mode == .new ? .inline : .massive)
.navigationBarItems(
main: cancelButton,
trailing: saveButton
)
.actionSheet(isPresented: $presentActionSheet) {
ActionSheet(title: Textual content("Are you positive?"),
buttons: [
.destructive(Text("Delete item"),
action: { self.handleDeleteTapped() }),
.cancel()
])
}
}
// MARK: - Motion Handlers
func handleCancelTapped() {
self.dismiss()
}
func handleDoneTapped() {
self.viewModel.handleDoneTapped()
self.dismiss()
}
func handleDeleteTapped() {
viewModel.handleDeleteTapped()
self.dismiss()
self.completionHandler?(.success(.delete))
}
func dismiss() {
self.presentationMode.wrappedValue.dismiss()
}
func GridContent(imageAsset: ImageAsset)-> some View {
GeometryReader { proxy in
let dimension = proxy.dimension
ZStack {
if let thumbnail = imageAsset.thumbnail {
Picture(uiImage: thumbnail)
.resizable()
.aspectRatio(contentMode: .fill)
.body(width: dimension.width, top: dimension.top)
.clipShape(RoundedRectangle(cornerRadius: 8, model: .steady))
} else {
ProgressView()
.body(width: dimension.width, top: dimension.top, alignment: .middle)
}
ZStack {
RoundedRectangle(cornerRadius: 8, model: .steady)
.fill(.black.opacity(0.1))
Circle()
.fill(.white.opacity(0.25))
Circle()
.stroke(.white, lineWidth: 1)
if let index = imagePickerModel.selectedImages.firstIndex(the place: { asset
in
asset.id == imageAsset.id
}) {
Circle()
.fill(.blue)
Textual content("(imagePickerModel.selectedImages[index].assetIndex + 1)")
.font(.caption2.daring())
.foregroundColor(.white)
}
}
.body(width: 20, top: 20)
.body(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
.padding(5)
}
.clipped()
.onTapGesture {
// MARK: Including / Eradicating Asset
withAnimation(.easeInOut) {
if let index = imagePickerModel.selectedImages.firstIndex(the place: { asset in
asset.id == imageAsset.id
}) {
// MARK: Take away and Replace Chosen Index
imagePickerModel.selectedImages.take away(at: index)
imagePickerModel.selectedImages.enumerated().forEach {
merchandise in
imagePickerModel.selectedImages[item.offset].assetIndex =
merchandise.offset
}
} else {
// MARK: Add new
var newAsset = imageAsset
newAsset.assetIndex = imagePickerModel.selectedImages.depend
imagePickerModel.selectedImages.append(newAsset)
}
}
}
}
.body(top: 70)
}
}
struct AddItemView_Previews: PreviewProvider {
static var previews: some View {
AddItemView()
}
}
extension View {
func endTextEditing() {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder),
to: nil, from: nil, for: nil)
}
}
ImagePickerViewModel
class ImagePickerViewModel: ObservableObject {
// MARK: Properties
@Printed var fetchedImages: [ImageAsset] = []
@Printed var selectedImages: [ImageAsset] = []
// MARK: Fetching Photographs
init() {
fetchImages()
}
func fetchImages() {
let choices = PHFetchOptions()
// MARK: Want modify after ending
choices.includeHiddenAssets = false
choices.includeAssetSourceTypes = [.typeUserLibrary]
choices.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
PHAsset.fetchAssets(with: .picture, choices: choices).enumerateObjects { asset, _, _ in
let imageAsset: ImageAsset = .init(asset: asset)
self.fetchedImages.append(imageAsset)
}
}
}
PhotosView
struct PhotosView: View {
// MARK: Picker properties
@State var showPicker: Bool = false
@State var pickedImages: [UIImage] = []
var physique: some View {
NavigationView {
TabView {
ForEach(pickedImages, id: .self) { picture in
GeometryReader { proxy in
let dimension = proxy.dimension
Picture(uiImage: picture)
.resizable()
.aspectRatio(contentMode: .fill)
.body(width: dimension.width, top: dimension.top)
.cornerRadius(15)
}
.padding()
}
}
.body(top: 450)
// MARK: Swift UI Bug
.tabViewStyle(.web page(indexDisplayMode: pickedImages.isEmpty ? .by no means : .at all times))
.toolbar {
Button {
showPicker.toggle()
} label: {
Picture(systemName: "plus")
}
}
}
.popupImagePicker(present: $showPicker) { asset in
// MARK: Instance of Kavsoft
let supervisor = PHCachingImageManager.default()
let choices = PHImageRequestOptions()
choices.isSynchronous = true
DispatchQueue.international(qos: .userInitiated).async {
asset.forEach { asset in
supervisor.requestImage(for: asset, targetSize: .init(), contentMode: .default, choices: choices) { picture, _ in
guard let picture = picture else { return }
DispatchQueue.primary.async {
self.pickedImages.append(picture)
}
}
}
}
}
}
}
struct PhotosView_Previews: PreviewProvider {
static var previews: some View {
PhotosView()
}
}
Extension
extension View {
@ViewBuilder
func popupImagePicker(present: Binding<Bool>, transition: AnyTransition = .transfer(edge: .backside), onSelect: @escaping ([PHAsset])->())-> some View {
self
.overlay {
let deviceSize = UIScreen.primary.bounds.dimension
ZStack {
// MARK: Bg blur
Rectangle()
.fill(.ultraThinMaterial)
.ignoresSafeArea()
.opacity(present.wrappedValue ? 1 : 0)
.onTapGesture {
present.wrappedValue = false
}
if present.wrappedValue {
AddItemView {
present.wrappedValue = false
} onSelect: { belongings in
onSelect(belongings)
present.wrappedValue = false
}
.transition(transition)
}
}
.body(width: deviceSize.width, top: deviceSize.top)
.animation(.easeInOut, worth: present.wrappedValue)
}
}
}
ImageAsset
struct ImageAsset: Identifiable {
var id: String = UUID().uuidString
var asset: PHAsset
var thumbnail: UIImage?
// MARK: Chosen Picture Index
var assetIndex: Int = -1
}