I Created Floating Menu in SwiftUI, but i am stuck in one issue, When press on bottom right plus button i open floating menu items and again press on plus button hide that floating menu items, but when press on menu items it will not hide that menu items.
Below is my code
MapContainerUIView.swift
struct MapContainerUIView: View {
let flotingMenuArray = [
MenuItemModel(title: "Camera", icon: "camera.fill"),
MenuItemModel(title: "Photo", icon: "photo.on.rectangle"),
MenuItemModel(title: "Share", icon: "square.and.arrow.up.fill")
]
var body: some View {
NavigationView{
ZStack {
FloatingMenu(dataSource: flotingMenuArray)
}
}
}
func floatingButtonAction(index: Int){
print("Floating item tap", index)
}
}
FloatingMenu.swift
struct FloatingMenu: View {
init() {
self.dataSource = []
}
init(dataSource: [MenuItemModel]) {
self.dataSource = dataSource
}
#State var isMenuShow = false
var dataSource: [MenuItemModel]
var body: some View {
ZStack(alignment: .trailing){
if isMenuShow{
Button(action: {
self.showMenu()
}) {
Text("")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.black)
.opacity(0.3)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.edgesIgnoringSafeArea(.all)
}
HStack{
Spacer()
VStack(alignment: .trailing, content: {
Spacer()
ForEach(0..<dataSource.count) { index in
if isMenuShow {
MenuItems(menuItemData: dataSource[index], index: index)
}
}
Button(action: {
self.showMenu()
}) {
Image(systemName: "plus.circle.fill")
.resizable()
.frame(width: 60, height: 60)
.foregroundColor(Color.white)
}
.background(Color.black)
.cornerRadius(35)
})
.shadow(color: .gray, radius: 4, x: 1, y: 1)
.padding(.init(top: 0, leading: 20, bottom: 20, trailing: 20))
}
}
}
func showMenu() {
self.isMenuShow.toggle()
}
}
MenuItems.swift
class MenuItemModel : Identifiable{
var id = UUID()
var title : String = ""
var icon : String = ""
init(title: String, icon: String) {
self.title = title
self.icon = icon
}
}
struct MenuItems: View {
var mapContainer = MapContainerUIView()
var menuItemData: MenuItemModel
var index : Int
var body: some View {
ZStack {
HStack{
Text(menuItemData.title)
.foregroundColor(.white)
.shadow(color: .gray, radius: 3, x: 1, y: 1)
Button(action: {
FloatingMenu().isMenuShow = false
print("button tap: ", menuItemData.title)
self.mapContainer.floatingButtonAction(index: index)
}) {
ZStack {
Circle()
.foregroundColor(Color.white)
.frame(width: 45, height: 45)
.shadow(color: .gray, radius: 3, x: 1, y: 1)
Image(systemName: menuItemData.icon)
.imageScale(.large)
.foregroundColor(.black)
}
.padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 10))
}
}
}
}
}
I want to hide floating menu when press on menu items, please someone guide me.
Right now, you're trying to set isMenuShow on a new instance of FloatingMenu, which is why it isn't working.
One solution is to pass a Binding from the original FloatingMenu instance.
struct MenuItems: View {
#Binding var isMenuShow : Bool //<-- Here
var mapContainer = MapContainerUIView()
var menuItemData: MenuItemModel
var index : Int
var body: some View {
ZStack {
HStack{
Text(menuItemData.title)
.foregroundColor(.white)
.shadow(color: .gray, radius: 3, x: 1, y: 1)
Button(action: {
self.isMenuShow = false //<-- Here
print("button tap: ", menuItemData.title)
self.mapContainer.floatingButtonAction(index: index)
}) {
And, in FloatingMenu:
if isMenuShow {
MenuItems(isMenuShow: $isMenuShow, //<-- Here
menuItemData: dataSource[index],
index: index)
}
Another solution would be to provide a closure as an argument to MenuItems that closes the menu:
struct MenuItems: View {
var closeMenu : () -> Void //<-- Here
var mapContainer = MapContainerUIView()
var menuItemData: MenuItemModel
var index : Int
var body: some View {
ZStack {
HStack{
Text(menuItemData.title)
.foregroundColor(.white)
.shadow(color: .gray, radius: 3, x: 1, y: 1)
Button(action: {
closeMenu() //<-- Here
print("button tap: ", menuItemData.title)
//self.mapContainer.floatingButtonAction(index: index)
}) {
if isMenuShow {
MenuItems(closeMenu: { self.isMenuShow = false },
menuItemData: dataSource[index],
index: index)
}
Related
I am trying to assign each weather location in my app a page in a tab view but am unsure on how to achieve this.
I have a currentPage variable in my location view which should add 1 every time a weather location is shown on screen but instead it just keeps adding one throughout the entire foreach loop I have, so a page number isn't actually assigned to a weather view.
I was wondering if anyone knew how I could assign a page number to each location when iterating through my for each loop.
My location view is shown below. The code I tried is at the bottom in the onAppear modifier in the if statement but it doesn't work like I said above and I am not sure if this is even the right approach for doing something like this. Any help would be great!
struct LocationView: View {
#ObservedObject var weatherVM: WeatherViewViewModel
#ObservedObject var multiLocationVM: MultiLocationViewModel
#State private var pageSelection = 1
#State var currentPage: Int = 0
var body: some View {
ZStack {
LinearGradient(colors: [Color("BackgroundColorSun"), Color("BackgroundColorSky")], startPoint: .topLeading, endPoint: UnitPoint(x: 0.5, y: 0.5)).ignoresSafeArea()
if multiLocationVM.weatherVM.count == 0 {
BlankLocationView(multiLocationVM: multiLocationVM)
} else {
TabView(selection: $pageSelection) {
ForEach(multiLocationVM.weatherVM, id: \.self.city) { location in
VStack(spacing: 0) {
VStack(spacing: -10) {
ZStack(alignment: .center) {
Text("\(location.getWeatherIconFor(icon: location.weatherIcon))")
//.innerShadow(Color(.red))
.font(.custom("SF Pro Text", size: 64))
.innerShadow()
//.foregroundColor(.gray)
.offset(x: -70, y: -35)
Text("\(location.getTempFor(temp: location.weather.current.temp))°")
.font(.system(size: 96, weight: .semibold, design: .rounded))
.tracking(-0.41)
.shadow(color: .black.opacity(0.25), radius: 4, x: 0, y: 4)
}
.offset(x: 15)
Text(location.conditions)
.font(.custom("SF Pro Display", size: 24))
}
.padding(EdgeInsets(top: -8, leading: 0, bottom: -8, trailing: 0))
VStack(spacing: -20) {
HourlyWeatherView(weatherVM: location)
DailyWeatherView(weatherVM: location)
}
Spacer()
}
.onAppear() {
if location.city != weatherVM.city {
weatherVM.city = location.city
currentPage += multiLocationVM.weatherVM.firstIndex(of: location)!
location.page = currentPage
print("current \(currentPage)")
print("Location \(location.page)")
} else {
return
}
}
.tag(currentPage)
}
}
.tabViewStyle(.page(indexDisplayMode: .automatic))
.indexViewStyle(.page(backgroundDisplayMode: .interactive))
}
}
.padding(.bottom, 70)
.ignoresSafeArea()
}
}
struct LocationView_Previews: PreviewProvider {
static var previews: some View {
LocationView(weatherVM: WeatherViewViewModel(city: "Phoenix"), multiLocationVM: MultiLocationViewModel())
}
}
IMHO you don't need the currentPage counter as TabView(selection: )is handling that for you. The selection, in your case pageSelection will update when the user swipes, or you can set it programmatically. You just have to provide a fitting .tag:
// dummy test data
struct Weather {
var city: String
var icon: String
var temp: Int
var condition: String
}
let multiLocationVM = [
Weather(city: "Vancouver", icon: "cloud", temp: 64, condition: " Mostly Cloudy"),
Weather(city: "New York", icon: "cloud.sun", temp: 78, condition: " Mostly Sunny"),
Weather(city: "Los Angeles", icon: "sun.max", temp: 91, condition: " Sunny"),
]
struct ContentView: View {
#State private var pageSelection = "Vancouver" // default here
#State var currentPage: Int = 0
var body: some View {
ZStack {
LinearGradient(colors: [Color(.yellow), Color(.blue)], startPoint: .topLeading, endPoint: UnitPoint(x: 0.5, y: 0.5)).ignoresSafeArea()
if multiLocationVM.count == 0 {
// BlankLocationView(multiLocationVM: multiLocationVM)
} else {
TabView(selection: $pageSelection) {
ForEach(multiLocationVM, id: \.city) { location in
LocalWeather(location: location)
.tag(location.city) // set tab id here
}
}
.tabViewStyle(.page(indexDisplayMode: .automatic))
}
}
.padding(.bottom, 70)
.ignoresSafeArea()
}
}
struct LocalWeather: View {
let location: Weather
var body: some View {
VStack(alignment: .center, spacing: 0) {
VStack(alignment: .center, spacing: -10) {
ZStack(alignment: .center) {
Image(systemName: location.icon)
.font(.custom("SF Pro Text", size: 32))
.offset(x: -90, y: -35)
Text("\(location.temp)")
.font(.system(size: 96, weight: .semibold, design: .rounded))
.tracking(-0.41)
.shadow(color: .black.opacity(0.25), radius: 4, x: 0, y: 4)
}
.offset(x: 15)
Text(location.condition)
.font(.custom("SF Pro Display", size: 24))
}
.padding(EdgeInsets(top: -8, leading: 0, bottom: -8, trailing: 0))
Spacer()
}
}
}
I am having a bug while adding an Object (Slider Card -- Photo of slider Card) on another Object (The background, and the map) What it does is stack 2 objects vertically (The bug), I assume it is because I use VStack. Is there a function that I can use to make the slider in front of the background? Thank you in advance.
import SwiftUI
struct ContentView: View {
let locations = ["Subaru WRX", "Tesla Model 3", "Porsche 911", "Renault Zoe", "DeLorean","Tony's Car"]
#State private var searchText : String = ""
var view = UILabel(frame:CGRect(x: 0, y: 0, width: 335, height: 29))
#State var slideBar = CGSize.zero
var body: some View {
VStack {
Label(
title: {Text("EcoFit")},
icon: {Image("logo")
.resizable()
.frame(width: 128.0, height: 64.0)
}
).labelStyle(IconOnlyLabelStyle())
SearchBar(text: $searchText)
HStack {
Spacer()
Button(action: {
}){
Text("Bike")
}
Spacer()
Button(action: {
}){
Text("Bus")
}
Spacer()
Button(action: {
}){
Text("Scooter")
}
Spacer()
Button(action: {
}){
Text("Walk")
}
Spacer()
}
List{
ForEach(self.locations.filter {
self.searchText.isEmpty ? true : $0.contains(self.searchText)
}, id: \.self) { car in Text(car)
}
}
.searchable(text: $searchText){
ForEach(self.locations.filter {
self.searchText.isEmpty ? true : $0.contains(self.searchText)
}, id: \.self) { result in Text("Are you looking for \(result)").searchCompletion(result)
}
}
Card() <----- slider card here!!
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading)
.background(Color.white)
}
}
this bug has been scratching my head for the past few days and I still don't know why the problem is arising and what the fix is. I have a camera screen and integrated it with the TOCropViewController (https://github.com/TimOliver/TOCropViewController) to allow a user to select a picture from their photo library and crop it to show a new post. For some reason the image picker is detecting that it should change the view to the ImagePicker from the camera view screen but it's not displaying it on ios14.4 and below but it works just fine for iOS 14.5 and above.
Here is my camera view code:
struct CameraView: View {
#StateObject var model = CameraModel()
#State var currentZoomFactor: CGFloat = 1.0
#StateObject var registerData = RegisterViewModel()
#StateObject var newPostData = NewPostModel()
enum SheetType {
case imagePick
case imageCrop
case share
}
#State private var currentSheet: SheetType = .imagePick
#State private var actionSheetIsPresented = false
#State private var sheetIsPresented = false
#State private var originalImage: UIImage?
#State private var image: UIImage?
#State private var croppingStyle = CropViewCroppingStyle.default
#State private var croppedRect = CGRect.zero
#State private var croppedAngle = 0
#StateObject var userData = UserViewModel()
var captureButton: some View {
Button(action: {
let impactMed = UIImpactFeedbackGenerator(style: .light)
impactMed.impactOccurred()
model.capturePhoto()
}, label: {
Circle()
.foregroundColor(.white)
.frame(width: 80, height: 80, alignment: .center)
.overlay(
Circle()
.stroke(Color.black.opacity(0.8), lineWidth: 2)
.frame(width: 65, height: 65, alignment: .center)
)
})
}
var capturedPhotoThumbnail: some View {
Group {
RoundedRectangle(cornerRadius: 10)
.frame(width: 55, height: 55, alignment: .center)
.foregroundColor(Color.gray.opacity(0.2))
.onTapGesture(perform: {
// newPostData.picker.toggle()
self.croppingStyle = .default
self.currentSheet = .imagePick
self.sheetIsPresented = true
print("HERE11 and \(self.currentSheet) and \(self.sheetIsPresented)")
})
.overlay(
Image("gallery")
.renderingMode(.template)
.resizable()
.frame(width: 25, height: 25)
.foregroundColor(Color("white")))
//CODE WITH BUG on ios 14.4 and below. I tried a regular sheet as well that works on another view in ios 14.4 but it doesn't work in the cameraview()
.sheet(isPresented: $sheetIsPresented) {
if (self.currentSheet == .imagePick) {
ImagePickerView(croppingStyle: self.croppingStyle, sourceType: .photoLibrary, onCanceled: {
// on cancel
}) { (image) in
guard let image = image else {
return
}
self.originalImage = image
DispatchQueue.main.async {
self.currentSheet = .imageCrop
self.sheetIsPresented = true
}
}
} else if (self.currentSheet == .imageCrop) {
ZStack {
Color("imagecropcolor").edgesIgnoringSafeArea(.all)
ImageCropView(croppingStyle: self.croppingStyle, originalImage: self.originalImage!, onCanceled: {
// on cancel
}) { (image, cropRect, angle) in
// on success
self.image = image
model.resetPhoto()
newPostData.newPost.toggle()
}
}
}
}
}
}
var flipCameraButton: some View {
Button(action: {
let impactMed = UIImpactFeedbackGenerator(style: .light)
impactMed.impactOccurred()
model.flipCamera()
}, label: {
Circle()
.foregroundColor(Color.gray.opacity(0.2))
.frame(width: 45, height: 45, alignment: .center)
.overlay(
Image(systemName: "camera.rotate.fill")
.foregroundColor(.white))
})
}
var body: some View {
GeometryReader { reader in
ZStack {
Color.black.edgesIgnoringSafeArea(.all)
VStack {
HStack{
Button(action: {
model.switchFlash()
}, label: {
Image(systemName: model.isFlashOn ? "bolt.fill" : "bolt.slash.fill")
.font(.system(size: 20, weight: .medium, design: .default))
})
.accentColor(model.isFlashOn ? .yellow : .white)
.padding(.leading, 30)
Spacer()
if model.photo != nil {
Text("taken photo").onAppear{
newPostData.newPost.toggle()
}
}
// Image(uiImage: model.photo.image!)
// .resizable()
// .aspectRatio(contentMode: .fill)
// .frame(width: 60, height: 60)
// .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
// .animation(.spring())
//
}
CameraPreview(session: model.session)
.gesture(
DragGesture().onChanged({ (val) in
// Only accept vertical drag
if abs(val.translation.height) > abs(val.translation.width) {
// Get the percentage of vertical screen space covered by drag
let percentage: CGFloat = -(val.translation.height / reader.size.height)
// Calculate new zoom factor
let calc = currentZoomFactor + percentage
// Limit zoom factor to a maximum of 5x and a minimum of 1x
let zoomFactor: CGFloat = min(max(calc, 1), 5)
// Store the newly calculated zoom factor
currentZoomFactor = zoomFactor
// Sets the zoom factor to the capture device session
model.zoom(with: zoomFactor)
}
})
)
.onAppear {
model.configure()
}
.alert(isPresented: $model.showAlertError, content: {
Alert(title: Text(model.alertError.title), message: Text(model.alertError.message), dismissButton: .default(Text(model.alertError.primaryButtonTitle), action: {
model.alertError.primaryAction?()
}))
})
.overlay(
Group {
if model.willCapturePhoto {
Color.black
}
}
)
.animation(.easeInOut)
HStack {
capturedPhotoThumbnail
Spacer()
captureButton
.padding(.trailing, 10)
Spacer()
flipCameraButton
}
.padding(.horizontal, 20)
.padding(.bottom, 20)
}
}.fullScreenCover(isPresented: $newPostData.newPost) {
if model.photo == nil {
NewPost(imageData: (self.image?.pngData())! )
} else {
NewPost(imageData: model.photo.originalData)
}
}
}
}
}
Here is where the CameraView() gets called from my Home Screen
import SwiftUI
import Firebase
struct Home: View {
#AppStorage("current_status") var status = false
#AppStorage("showSheet") var showSheet = false
#State var loadedPost = Post(id: 0, PostUID: "", PostName: "", selectedForPost: false, time: Date())
#State var selectedTab = "camera"
var edges = UIApplication.shared.windows.first?.safeAreaInsets
#StateObject var modelData = ModelView()
#StateObject var userData = UserViewModel()
var body: some View {
VStack(spacing: 15){
VStack (spacing: 0) {
GeometryReader{_ in
ZStack{
if selectedTab == "Post"{
Post(loadedPost: $loadedPost, selectedTab: $selectedTab)
}else if selectedTab == "camera"{
CameraView()
}else if selectedTab == "user"{
User(selectedTab: $selectedTab, loadedPost: $loadedPost)
}
}
}.onChange(of: selectedTab) { (_) in
switch(selectedTab){
case "Post": if
!modelData.isPostLoad{modelData.loadPost()}
case "camera": if
!modelData.isCameraLoad{modelData.loadCamera()}
case "user": if
!modelData.isUserLoad{modelData.loadUser()}
default: ()
}
}
//Tabview hide to show friend modal
if !showSheet{
Divider()
HStack(spacing: 0) {
Spacer(minLength: 0)
TabButton(title: "Post", selectedTab: $selectedTab)
Spacer(minLength: 0)
TabButton(title: "camera", selectedTab: $selectedTab)
.padding(.leading, 30)
.padding(.trailing, 30)
Spacer(minLength: 0)
TabButton(title: "user", selectedTab: $selectedTab)
Spacer(minLength: 0)
}
.padding(.horizontal, 30)
.padding(.bottom, edges!.bottom == 0 ? 15 : edges!.bottom)
.background(Color.black)
}
}
.ignoresSafeArea(.all, edges: .bottom)
.background(Color("Black").ignoresSafeArea(.all, edges: .all))
}
}
}
//Tab Button
struct TabButton : View {
var title: String
#Binding var selectedTab: String
var body: some View {
Button(action: {
withAnimation{selectedTab = title}
}) {
VStack(spacing: 5) {
//Top indicator
//Custom shape...
if title == "user" {
Image(title)
.renderingMode(.template)
.resizable()
.foregroundColor(selectedTab == title ? Color.white : Color("Grey"))
.frame(width: 26.5, height: 26.5)
.padding(.top, UIScreen.screenHeight < 500 ? -5 : 15)
}else if title == "camera"{
Image(title)
.renderingMode(.template)
.resizable()
.foregroundColor(selectedTab == title ? Color.white : Color("Grey"))
.frame(width: 40, height: 40)
.padding(.top, UIScreen.screenHeight < 500 ? -5 : 15)
}else{
Image(title)
.renderingMode(.template)
.resizable()
.foregroundColor(selectedTab == title ? Color.white : Color("Grey"))
.frame(width: 32.5, height: 32.5)
.padding(.top, UIScreen.screenHeight < 500 ? -5 : 15)
}
}
}
}
}
//can update with load views here
class ModelView: ObservableObject {
#Published var isPostLoad = false
#Published var isCameraLoad = false
#Published var isUserLoad = false
init() {
//load initial data
isCameraLoad = true
print("Home Data Loaded")
}
func loadPost(){
print("Post Loaded")
isPostLoad = true
}
func loadCamera(){
print("Camera Loaded")
isCameraLoad = true
}
func loadUser(){
print("User loaded")
isUserLoad = true
}
}
I would greatly appreciate any help on how to get the ImagePicker view to show up for iOS 14.1-ios 14.4 I've been scratching my head since I worked on it assuming anything that works on iOS 14.5 and above should work on below but only this specific ImagePicker is not working as intended. Thanks!
I hope you are having a more pleasant evening than mine!
So as I mentioned in the title, my for each loop crashes whenever I try to remove an item from the original list with a binding. I did some research and the problem is that for each generates a view with an id but when you delete the item in your child view it can't find the contents and crashes. Returns 'Thread 1: Fatal error: Index out of range'. I can fix the issue by declaring a #State var instead of #Binding, which really works! However, I have more than a delete button in my child view and if I don't use a binding declaration, changes made don't reflect on the main view. I don't wanna give up on neither the delete button nor buttons. Is there way to keep all of them in my childview?
Mainview declarations;
struct ContentView: View {
#ObservedObject var superReminders = SuperReminders()
#State var superReminder = SuperReminder()}
My list View;
List{
ForEach(superReminders.reminderlist.indices, id: \.self) { index in
NavigationLink(destination: DetailedRemView(superReminder : self.$superReminders.reminderlist[index] ).environmentObject(superReminders)) {
squareImageView(superReminder : self.$superReminders.reminderlist[index]).environmentObject(superReminders).environmentObject(superReminders)
}.listRowBackground(Color.clear)
}.onDelete { indexSet in
superReminders.reminderlist.remove(atOffsets: indexSet)}
}
Childview declarations;
import SwiftUI
struct DetailedRemView: View {
var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "EE, MMM d, YYYY"
return formatter
}
#State public var showingDetail = false
#Environment(\.colorScheme) var colorScheme: ColorScheme
#State private var deleteReminderAlert = false
#EnvironmentObject var superReminders : SuperReminders
#Environment(\.presentationMode) var presentationMode
#Binding var superReminder : SuperReminder
#State private var showDialog = false
#State var animate = false
var body: some View {
VStack{
HStack(alignment: .center){
Text(superReminder.remdate)
.font(.title)
.multilineTextAlignment(.leading)
.padding(.leading)
.frame(minWidth: 100,maxWidth: .infinity, maxHeight: 50)
Spacer()
Button(action: {
self.showDialog.toggle()
}, label: {
ZStack{
RoundedRectangle(cornerRadius: 10)
.fill(Color.blue)
.frame(width: 80, height: 35)
HStack{
Text("Edit")
.foregroundColor(.white)
.multilineTextAlignment(.center)
.cornerRadius(8)
Image(systemName: "pencil")
.foregroundColor(.white)
}
}
.shadow(color:Color.gray.opacity(0.3), radius: 3, x: 3, y: 3)
.padding(.leading)
.alert(isPresented: $showDialog,
TextAlert(title: "Edit reminder title",
message: "Enter a new title or dissmis.", placeholder: superReminder.remdate,
keyboardType: .default) { result in
if let text = result {
if text != "" {
superReminder.remdate = text }
else{}
} else {
}
})
})
.padding(.leading)
}
.frame(minWidth: 100, maxWidth: /*#START_MENU_TOKEN#*/.infinity/*#END_MENU_TOKEN#*/, maxHeight: 50, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.padding(.vertical, -10)
ZStack(alignment: .topTrailing){
Image(superReminder.image)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(minWidth: 0,maxWidth: .infinity,minHeight: 200,maxHeight: .infinity)
.saturation(superReminder.pastreminder ? 0.1 : 1)
.clipShape(Rectangle())
.cornerRadius(10)
.padding(.all)
.pinchToZoom()
HStack{
Text(superReminder.dateactual, formatter: dateFormatter)
.foregroundColor(.white)
.frame(width: 180, height: 30, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.background( superReminder.pastreminder ? Color.gray : Color.lightlygreen)
.cornerRadius(8)
.animation(/*#START_MENU_TOKEN#*/.easeIn/*#END_MENU_TOKEN#*/)
if superReminder.pastreminder == true {
ZStack{
RoundedRectangle(cornerRadius: 8)
.fill(Color.black)
.frame(width: 30, height: 30)
Image(systemName: "moon.zzz")
.foregroundColor(.white)
}.offset(x: animate ? -3 : 0)
.onAppear(perform: {
shake()
})
}
else{}
}
.zIndex(0)
.offset(x: -10, y: 10)
.padding()
}
.zIndex(1)
.shadow(color: Color.gray.opacity(0.4), radius: 3, x: 1, y: 2)
HStack{
Button(action: {
self.showingDetail.toggle()
}){
ZStack{
RoundedRectangle(cornerRadius: 10)
.fill(Color.lightlygreen)
.frame(width: 140, height: 40)
HStack{
Text("Reschedule")
.foregroundColor(.white)
.multilineTextAlignment(.center)
.cornerRadius(8)
Image(systemName: "calendar")
.foregroundColor(.white)
}
}
.shadow(color:Color.gray.opacity(0.3), radius: 3, x: 3, y: 3)
.padding(.all, 4.0)
}
.sheet(isPresented: $showingDetail, content :{
remdatepicker(isPresented: self.$showingDetail, superReminder: $superReminder)})
Button(action: {
if superReminder.pastreminder == true {
superReminder.pastreminder = false
}
else if superReminder.pastreminder == false{
superReminder.pastreminder = true
}
}, label: {
ZStack{
RoundedRectangle(cornerRadius: 10)
.fill(superReminder.pastreminder == true ? Color.lightlyblue : Color.gray)
.frame(width: 100, height: 40)
HStack{
Text(superReminder.pastreminder == true ? "Activate" : "Silence")
.foregroundColor(.white)
.multilineTextAlignment(.center)
.cornerRadius(8)
Image(systemName: superReminder.pastreminder == true ? "checkmark.circle" : "moon.zzz")
.foregroundColor(.white)
}
}
.shadow(color:Color.gray.opacity(0.3), radius: 3, x: 3, y: 3)
.padding(.all, 4.0)
})
Button(action: {
self.deleteReminderAlert.toggle()
}, label: {
ZStack{
RoundedRectangle(cornerRadius: 10)
.fill(Color(.red))
.frame(width: 40, height: 40)
HStack{
Image(systemName: "trash")
.foregroundColor(.white)
}
}
.shadow(color:Color.gray.opacity(0.3), radius: 3, x: 3, y: 3)
.padding(.all, 4.0)
})
}.padding(.bottom, 20)
.alert(isPresented: $deleteReminderAlert){
Alert(
title: Text("Are you sure?"),
message: Text("Do you want to delete this reminder?"),
primaryButton: .destructive(Text("Yes"), action: {
superReminders.remove(superReminder: superReminder)
self.presentationMode.wrappedValue.dismiss()
}),
secondaryButton: .cancel(Text("No"))
)
}
}
}
func shake() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
withAnimation(Animation.default.repeatCount(6).speed(7)){
animate.toggle()}}}
}
Class and List;
import SwiftUI
struct SuperReminder: Identifiable, Codable, Equatable {
var id = UUID()
var remdate = ""
var dateactual = Date.init()
var image = "New1"
var pastreminder = false
}
class SuperReminders: ObservableObject {
#Published var reminderlist: [SuperReminder]
init() {
self.reminderlist = [
]
}
func add(superReminder: SuperReminder) {
reminderlist.append(superReminder)
}
func remove(superReminder: SuperReminder) {
if let index = reminderlist.firstIndex(of: superReminder) {
reminderlist.remove(at: index)
}
}
}
This answer is similar to
Accessing and manipulating array item in an EnvironmentObject
Loop over superReminders.reminderlist since SuperReminder: Identifiable, Codable, Equatable.
ForEach(superReminders.reminderlist) { superReminder in
NavigationLink(destination: DetailedRemView(superReminders: superReminders,
superReminder: superReminder)) {
-----
}
}
In DetailedRemView, do the following:
struct DetailedRemView: View {
#ObservedObject var superReminders : SuperReminders
var superReminder : SuperReminder
// find index of current superReminder
var indexOfReminder: Int? {
superReminders.reminderlist.firstIndex {$0 == superReminder}
}
var body: some View {
// Unwrap indexOfReminder
if let index = indexOfReminder {
VStack {
------
}
}
}
----
}
Use superReminders.reminderlist[index] in DetailRemView whereever you need to update superReminder.
superReminders.reminderlist[index].pastreminder = false
I created a tabview with 4 views. Each view has a viewModel where I load the data needed in the view from ana api. It works fine except the first view is always empty unless I tap in a tab then tap on the first view tap again.
Any idea how I could make sure all the first view's data is there as soon as it's loaded?
Find my code below
thanks
-Contentview
#ViewBuilder
var body: some View {
if isLoggedIn() {
MainScreen()
} else {
UnAuthenticatedScreen()
}
}
-MainView
#ObservedObject var eventsVM: EventsVM = EventsVM()
var body: some View {
TabView(){
HomeScreen(events: self.eventsVM.events)
.tabItem {
Image(systemName: "house")
Text("Home")
}
.navigationBarHidden(true)
EventsScreen(events: self.eventsVM.events)
.tabItem {
Image(systemName: "calendar")
Text("Events")
}.navigationBarHidden(true)
}
}
- EventsVM
import Foundation
import Combine
class EventsVM: ObservableObject {
let didChange = PassthroughSubject<[EventModel], Never>()
private let eventsService: EventService
#Published var events = [EventModel]()
init() {
self.eventsService = EventService()
self.fetchEvents()
}
private func fetchEvents(){
self.eventsService.getAllEvents { (_events, _error) in
guard let events = _events else { return }
self.events = events
}
}
}
The home view
import SwiftUI
struct HomeScreen: View {
var eventsVM: EventsVM
#State var news: [NewsModel] = []
#State var albums = [AlbumModel]()
init(eventsVM: EventsVM){
self.eventsVM = EventsVM()
}
var body: some View {
GeometryReader { gr in
VStack(alignment: .leading, spacing: 0) {
HStack{
Spacer()
}
HStack {
Spacer()
Text("About Us")
Image("logo_squad")
.resizable()
.frame(width: 50, height: 50)
}
Text("Events").font(Font.custom("Francois One", size: 30)).foregroundColor(.red)
ScrollView(.horizontal, showsIndicators: false){
HStack {
ForEach(self.eventsVM.events, id: \.self) { event in
HomeEventRow(event: event).frame(width: gr.size.width - 60, height: 170)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.gray, lineWidth: 1).shadow(radius: -2)
)
.clipShape(RoundedRectangle(cornerRadius: 10))
}
}
}
// Text("News").font(Font.custom("Francois One", size: 30)).foregroundColor(.red)
// ScrollView(.horizontal, showsIndicators: false){
// HStack {
// ForEach(self.news, id: \.self){ _news in
// HomeNewsRow(news: _news)
// .frame(width: gr.size.width - 60, height: 150)
// .overlay(
// RoundedRectangle(cornerRadius: 10)
// .stroke(Color.gray, lineWidth: 1).shadow(radius: -2)
// )
// .clipShape(RoundedRectangle(cornerRadius: 10))
// }
// }
// }
// Text("Albums").font(Font.custom("Francois One", size: 30)).foregroundColor(.red)
// ScrollView(Axis.Set.horizontal, showsIndicators: true){
// HStack {
// ForEach(self.albums, id: \.self){ album in
// HomeMediaRow(album: album)
// .frame(width: gr.size.width - 60, height: 150)
// }.overlay(
// RoundedRectangle(cornerRadius: 10)
// .stroke(Color.gray, lineWidth: 1).shadow(radius: -2)
// )
// .clipShape(RoundedRectangle(cornerRadius: 10))
// }
// }
Spacer()
}
.padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.onAppear {
print("self.eventsVM.events \(self.eventsVM.events)")
}
}.padding(EdgeInsets(top: 0, leading: 5, bottom: 0, trailing: 0))
}
}
struct HomeScreen_Previews: PreviewProvider {
static var previews: some View {
HomeScreen(eventsVM: EventsVM())
}
}