SwiftUI How to Disable Button While Uploading Objects in an Array - swift

I have a SwiftUI form where the user adds images to an array from a picker and then a function fires to upload each image. The images are displayed in a ForEach as either an upload spinner while the upload is happening OR the actual image once the upload has completed.
I'd like the NEXT button, which would remove the user from the view, to be disabled until all of the uploads have completed.
I'm not sure how to inform the State of the Parent View that all of the uploads are completed.
Here's what my Parent View looks like:
struct ProjectFormStep4: View {
#EnvironmentObject var authVM: AuthorizationClass
#EnvironmentObject var projectVM: ProjectsViewModel
#EnvironmentObject var uploadVM: UploadManager
#ObservedObject var mediaItems = PickedMediaItems()
#State private var showSheet: Bool = false
#State private var showUploadView: Bool = false
var body: some View {
ZStack{
Color("BrandGrey").ignoresSafeArea()
VStack{
HStack{
Button{
projectVM.showProjectFormStep1 = false
projectVM.showProjectFormStep2 = false
projectVM.showProjectFormStep3 = true
projectVM.showProjectFormStep4 = false
} label: {
Text("< Back")
.font(.headline)
.foregroundColor(Color("BrandLightBlue"))
}
Spacer()
}
Spacer()
.frame(height: 30)
ZStack{
Rectangle()
.fill(Color(.white))
.frame(width: 300, height: 100)
.cornerRadius(12)
Image(systemName: "camera")
.resizable()
.foregroundColor(Color("BrandLightBlue"))
.scaledToFit()
.frame(height: 60)
}.onTapGesture {
self.showSheet = true
}
Spacer()
.sheet(isPresented: $showSheet, content: {
PhotoPicker(mediaItems: mediaItems) { didSelectItem in
showUploadView = true
showSheet = false
}
})
Spacer()
ScrollView{
ForEach(uploadVM.uploadedMedia, id:\.id){ item in
ImageArea(
item: item,
items: uploadVM.uploadedMedia
)
}
}
Spacer()
if showUploadView {
ForEach(mediaItems.items, id: \.id) { item in
UploadView(item: item)
}
}
Button {
} label: {
Text("Next")
.font(.headline)
.foregroundColor(.white)
.padding()
.frame(width: 220, height: 60)
.background(Color("BrandOrange"))
.cornerRadius(15.0)
}
}.padding()
}
}
}
Here's my Child View of which handles the upload spinner and actual image:
struct ImageArea: View {
#EnvironmentObject var projectVM: ProjectsViewModel
#EnvironmentObject var uploadVM: UploadManager
#State private var showThumbnailOptions: Bool = false
var item: UploadedMedia
var items : [UploadedMedia]
var body: some View {
if item.secureUrl == "" {
ZStack{
UploadSpinner()
.frame(
width: 300,
height: 200
)
.cornerRadius(12)
VStack(alignment: .leading ){
HStack{
Spacer()
.frame(
width: 30
)
Image(systemName: self.getWaterMarkName(item: item))
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 24, height: 24)
.padding(4)
.background(Color.black.opacity(0.5))
.foregroundColor(.white)
}
Spacer()
HStack{
Spacer()
Text("\(item.uploadProgress)%")
.foregroundColor(.black)
.font(.body)
.padding()
Spacer()
}
}
}
} else {
ZStack{
ProjectRemoteUploadImage(projectImageUrl: projectVM.standardizeThumbnail(thumbnailUrl: item.secureUrl))
.aspectRatio(contentMode: .fit)
.onTapGesture {
showThumbnailOptions.toggle()
}
if showThumbnailOptions {
Color(.black).opacity(0.8)
VStack{
Spacer()
HStack{
Spacer()
Image(systemName: "arrowshape.turn.up.backward.circle.fill")
.resizable()
.foregroundColor(.white)
.frame(width: 50, height: 50)
.padding()
.onTapGesture {
self.showThumbnailOptions.toggle()
}
Image(systemName: "trash.circle.fill")
.resizable()
.foregroundColor(.red)
.frame(width: 50, height: 50)
.padding()
.onTapGesture {
deleteThumbnail(
item: item
)
}
Spacer()
}
Spacer()
}
}
}
}
}
func getWaterMarkName(item: UploadedMedia) -> String {
if item.mediaType == "photo"{
return "photo"
} else if item.mediaType == "video" {
return "video"
} else {
return "photo"
}
}
func deleteThumbnail(item: UploadedMedia){
let itemID = item.id
guard let itemIndex = uploadVM.uploadedMedia.firstIndex(where: {$0.id == itemID}) else { return }
uploadVM.uploadedMedia.remove(at: itemIndex)
}
}
Adding model info for the uploadedMedia
struct UploadedMedia {
var id: String
var uploadProgress: Float
var secureUrl: String
var mediaType: String
var width: Int
var length: Double
init(
id: String,
uploadProgress: Float,
secureUrl: String,
mediaType: String,
width: Int,
length: Double
){
self.id = id
self.uploadProgress = uploadProgress
self.secureUrl = secureUrl
self.mediaType = mediaType
self.width = width
self.length = length
}
}

Related

How to append something to a list?

I'm new to Swift at the moment and I'm having trouble appending to a to-do list. I am not receiving an error, so I don't know what's wrong. I hope this isn't too much code to go through, but I don't even know where the problem is.
There is a sheet modal that opens and closes fine. The only issue is that when I press the save button, the information I typed in doesn't add to the list.
I've add the ViewModel, NewTaskView(sheet), TaskView(customizes list), and a bit of code of the DetailView where the list should be.
import Foundation
import SwiftUI
class TaskViewModel : Identifiable , ObservableObject {
#Published var tasks : [Task] = [
Task(taskName: "Lab", taskDate: Date(), taskCompleted: false),
Task(taskName: "Assignment 4.02", taskDate: Date(), taskCompleted: false)
]
#Published var sortType : SortType = .alphabetical
#Published var isPresented = false
#Published var searched = ""
func addTask(task : Task){
tasks.append(task)
}
func removeTask(indexAt : IndexSet){
tasks.remove(atOffsets: indexAt)
}
func sort(){
switch sortType {
case .alphabetical :
tasks.sort(by: { $0.taskName < $1.taskName })
case .date :
tasks.sort(by: { $0.taskDate > $1.taskDate })
}
}
}
struct Task : Identifiable , Equatable {
var id : String = UUID().uuidString
let taskName : String
let taskDate : Date
var taskCompleted: Bool
}
enum SortType : String , Identifiable , CaseIterable {
var id : String { rawValue }
case alphabetical
case date
}
struct NewTaskView: View {
#Environment(\.presentationMode) var presentationMode
#ObservedObject var taskVM : TaskViewModel
#State var taskName = ""
#State var taskCompleted = Bool()
#State var taskDate = Date()
var body: some View {
NavigationView {
VStack(spacing: 14) {
Spacer()
TextField("Assignment Name",text: $taskName)
.padding()
.background(Color("tan"))
.cornerRadius(5)
.font(Font.custom(FontNameManager.Montserrat.semibold, size: 15))
.padding(.bottom, 20)
HStack{
Text("Due Date")
.font(Font.custom(FontNameManager.Montserrat.semibold, size: 15))
Image(systemName: "bell.badge")
.foregroundColor(.gray)
.frame(width: 30, height: 30, alignment: .leading)
VStack{
DatePicker("Select Date", selection: $taskDate, displayedComponents: .date)
.labelsHidden()
}
}
HStack{
Text("Mark as Completed")
.font(Font.custom(FontNameManager.Montserrat.semibold, size: 15))
Button(action : {
taskCompleted.toggle()
}){
if taskCompleted == true {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(Color("orange"))
.font(.title2)
}
else {
Image(systemName: "circle")
.foregroundColor(Color("lightorange"))
.font(.title2)
}
}
}
Spacer()
Button(action:{taskVM.addTask(task: .init(taskName: taskName, taskDate: taskDate, taskCompleted: taskCompleted))
presentationMode.wrappedValue.dismiss()},
label:{
Text("Save")
})
}.padding()
.navigationBarItems(trailing: Button(action: {
presentationMode.wrappedValue.dismiss()
}) {
HStack{
Image(systemName: "xmark")
.foregroundColor(Color("orange"))
}
})
}
}
}
struct NewTaskView_Previews: PreviewProvider {
static var previews: some View {
NewTaskView(taskVM: TaskViewModel())
}
}
struct TaskView: View {
var task : Task
#Environment(\.managedObjectContext) var moc
#State var currentDate: Date = Date()
#State var taskCompleted = false
func getDateFormatString(date:Date) -> String
{
var dateString = ""
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM. dd"
dateString = dateFormatter.string(from: date)
return dateString
}
var body: some View {
HStack {
Button(action : {
self.taskCompleted.toggle()
try? self.moc.save()
}){
if self.taskCompleted {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(Color("orange"))
.font(.title2)
}
else {
Image(systemName: "circle")
.foregroundColor(Color("lightorange"))
.font(.title2)
}
}.padding()
VStack (alignment: .leading, spacing: 2){
Text("\(task.taskName)")
.font(Font.custom(FontNameManager.Montserrat.bold, size: 18))
.foregroundColor(Color("dark"))
.padding([.leading, .trailing], 1)
if self.taskCompleted {
Text("Completed")
.font(Font.custom(FontNameManager.Montserrat.bold, size: 12))
.foregroundColor(Color("burntorange"))
.frame(width: 95, height: 20)
.background(Color("lightorange"))
.cornerRadius(4)
}
else if currentDate > task.taskDate {
Text("Late")
.font(Font.custom(FontNameManager.Montserrat.semibold, size: 12))
.foregroundColor(.white)
.frame(width: 95, height: 18)
.background(Color("lightbrown"))
.cornerRadius(4)
}
else {
let diffs = Calendar.current.dateComponents([.day], from: currentDate, to: task.taskDate )
Text("\(diffs.day!) days left")
.font(Font.custom(FontNameManager.Montserrat.semibold, size: 12))
.foregroundColor(Color("burntorange"))
.frame(width: 95, height: 20)
.background(Color("lightorange"))
.cornerRadius(4)
}
}
Spacer()
Text(getDateFormatString(date: task.taskDate ))
.font(Font.custom(FontNameManager.Montserrat.medium, size: 12))
.padding()
}
.padding(10)
.cornerRadius(10)
.background(
RoundedRectangle(cornerRadius: 10 , style: .continuous)
.foregroundColor(.white))
}
}
struct TaskView_Previews: PreviewProvider {
static var previews: some View {
TaskView(task: Task(id: "", taskName: "Task Name", taskDate: Date(), taskCompleted: false))
}
}
struct DetailsView: View {
#Environment(\.managedObjectContext) var moc
#ObservedObject var developers : Developer
#ObservedObject var taskVM : TaskViewModel
#Environment(\.presentationMode) var presen
#Environment(\.editMode) var editButton
#State var taskCompleted = false
#State var show = false
#State var showSearch = false
#State var txt = ""
#State var currentDate: Date = Date()
#State var image : Data = .init(count: 0)
#State var indices : [Int] = []
func getDateFormatString(date:Date) -> String
{
var dateString = ""
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM. dd"
dateString = dateFormatter.string(from: date)
return dateString
}
var body: some View {
NavigationView {
ZStack {
ZStack{
VStack{
Color("lightorange")
.clipShape(CustomCorners(corners: [.bottomLeft, .bottomRight], size: 70))
.ignoresSafeArea(.all, edges: .top)
.frame(width: 420, height: 175)
Spacer()
}
VStack{
HStack {
if !self.showSearch{
Button(action: {
withAnimation(.spring()){
self.presen.wrappedValue.dismiss()
}
}) {
Image(systemName: "chevron.left")
.font(.title2)
.foregroundColor(Color("dark"))
.padding(10)
}
}
Spacer(minLength: 0)
HStack {
if self.showSearch{
Image(systemName: "magnifyingglass")
.font(.title2)
.foregroundColor(Color("dark"))
.padding(.horizontal, 8)
TextField("Search", text: $taskVM.searched , onEditingChanged: { (isBegin) in
if isBegin {
showSearch = true
} else {
showSearch = false
}
})
Button(action: {
withAnimation{
self.showSearch.toggle()
}
}) {
Image(systemName: "xmark").foregroundColor(.black).padding(.horizontal, 8)
}
}
else {
Button(action: {
withAnimation {
self.showSearch.toggle()
}
}) {
Image(systemName: "magnifyingglass")
.font(.title2)
.foregroundColor(Color("dark"))
.padding(10)
}
}
}.padding(self.showSearch ? 10 : 0)
.background(Color("lightorange"))
.cornerRadius(20)
}
ZStack{
RoundedRectangle(cornerRadius: 25)
.foregroundColor(Color("tan"))
.frame(width: 335, height: 130)
HStack {
VStack (alignment: .leading){
// Image(uiImage: UIImage(data: developers.imageD ?? self.image)!)
// .resizable()
// .frame(width: 70, height: 70)
// .clipShape(RoundedRectangle(cornerRadius: 20))
Text("\(developers.username ?? "")")
.font(Font.custom(FontNameManager.Montserrat.bold, size: 24))
.foregroundColor(Color("dark"))
Text("\(developers.descriptions ?? "")")
.font(Font.custom(FontNameManager.Montserrat.semibold, size: 18))
.foregroundColor(.gray)
}
.padding([.leading, .trailing], 70)
.padding(.bottom, 80)
Spacer()
}
}
HStack{
Text("Assignments")
.font(Font.custom(FontNameManager.Montserrat.bold, size: 20))
Spacer(minLength: 0)
EditButton()
}
.padding([.leading, .trailing], 20)
Spacer()
List {
ForEach (taskVM.tasks.filter {
self.taskVM.searched.isEmpty ? true : $0.taskName.localizedCapitalized.contains(self.taskVM.searched)} ){ task in
TaskView(task: task)
.listRowSeparator(.hidden)
}
.onDelete(perform: {
taskVM.removeTask(indexAt: $0)
})
}
.listStyle(InsetListStyle())
}
}
VStack {
Spacer()
HStack {
Spacer()
ExpandableFAB(taskVM: TaskViewModel(), show: $show)
}
}
}.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
}
}
}
struct DetailsView_Previews: PreviewProvider {
static let persistenceController = PersistenceController.shared
static var developers: Developer = {
let context = persistenceController.container.viewContext
let developers = Developer(context: context)
developers.username = "Math"
developers.descriptions = "Calculus"
return developers
}()
static var previews: some View {
DetailsView(developers: developers, taskVM: TaskViewModel()).environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
struct ExpandableFAB: View {
#Environment(\.managedObjectContext) var moc
#ObservedObject var taskVM : TaskViewModel
#Binding var show: Bool
var body: some View{
VStack(spacing: 20){
if self.show{
Button(action: {
taskVM.isPresented.toggle()
}) {
Image(systemName: "list.bullet.rectangle.portrait").resizable().frame(width: 25, height: 25).padding()
}
.foregroundColor(.white)
.background(Color("orange"))
.clipShape(Circle())
Button(action: {
self.show.toggle()
}) {
Image(systemName: "doc.badge.plus").resizable().frame(width: 25, height: 25).padding()
}.foregroundColor(.white)
.background(Color("orange"))
.clipShape(Circle())
}
Button(action: {
self.show.toggle()
}) {
Image(systemName: "plus").resizable().frame(width: 25, height: 25).padding()
}.foregroundColor(.white)
.background(Color("orange"))
.clipShape(RoundedRectangle(cornerRadius: 20))
.padding()
.rotationEffect(.init(degrees: self.show ? 180 : 0))
}.fullScreenCover(isPresented: $taskVM.isPresented, content: {
NewTaskView(taskVM: taskVM)
})
}
}
#burnsi I don't think I have that anywhere. Where should there be one?
Of course you have one and you need one:
ExpandableFAB(taskVM: TaskViewModel(), show: $show)
This line is causing the issue.
You are somehow injecting a TaskViewModel in your DetailsView (You are not showing where), but not using it. Try:
ExpandableFAB(taskVM: taskVM, show: $show)
And the place you create your ViewModel ('TaskViewModel()') should have a #StateObject wrapper.
Explanation:
As you add something to your ViewModel the views depending on the ViewModel get rebuild. So your TaskViewModel ends up beeing recreated and you still have your 2 initial values in your array.

How to show data from a ObservableObject in Swiftui

iam trying to make an small Social Media App with SwiftUI.
I creat a ObservableObject class called user and an Sign up view where the var get there value. When iam tying to show the Data in my ProfilView a Error happens.
Thread 1: Fatal error: No ObservableObject of type User found. A View.environmentObject(_:) for User may be missing as an ancestor of this view.
Home is the Main View where i switch between the views with a bar when an Button is pressed a bool change in AppInformation.
struct ProfileView: View {
#EnvironmentObject var appUser: User
#State var beschreibung: String = ""
#State var benutzername: String = ""
#State var name: String = ""
var body: some View {
init() {
benutzername = appUser.username
name = appUser.name
}
ZStack {
Rectangle()
.frame(width: 400, height: 720)
.cornerRadius(50)
.foregroundColor(.gray)
.overlay(
HStack {
Image(systemName: "person.circle")
.resizable()
.frame(width: 100, height: 100)
.onTapGesture {
print("pressed")
}
.padding(20)
.overlay(
ZStack{
Rectangle()
.frame(width: 20, height: 20)
.offset(x: 35, y: -35)
.foregroundColor(.white)
Image(systemName: "plus.circle.fill")
.resizable()
.frame(width: 30, height: 30)
.offset(x: 35, y: -35)
.foregroundColor(.blue)
}
)
VStack {
Text(benutzername)
.font(.largeTitle)
.frame(width: 240 ,alignment: .leading)
.offset(x: -10, y: -25)
.lineLimit(1)
Text(name)
.frame(width: 220, alignment: .leading)
.offset(x: -15,y: -20)
.lineLimit(1)
}
}
.frame(width: 400, height: 720, alignment: .topLeading)
)
.padding()
ZStack {
Rectangle()
.foregroundColor(.secondary)
.frame(width: 380, height: 510)
.cornerRadius(45)
}
.frame(width: 400, height: 700, alignment: .bottom)
.padding()
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
}
}
class User: ObservableObject{
#Published var username: String = ""
#Published var name: String = ""
var password: String = ""
#Published var email: String = ""
#Published var beschreibung: String = ""
}
#State var isSecured: Bool = true
#State var noPassword: Int = 0
#State var noEmail: Int = 0
#State var noUsername: Int = 0
#State var noName: Int = 0
#State var password: String = ""
#State var username: String = ""
#State var name: String = ""
#State var email: String = ""
#EnvironmentObject var appInfo: AppInformation
#EnvironmentObject var appUser: User
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 25)
.frame(width: 380, height: 650)
.overlay(Text("Erstelle Accout ")
.foregroundColor(.white)
.frame(width: 360, height: 510, alignment: .top)
.font(.headline))
ZStack {
VStack {
TextField("Username", text: $username)
.frame(width:180 ,height:40 ,alignment: .center)
.multilineTextAlignment(.center)
.font(.headline)
.background(Color.gray.opacity(0.25))
.clipShape(Capsule())
.foregroundColor(.white)
.border(.red, width: CGFloat(noUsername))
Spacer()
.frame(height: 35)
TextField("Full name", text: $name)
.frame(width:180 ,height:40 ,alignment: .center)
.multilineTextAlignment(.center)
.font(.headline)
.background(Color.gray.opacity(0.25))
.clipShape(Capsule())
.foregroundColor(.white)
.border(.red, width: CGFloat(noName))
Spacer()
.frame(height: 35)
TextField("Email", text: $email)
.frame(width:180 ,height:40 ,alignment: .center)
.multilineTextAlignment(.center)
.font(.headline)
.background(Color.gray.opacity(0.25))
.clipShape(Capsule())
.foregroundColor(.white)
.border(.red, width: CGFloat(noEmail))
Spacer()
.frame(height: 35)
HStack {
Spacer()
.frame(width: 37)
if isSecured {
SecureField("Password", text: $password)
.frame(width:180 ,height:40 ,alignment: .center)
.multilineTextAlignment(.center)
.font(.headline)
.background(Color.gray.opacity(0.25))
.clipShape(Capsule())
.foregroundColor(.white)
.border(.red, width: CGFloat(noPassword))
} else {
TextField("Password", text: $password)
.frame(width:180 ,height:40 ,alignment: .center)
.multilineTextAlignment(.center)
.font(.headline)
.background(Color.gray.opacity(0.25))
.clipShape(Capsule())
.foregroundColor(.white)
.border(.red, width: CGFloat(noPassword))
}
ZStack {
Button(" ") {
isSecured.toggle()
print(isSecured)
}
Image(systemName: self.isSecured ? "eye" : "eye.slash")
.foregroundColor(.gray)
}
}
Spacer()
.frame(height: 60)
Button("Erstellen"){
if username == "" {
noUsername = 1
if name == "" {
noName = 1
if email == "" {
noEmail = 1
if password == "" {
noPassword = 1
}
}
else if password == "" {
noPassword = 1
}
}
else if email == "" {
noEmail = 1
if password == "" {
noPassword = 1
}
}
else if password == "" {
noPassword = 1
}
}
else if name == "" {
noName = 1
if email == "" {
noEmail = 1
if password == "" {
noPassword = 1
}
}
else if password == "" {
noPassword = 1
}
}
else if email == "" {
noEmail = 1
if password == "" {
noPassword = 1
}
}
else if password == "" {
noPassword = 1
}
else {
appUser.username = username
appUser.email = email
appUser.password = password
appUser.name = name
appInfo.finished = true
}
}
.font(.headline)
.frame(width: 150, height: 50)
.background(.blue)
.foregroundColor(.white)
.clipShape(Capsule())
}
}
}
}
}
class AppInformation: ObservableObject{
#Published var home: Bool = true
#Published var camera: Bool = false
#Published var friends: Bool = false
#Published var profil: Bool = false
#Published var showBar: Bool = true
#Published var finished: Bool = false
}
struct Home: View {
#EnvironmentObject var appInfo: AppInformation
#EnvironmentObject var appUser: User
var body: some View {
ZStack {
if appInfo.finished {
if appInfo.home {
HomeView()
}
else if appInfo.camera {
MakePostView()
}
else if appInfo.friends {
FriendsView()
}
else if appInfo.profil {
ProfileView()
.environmentObject(appUser)
}
if appInfo.showBar {
NavigationBar()
}
}
else {
ErstellenView().environmentObject(appInfo)
.environmentObject(appUser)
}
}
}
}
In ProfileView you must not have init() inside the view body.
Remove init() completely, and instead, use appUser.username and appUser.name directly, such as:
Text(appUser.username) and Text(appUser.name) . This is the purpose of using a User ObservableObject model. There is no need to create
local variables. When you try to access appUser in init() the EnvironmentObject will not be ready, it will be ...missing as an ancestor of this view. In other words, you should not use EnvironmentObject in init().
User should be a struct and make it a published property on the appInfo object. We usually only have one environment object holding the model structs and we usually call it model rather than appInfo.
Most of your existing bools in appInfo should be in an #State var bool in the View struct and related ones could be together in an #State var struct and you can use mutating funcs to manipulate the data.

Swift, Stuck on making an notes app, don't know what the problem is using text editor

I am new to coding and am trying to make a notes app come to life, the issue is I have no idea how to make separate text editors in my for loops.
This is my code. When you run the project and create a green sticky note and type on it, it works fine, but if you do a second one of the same color, they have the same text. How do I fix this in a way that doesn't take hours of tedious work?
I have tried to use different ways to make a for loop. I have made one with normal lists and with a struct list that has different ids, both come up with the same text editor.
(Text editors don't copy over color, which makes me think it's the for loop because when I tried to use different variables for the bindings it didn't work either.)
//
// ContentView.swift
// Notesapp
//
//
import SwiftUI
import Foundation
struct newBoy: Identifiable {
let id = UUID()
let number: Int
}
class NewBoys: ObservableObject {
#Published var numbs = [newBoy]()
}
struct ContentView: View {
#StateObject var numbers1 = NewBoys()
#State var buttonoff = true
#State var recaddor: [GridItem] = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible()),
]
#State var glist = [Int]()
#State var i = -1
#State var gtext = "This be me"
#State var blist = [Int]()
#State var bman = -1000
#State var btext = "Enter your notes here!"
#State var bklist = [Int]()
#State var bkman = -2000
#State var bktext = "Enter your notes here!"
#State var ylist = [Int]()
#State var yman = -3000
#State var ytext = "Enter your notes here!"
#State var olist = [Int]()
#State var oman = -4000
#State var otext = "Enter your notes here!"
#State var plist = [Int]()
#State var pman = -5000
#State var ptext = "Enter your notes here!"
var body: some View {
HStack{
VStack {
Text("Make new note!").padding().foregroundColor(Color.black)
Button {
withAnimation(.easeIn) {
self.buttonoff.toggle()
}
} label: {
Image(systemName: "plus")
.font(.title2)
.foregroundColor(.white)
.rotationEffect(.init(degrees: buttonoff ? 0 : 45))
.scaleEffect(buttonoff ? 1 : 1.3)
.padding()
}.buttonStyle(PlainButtonStyle())
.background(Color.black)
.clipShape(Circle())
.padding()
if buttonoff {
}
else {
Group {
Button {
recGreen()
} label: {
RoundedRectangle(cornerRadius: 25)
.fill(Color.green)
.frame(width: 30, height: 30)
}.buttonStyle(PlainButtonStyle())
Button {
recBlue()
} label: {
RoundedRectangle(cornerRadius: 25)
.fill(Color.blue)
.frame(width: 30, height: 30)
}.buttonStyle(PlainButtonStyle())
Button {
recBlack()
} label: {
RoundedRectangle(cornerRadius: 25)
.fill(Color.black)
.frame(width: 30, height: 30)
}.buttonStyle(PlainButtonStyle())
Button {
recYellow()
} label: {
RoundedRectangle(cornerRadius: 25)
.fill(Color.yellow)
.frame(width: 30, height: 30)
}.buttonStyle(PlainButtonStyle())
Button {
recOrange()
} label: {
RoundedRectangle(cornerRadius: 25)
.fill(Color.orange)
.frame(width: 30, height: 30)
}.buttonStyle(PlainButtonStyle())
Button {
recPurple()
} label: {
RoundedRectangle(cornerRadius: 25)
.fill(Color.purple)
.frame(width: 30, height: 30)
}.buttonStyle(PlainButtonStyle())
}.padding(20)
.scaleEffect(1.5)
}
}.frame(width: 100, height: 700, alignment: .top)
.background(Color.white)
.border(Color.gray, width: 2)
VStack {
ScrollView{
LazyVGrid(columns: recaddor){
ForEach(numbers1.numbs, id: \.id) {o in
ZStack{
RoundedRectangle(cornerRadius: 25)
.fill(Color.green)
.frame(width: 250, height: 200)
.padding()
VStack{
HStack{
Spacer()
Button {
print("hi")
} label: {
Image(systemName: "minus")
}
.padding(.top, 25.0)
.padding(.trailing, 30.0)
.frame(alignment:.trailing)
}
TextEditor(text: $gtext)
.frame(width: 225, height: 150, alignment: .center)
.cornerRadius(3.0)
.colorMultiply(.green)
Spacer()
}
}
}
ForEach(blist, id: \.self) {blueman in
ZStack{
RoundedRectangle(cornerRadius: 25)
.fill(Color.blue)
.frame(width: 250, height: 200)
.padding()
VStack{
HStack{
Spacer()
Button {
blist = blist.filter({ Int in
return Int != blueman
})
} label: {
Image(systemName: "minus")
}
.padding(.top, 25.0)
.padding(.trailing, 30.0)
.frame(alignment:.trailing)
}
TextEditor(text: $btext)
Spacer()
}
}
}
ForEach(bklist, id: \.self) {bklueman in
ZStack{
RoundedRectangle(cornerRadius: 25)
.fill(Color.black)
.frame(width: 250, height: 200)
.padding()
VStack{
HStack{
Spacer()
Button {
bklist = bklist.filter({ Int in
return Int != bklueman
})
} label: {
Image(systemName: "minus")
}
.padding(.top, 25.0)
.padding(.trailing, 30.0)
.frame(alignment:.trailing)
}
TextEditor(text: $bktext)
Spacer()
}
}
}
ForEach(ylist, id: \.self) {ylueman in
ZStack{
RoundedRectangle(cornerRadius: 25)
.fill(Color.yellow)
.frame(width: 250, height: 200)
.padding()
VStack{
HStack{
Spacer()
Button {
ylist = ylist.filter({ Int in
return Int != ylueman
})
} label: {
Image(systemName: "minus")
}
.padding(.top, 25.0)
.padding(.trailing, 30.0)
.frame(alignment:.trailing)
}
TextEditor(text: $ytext)
Spacer()
}
}
}
ForEach(olist, id: \.self) {olueman in
ZStack{
RoundedRectangle(cornerRadius: 25)
.fill(Color.orange)
.frame(width: 250, height: 200)
.padding()
VStack{
HStack{
Spacer()
Button {
olist = olist.filter({ Int in
return Int != olueman
})
} label: {
Image(systemName: "minus")
}
.padding(.top, 25.0)
.padding(.trailing, 30.0)
.frame(alignment:.trailing)
}
TextEditor(text: $otext)
Spacer()
}
}
}
ForEach(plist, id: \.self) {plueman in
ZStack{
RoundedRectangle(cornerRadius: 25)
.fill(Color.purple)
.frame(width: 250, height: 200)
.padding()
VStack{
HStack{
Spacer()
Button {
plist = plist.filter({ Int in
return Int != plueman
})
} label: {
Image(systemName: "minus")
}
.padding(.top, 25.0)
.padding(.trailing, 30.0)
.frame(alignment:.trailing)
}
TextEditor(text: $ptext)
Spacer()
}
}
}
}
}
}.frame(width: 1100)
}
}
func recGreen() {
let i = newBoy(number: 0)
numbers1.numbs.append(i)
print(numbers1.numbs)
}
func recBlue() {
bman += 1
blist.append(bman)
}
func recBlack() {
bkman += 1
bklist.append(bkman)
}
func recYellow() {
yman += 1
ylist.append(yman)
}
func recOrange() {
oman += 1
olist.append(oman)
}
func recPurple() {
pman += 1
plist.append(pman)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
It's because you are using the same binding.
In your ForEach, you are creating multiple TextEditors but they are editing the same variable like $otext. You need to create a struct that holds the text, and the array you use in the ForEach should be of type YourStruct. Then you pass the text to TextEditors.
struct Note: Identifiable {
var id: Int //your current array type, i.e if your array is [Int] use Int.
var text: String //this is the text to pass to the editors
}

Using TextField hides the ScrollView beneath it in VStack

This view hold a list of pdf names which when tapped open webviews of pdf links.
The view has a search bar above the list which when tapped causes the scrollview to disappear.
struct AllPdfListView: View {
#Environment(\.presentationMode) var mode: Binding<PresentationMode>
#ObservedObject var pdfsFetcher = PDFsFetcher()
#State var searchString = ""
#State var backButtonHidden: Bool = false
#State private var width: CGFloat?
var body: some View {
GeometryReader { geo in
VStack(alignment: .leading, spacing: 1) {
HStack(alignment: .center) {
Image(systemName: "chevron.left")
Text("All PDFs")
.font(.largeTitle)
Spacer()
}
.padding(.leading)
.frame(width: geo.size.width, height: geo.size.height / 10, alignment: .leading)
.background(Color(uiColor: UIColor.systemGray4))
.onTapGesture {
self.mode.wrappedValue.dismiss()
}
HStack(alignment: .center) {
Image(systemName: "magnifyingglass")
.padding([.leading, .top, .bottom])
TextField ("Search All Documents", text: $searchString)
.textFieldStyle(PlainTextFieldStyle())
.autocapitalization(.none)
Image(systemName: "slider.horizontal.3")
.padding(.trailing)
}
.overlay(RoundedRectangle(cornerRadius: 10).stroke(.black, lineWidth: 1))
.padding([.leading, .top, .bottom])
.frame(width: geo.size.width / 1.05 )
ScrollView {
ForEach($searchString.wrappedValue == "" ? pdfsFetcher.pdfs :
pdfsFetcher.pdfs.filter({ pdf in
pdf.internalName.contains($searchString.wrappedValue.lowercased())
})
, id: \._id) { pdf in
if let parsedString = pdf.file?.split(separator: "-") {
let request = URLRequest(url: URL(string: "https://mylink/\(parsedString[1]).pdf")!)
NavigationLink(destination: WebView(request: request)
.navigationBarBackButtonHidden(backButtonHidden)
.navigationBarHidden(backButtonHidden)
.onTapGesture(perform: {
backButtonHidden.toggle()
})) {
HStack(alignment: .center) {
Image(systemName: "doc")
.padding()
.frame(width: width, alignment: .leading)
.lineLimit(1)
.alignmentGuide(.leading, computeValue: { dimension in
self.width = max(self.width ?? 0, dimension.width)
return dimension[.leading]
})
Text(pdf.internalName)
.padding()
.multilineTextAlignment(.leading)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
}
.padding(.leading)
}
}
}
.navigationBarHidden(true)
}
.accentColor(Color.black)
.onAppear{
pdfsFetcher.pdfs == [] ? pdfsFetcher.fetchPDFs() : nil
}
}
}
}
}
Pdf list and Searchbar.
The same view on Searchbar focus.
I would like the search string to filter the list of pdfs while maintaining the visibility of the list.
I was able to fix this by making my #ObservableObject an #EnvironmentObject in my App :
#main
struct MyApp: App {
#ObservedObject var pdfsFetcher = PDFsFetcher()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(pdfsFetcher)
}
}
}
struct AllPdfListView: View {
#EnvironmentObject var pdfsFetcher: PDFsFetcher
}

How to change view with Navigationlink with two conditions in SwiftUI?

I have 3 buttons in my view. I want to change view with navigationlink when 2 buttons are active. When I tap to tap1 and tap3 I want to go to ViewOne(), when I tap to tap2 and tap3 I want to ViewTwo() How can I do that ?
#State var tap1 : Bool = false
#State var tap2 : Bool = false
#State var tap3 : Bool = false
NavigationLink(destination: ViewOne(), isActive: $tap1, label: {
VStack{
Image("smile")
.resizable()
.renderingMode(.original)
.aspectRatio(contentMode: .fit)
.frame(width: 150, height: 90)
Text("Fine")
.font(.system(size: 50, weight: .thin))
.padding(.bottom, 30)
}
NavigationLink(destination: ViewTwo(), isActive: $tap2) {
VStack{
Image("sad")
.resizable()
.renderingMode(.original)
.aspectRatio(contentMode: .fit)
.frame(width: 150, height: 90)
Text("Bad")
.font(.system(size: 50, weight: .thin))
.padding(.bottom, 30)
}
NavigationLink(destination: ?(), isActive: $tap3) {
Text("Continue")
You can try the following:
struct ContentView: View {
#State var tap1: Bool = false
#State var tap2: Bool = false
#State var isLinkActive: Bool = false
var body: some View {
NavigationView {
VStack {
button1
button2
button3
}
}
}
var button1: some View {
Button(action: {
self.tap1.toggle()
}) {
VStack {
Image("smile")
.resizable()
.renderingMode(.original)
.aspectRatio(contentMode: .fit)
.frame(width: 150, height: 90)
Text("Fine")
.font(.system(size: 50, weight: .thin))
.padding(.bottom, 30)
}
}
.background(tap1 ? Color.green : .clear)
}
var button2: some View {
Button(action: {
self.tap2.toggle()
}) {
VStack {
Image("sad")
.resizable()
.renderingMode(.original)
.aspectRatio(contentMode: .fit)
.frame(width: 150, height: 90)
Text("Bad")
.font(.system(size: 50, weight: .thin))
.padding(.bottom, 30)
}
}
.background(tap2 ? Color.red : .clear)
}
var button3: some View {
Button(action: {
// make sure you set the link only when ready
// what should happen if tap1 and tap2 are true?
self.isLinkActive.toggle()
}) {
Text("Continue")
}
.background(
NavigationLink(destination: destinationView, isActive: $isLinkActive) {
EmptyView()
}
.hidden()
)
}
#ViewBuilder
var destinationView: some View {
if tap1 {
Text("ViewOne")
} else if tap2 {
Text("ViewTwo")
}
}
}