Construct View Like Pedantix SwiftUI - swift

The problem is quite simple. I want to build something like Pedantix https://cemantix.certitudes.org/pedantix in SwiftUI.
I've this already :
So, I try to have my RoundedRectangle overlay to totally hide my text. And I want blocks to go at the line if needed, etc. I tried LazyHGrid (actually this), LazyVGrid, custom grid. But no results ...
import SwiftUI
struct Word: Identifiable, Equatable {
var id = UUID()
var text: String
var isFramed: Bool
var isTouched: Bool
}
struct ContentView: View {
#EnvironmentObject var service: Service
let rows = [
GridItem(.adaptive(minimum: 30)),
]
var body: some View {
GeometryReader { gr in
ScrollView {
VStack(alignment: .leading) {
HStack {
Spacer()
Image(systemName: "arrow.counterclockwise.circle")
.resizable()
.scaledToFit()
.frame(width: 24)
.onTapGesture {
service.loadRandomMovies(page: 1, completion: { _ in
service.loadMovie(id: service.randomMovieId ?? 0, completion: { _ in })
service.loadCredits(id: service.randomMovieId ?? 0, completion: { _ in })
})
}
}
HStack {
VStack {
RoundedRectangle(cornerRadius: 8)
.frame(width: 150, height: 250)
}
.padding()
VStack(alignment: .center) {
customTextView(with: service.frame(in: .title))
.padding(.bottom, 8)
customTextView(with: service.frame(in: .genres))
.padding(.bottom, 8)
.frame(width: gr.size.width * 0.8)
Text("\(service.movie?.releaseDate ?? "")")
.font(.title3)
.padding(.bottom, 8)
if service.movie?.tagline != "" {
Text("\"\(service.movie?.tagline ?? "")\"")
.font(.title3)
.padding(.bottom, 8)
.frame(alignment: .center)
}
customTextView(with: service.frame(in: .overview))
.padding(.bottom, 8)
.frame(width: gr.size.width * 0.8)
Text("\(service.credits?.cast.map({ $0.name }).joined(separator: " - ") ?? "")")
.fontWeight(.bold)
}
}
}
.padding()
}
.frame(width: gr.size.width)
}
}
}
extension ContentView {
#ViewBuilder
func customTextView(with words: [Word]) -> some View {
VStack {
LazyHGrid(rows: rows, spacing: 2) {
ForEach(words) { word -> AnyView in
if word.isFramed {
return AnyView(
Text("\(word.text)")
.padding(2)
.overlay(RoundedRectangle(cornerRadius: 4))
.overlay {
if word.isTouched {
Text("\(word.text.count)")
.foregroundColor(Color.cyan)
}
}
)
}
return AnyView(Text(word.text))
}
}
}
}
}

Do you think you could post your code so that we can see what you have done?

Related

Swift: reload view after midnight or when a widget refreshes?

My app uses CoreData to store one quote for each day. There's a widget, the timeline of which is set to update after midnight and fetch the quote for that particular date. This works fine. However, I noticed that when I click on the widget with the new day's quote, the app - which has remained in the background - still shows the previous day's quote. Removing the app from the list of open ones and reopening it shows the correct information.
Is there a way to force a refresh of the DailyQuoteView and make sure that there isn't leftover data?
EDIT: Here's the daily quote struct that I'd like to always show the relevant data when brought from the background.
import SwiftUI
struct DailyQuoteView: View {
#Environment(\.scenePhase) var scenePhase
#State var id: UUID = .init()
#EnvironmentObject var unsplashModel: UnsplashModel
let managedObjectContext = CoreDataStack.shared.managedObjectContext
#FetchRequest(
sortDescriptors: [NSSortDescriptor(key: "date", ascending: true)],
predicate: NSPredicate(
format: "date >= %# AND date <= %#",
argumentArray: [
Calendar.current.startOfDay(for: Date()),
Calendar.current.date(byAdding: .minute, value: 1439, to: Calendar.current.startOfDay(for: Date()))!
]
)
) var quote: FetchedResults<QuoteEntity>
#FetchRequest(
sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)]
) var quotes: FetchedResults<QuoteEntity>
#State var shouldPresentSheet = false
#State var animateCard = false
#Binding var cardOffset: Double
#Binding var coverImageSize: Double
#State var showAboutScreen = false
// MARK: - ELEMENTS
var coverImage: some View {
Image(uiImage: UIImage(data: quote.first?.image ?? Data()) ?? UIImage())
.resizable()
.scaledToFill()
}
var aboutButton: some View {
Button {
showAboutScreen.toggle()
} label: {
Image(systemName: "info")
.foregroundColor(.primary)
.padding(10)
.background(Circle().fill(Color("background")))
.font(.title3)
}
.padding()
.shadow(radius: 5)
}
var titleView: some View {
Text(quote.first?.title ?? "")
.font(.largeTitle)
.multilineTextAlignment(.center)
.bold()
.minimumScaleFactor(0.5)
}
var contentsBox: some View {
ScrollView {
VStack (alignment: .leading, spacing: 10) {
Text(quote.first?.summary ?? "")
.font(.body)
.multilineTextAlignment(.leading)
.padding(.bottom, 20)
if let username = quote.first?.imageUserName {
HStack(spacing: 0) {
Text("Photo: ")
Link(
destination: URL(string: "https://unsplash.com/#\(username)?utm_source=quote_of_the_day&utm_medium=referral")!,
label: {
Text((quote.first?.imageUser)!)
.underline()
})
Text(" on ")
Link(
destination: URL(string: "https://unsplash.com/")!,
label: {
Text("Unsplash")
.underline()
})
Spacer()
}
.font(.caption)
.italic()
.foregroundColor(.gray)
.padding(.vertical, 3)
}
}
.font(.subheadline)
}
}
var buttonRow: some View {
HStack {
Menu(
content: {
Button {
shouldPresentSheet.toggle()
} label: {
Text("as image")
Image(systemName: "photo")
}
ShareLink(item: quote.first!.title, message: Text(quote.first!.summary)) {
Text("as text")
Image(systemName: "text.justify.left")
}
},
label: {
HStack {
Image(systemName: "square.and.arrow.up")
Text("Share")
}
.padding(10)
.background(RoundedRectangle(cornerRadius: 10).fill(Color("whitesand")))
.tint(Color(.secondaryLabel))
}
)
Spacer()
Link(destination: URL(string: quote.first!.link)!) {
Image(systemName: "book")
Text("Read more")
}
.padding(10)
.background(RoundedRectangle(cornerRadius: 10).fill(Color("vpurple")))
.tint(Color(.white))
}
.frame(height: 60)
.sheet(isPresented: $shouldPresentSheet) {
QuoteShare(quote: quote.first!)
}
}
var cardView: some View {
Rectangle()
.fill((Color("background")))
.cornerRadius(50, corners: [.topLeft, .topRight])
.overlay{
VStack{
titleView
.padding(.top, 30)
contentsBox
Spacer()
buttonRow
}
.padding(.horizontal, 20)
.frame(maxWidth: 600)
}
}
// MARK: - BODY
var body: some View {
ZStack{
GeometryReader{ proxy in
coverImage
.ignoresSafeArea()
.frame(width: UIScreen.main.bounds.width, height: proxy.size.height * (animateCard ? 0.30 : 1.0))
.animation(.easeInOut(duration: 1), value: animateCard)
}
GeometryReader { proxy in
aboutButton
.frame(width: 30, height: 30)
.position(x: proxy.frame(in: .global).maxX - 30, y: proxy.frame(in: .global).minY)
}
GeometryReader{ proxy in
VStack(spacing: 0){
Spacer()
cardView
.frame(height: proxy.size.height * 0.8)
.offset(y: animateCard ? 0 : cardOffset)
.animation(.spring().delay(0.2), value: animateCard)
}
}
}
.id(id)
.blur(radius: shouldPresentSheet ? 5.0 : 0.0)
.brightness(shouldPresentSheet ? -0.2 : 0.0)
.animation(.easeIn, value: shouldPresentSheet)
.onAppear {
if !imagesLoaded {
unsplashModel.loadData(imageCount: quotes.count)
imagesLoaded = true
}
animateCard = true
}
.onChange(of: unsplashModel.dailyImageData) { _ in
if unsplashModel.dailyImageData.count == quotes.count {
updateDailyImages()
}
}
.onChange(of: scenePhase, perform: { newValue in
if newValue == .active {
id = .init()
}
})
.task {
NotificationManager.shared.scheduleNotifications(notificationText: quote.first?.title ?? "")
}
}
}
EDIT 2: Code updated with lorem ipsum's suggestion below (.id and onChange of scenePhase). However, this seems to interfere with the card animation in .onAppear { withAnimation { animateCard = true } }

SwiftUI How to Disable Button While Uploading Objects in an Array

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
}
}

I've searched and search SwiftUI Switch Case Menu Cycle?

I've found great content, But nothing too specific to my needs being swiftui is still new.
I need to support menu cycling with switch case in the latest swift and monterey for macos, no ios development. i need strings, variables, and custom graphics to make a menu title and current in need of up to 9 menus to cycle randomly from one to a random other without an if statement looping through all of the others first:
more info here: https://pastebin.com/VCnEmdBa
Additional information on needs:
I want to have the switch case cycle my nine menus, where i can be on any given one and the menu jump to the next random selection, right now it currently rotates in order no matter where i click.
import Foundation
import SwiftUI
import CoreData
import Combine
import PDFKit
import SceneKit
import WebKit
struct Cotharticren: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct Option: Hashable {
let title: String
let imageName: String
}
struct ContentView: View {
#State var currentOption = 0
let options: [Option] = [
.init(title: "DART Meadow", imageName: "sun.max.fill"),
.init(title: "Research", imageName: "flame"),
.init(title: "Navigation", imageName: "moon.stars.fill"),
.init(title: "Shelf", imageName: "archivebox"),
.init(title: "Chest" ,imageName: "shippingbox"),
.init(title: "Crate" ,imageName: "bonjour"),
.init(title: "Manufactoring", imageName: "gear"),
.init(title: "Warehouse", imageName: "archivebox.fill"),
.init(title: "Journal", imageName: "note.text"),
]
var body: some View {
VStack(alignment: .leading) {
HStack(alignment: .top) {
ScrollView( .vertical) {
NavigationView{
/*
List(1..<9)
{
Text("\($0)")
}
.listStyle(SidebarListStyle())
}
*/
ListView(options: options, currentSelection: $currentOption)
//Text(systemName: myItem.selectedImageName) + Text(myItem.whateverText)
switch (currentOption) {
case 1:
OrbitNodeView()
case 2:
ATM26NodeView()
case 3:
NozzleNodeView()
case 4:
EmptyView()
VStack(alignment: .center) {
Text("Chest")
.font(.largeTitle)
.bold()
.colorInvert()
}
case 5:
EmptyView()
VStack(alignment: .center) {
Text("Crate")
.font(.largeTitle)
.bold()
.colorInvert()
}
case 6:
EmptyView()
VStack(alignment: .center) {
Text("Manufactoring")
.font(.largeTitle)
.bold()
.colorInvert()
}
case 7:
EmptyView()
VStack(alignment: .center) {
Text("Warehouse")
.font(.largeTitle)
.bold()
.colorInvert()
}
case 8:
VStack(alignment: .center) {
Text("Journal")
.font(.largeTitle)
.bold()
.colorInvert()
.padding(.top, 60)
Image("articrenmeadowopacity")
.shadow(radius: 3)
WebView()
}
default:
MainView()
}
}.background(Color.white)
}
}
}
Spacer()
}
}
struct MainView: View {
var body: some View{
VStack(alignment: .leading) {
HStack(alignment: .bottom) {
Image("CotharticrenMainView")
.shadow(radius: 3)
}
}
.frame(width: 900, height: 800, alignment: .center)
Spacer()
}
}
struct ListView: View {
let options: [Option]
#Binding var currentSelection: Int
var body: some View{
VStack(alignment: .leading) {
HStack(alignment: .top) {
HStack(alignment: .top) {
VStack(alignment: .trailing) {
Circle()
.stroke(Color.init(red: 0.9, green: 0.95, blue: 0.2, opacity: 1), lineWidth: 2)
.alignmentGuide(HorizontalAlignment.myAlignment)
{ d in d[.leading] }
.alignmentGuide(VerticalAlignment.myAlignment)
{ d in d[.bottom] }
.frame(width: 50, height: 50)
Circle()
.stroke(Color.init(red: 0.25, green: 0.9, blue: 0.2, opacity: 1), lineWidth: 2)
.alignmentGuide(HorizontalAlignment.myAlignment)
{ d in d[.leading] }
.alignmentGuide(VerticalAlignment.myAlignment)
{ d in d[.bottom] }
.frame(width: 25, height: 25)
VStack(alignment: .leading) {
Circle()
.stroke(Color.init(red: 0.1, green: 0.1, blue: 1, opacity: 1), lineWidth: 2)
.alignmentGuide(HorizontalAlignment.myAlignment)
{ d in d[.leading] }
.alignmentGuide(VerticalAlignment.myAlignment)
{ d in d[.bottom] }
.frame(width: 75, height: 75)
}
}
}
HStack(alignment: .top) {
Image("DARTMeadowCSMwidthArtemis2by1")
.shadow(radius: 3)
.padding(.top, 10)
}
.padding(.top, 20)
.padding(.trailing, 10)
}.padding(.top, 20).padding(.leading, 10)
HStack(alignment: .center) {
VStack(alignment: .center) {
Image("arrow300")
HStack(alignment: .center) {
Text("You've never plotted an Edge?")
}
}
}.shadow(radius: 3)
VStack(alignment: .leading) {
let current = options[currentSelection]
ForEach(options, id: \.self) {option in
HStack {
Image(systemName: option.imageName)
//.resizable()
//.aspectRatio(contentMode: .fit)
.frame(width: 20)
Text(option.title)
.foregroundColor(current == option ? Color.blue : Color.white)
}
.padding(8)
.onTapGesture {
currentSelection += 1
if currentSelection == 9 {
currentSelection = 0
}
}
}
Spacer()
}.frame(width: 300, height: 800, alignment: .leading)
}
Spacer()
}
}
struct WebView: View {
var body: some View{
VStack(alignment: .leading) {
HStack(alignment: .bottom) {
}
}
.frame(width: 900, height: 800, alignment: .center)
Spacer()
}
}
You can add an Identifier to your Option class and use this for currentSelection, if you want to set an option, just set currentSelection to option.id:
Also:
1: If you want answers, it's best to format your code, before you post it (select in Xcode and ctrl+i should do it), so it's easy to read and understand
2: A minimal, reproducible example is not just posting your entire code, create an example, that contains only as much code as necessary to show the problem you're experiencing. The code I posted would be a better example, it will work without having to change anything. Your code includes references to objects that are not on here, so a possible helper would have to remove those, before he could even test your issue
here is a guide on how to create a minimal, reproducible example:
struct Option: Hashable, Identifiable {
// Identifier for Option !! MUST be unique
let id: Int
let title: String
let imageName: String
}
struct ContentView: View {
#State var currentOption: Int = 0
let options: [Option] = [
.init(id: 1, title: "DART Meadow", imageName: "sun.max.fill"),
.init(id: 2, title: "Research", imageName: "flame"),
.init(id: 3, title: "Navigation", imageName: "moon.stars.fill"),
]
var body: some View {
GeometryReader { geo in
HStack {
ListView(options: options, currentSelection: $currentOption)
.frame(width: geo.size.width / 2, height: geo.size.height)
switch (currentOption) {
case 1: Text("OrbitNodeView")
case 2: Text("ATM26NodeView")
case 3: Text("NozzleNodeView")
default: Text("MainView")
}
}
}
}
}
struct ListView: View {
let options: [Option]
#Binding var currentSelection: Int
var body: some View{
VStack(alignment: .leading) {
ForEach(options, id: \.self) {option in
HStack {
Image(systemName: option.imageName)
.frame(width: 20)
Text(option.title)
// Don't even have to use current = options[currentSelection] anymore:
.foregroundColor(currentSelection == option.id ? .accentColor : .primary)
}
.padding(8)
.onTapGesture {
// Set the currentSelection to the ID of the option
currentSelection = option.id
}
}
}
}
}

I'd like to use the navigation link.Newbie Wang is in the process of hair loss

I want to use Navigationlink. I've been a novice for 2 weeks since I started.I am currently learning SwiftUi.
I created "OnboredView" after watching YouTube, but I don't know how to connect "OnboredView" to "CountentView".
NavigationView(){
NavigationLink(destination: OnboardView())
I learned how to make it like this through YouTube, but I don't know what to do now. I put it here and there, but the red errors bother me.
Tell me how to connect "NavigationLink" by pressing the button on "CountentView".
I'd like to click "Chevron.Light" to move on to "OnboredView."And if possible, please let me know how I can get rid of the "onboard screen" on the second run?
I am not good at English.I'm sorry. I'm experiencing hair loss again.
import SwiftUI
struct ContentView: View {
#State private var animate: Bool = false
var body: some View {
ZStack{
ZStack{
Image("rogo1")
.resizable()
.frame(width: 75, height: 75)
.offset(y: animate ? -100 : 0)
}
ZStack{
Image("rogo2")
.resizable()
.frame(width: 75, height: 75)
.offset(y: animate ? -100 : 0)
}
VStack {
HStack {
Spacer()
Image("images (1)")
.resizable()
.frame(width: 300, height: 300)
.offset(x: animate ? 300 : 150, y: animate ? -300 : -150)
}
Spacer()
HStack {
Image("images (1)")
.resizable()
.frame(width: 400, height: 400)
.offset(x: animate ? -500 : -150, y: animate ? 500 : 150)
Spacer()
}
}
ZStack(alignment: .bottom){
GeometryReader { g in
VStack (alignment: .leading, spacing: 20){
Text("안녕하세요!")
.font(.title)
.fontWeight(.semibold)
.padding(.top, 20)
//인삿말과 회원가입
Text("기분 좋은 매일습관을 만들기 위한 앱 ( ) 입니다! 시간표와 더불어 루틴을 함께 할수
있도록 설계 되었습니다.저희 ( )와 함께 계획해봐요!")
.fontWeight(.medium)
.multilineTextAlignment(.center)//중앙으로 결집
.padding(5)
ZStack {
Button(action: {},label: {
Image(systemName: "chevron.right")
.font(.system(size:20, weight: .semibold))
.frame(width: 60, height: 60)
.foregroundColor(.black)
.background(Color.white)
.clipShape(Circle())
.overlay(
ZStack {
Circle()
.stroke(Color.black.opacity(0.04),lineWidth: 4)
Circle()
.trim(from: 0, to: 0.03)
.stroke(Color.white,lineWidth: 4)
.rotationEffect(.init(degrees: -40))
})
})
.padding(-10)
}
Spacer()
}
.frame(maxWidth: .infinity)
.padding(.horizontal, 30)
.background(Color.green)
.clipShape(CustomShape(leftCorner: .topLeft, rightCorner: .topRight,
radii: 20))
.offset(y: animate ? g.size.height : UIScreen.main.bounds.height)
}
}.frame(height: 275)
//여기까지 짤라도 됨 온보드
}
.frame(maxWidth: .infinity)
.edgesIgnoringSafeArea(.all)
.onAppear(perform: {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
withAnimation(Animation.easeOut(duration: 0.45)){
animate.toggle()
}
}
})
}
{
struct CustomShape: Shape {
var leftCorner: UIRectCorner
var rightCorner: UIRectCorner
var radii: CGFloat
func path(in rect: CGRect) -> Path {
let path = UIBezierPath(roundedRect: rect, byRoundingCorners:
[leftCorner,rightCorner], cornerRadii: CGSize(width: radii, height: radii))
return Path(path.cgPath)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
Group {
}
}
}
}
import SwiftUI
struct OnboardView: View {
#AppStorage("currentPage") var currentPage = 1
var body: some View {
if currentPage > totalPages {
Home()
}else{
WalkthroughScreen()
}
}
}
struct OnboardView_Previews: PreviewProvider {
static var previews: some View {
OnboardView()
}
}
struct Home: View {
var body: some View{
Text("welcome To Home!!!")
.font(.title)
.fontWeight(.heavy)
}
}
//..Walkthrough Screen..
struct WalkthroughScreen: View {
#AppStorage("currentPage") var currentPage = 1
var body: some View {
//For Slide Animation
ZStack{
//Changing Between Views..
if currentPage == 1 {
ScreenView(image: "image1", title: "Step1", detail: "", bgColor:
Color(.white))
//transition(.scale)영상에서는 넣었으나 오류가나서 사용하지 못함
}
if currentPage == 2 {
ScreenView(image: "image2", title: "Step2", detail: "", bgColor:
Color(.white))
}
if currentPage == 3 {
ScreenView(image: "image3", title: "Step3", detail: "아니 ㅡㅡ 이런 방법이 유레카",
bgColor: Color(.white))
}
}
.overlay(
Button(action: {
//changing views
withAnimation(.easeInOut){
if currentPage < totalPages {
currentPage += 1
}else{
currentPage = 1
//For app testing ONly
}
}
}, label: {
Image(systemName: "chevron.right")
.font(.system(size: 20, weight: .semibold))
.foregroundColor(.black)
.frame(width: 60, height: 60)
.clipShape(Circle())
//strclulat Slider
.overlay(
ZStack{
Circle()
.stroke(Color.black.opacity(0.04),lineWidth: 4
Circle()
.trim(from: 0, to: CGFloat(currentPage) /
CGFloat(totalPages))
.stroke(Color.green,lineWidth: 4)
.rotationEffect(.init(degrees: -99))
}
.padding(-15)
)
})
.padding(.bottom,20)
,alignment: .bottom
)
}
}
struct ScreenView: View {
var image: String
var title: String
var detail: String
var bgColor: Color
#AppStorage("currentPage") var currentPage = 1
var body: some View {
VStack(spacing:20){
HStack {
//Showing it only for first page..
if currentPage == 1{
Text("Hello Members!")
.font(.title)
.fontWeight(.semibold)
//Letter Spacing
.kerning(1.4)
}else{
//Back Butten..
Button(action: {
withAnimation(.easeInOut){
currentPage -= 1
}
}, label: {
Image(systemName: "chevron.left")
.foregroundColor(.white)
.padding(.vertical,10)
.padding(.horizontal)
.background(Color.black.opacity(0.4))
.cornerRadius(10)
})
}
Spacer()
Button(action: {
withAnimation(.easeInOut){
currentPage = 4
}
}, label: {
Text("Skip")//글자입력
.fontWeight(.semibold)//글자 폰트변경
.kerning(1.2)//글자간 간격 조정
})
}
.foregroundColor(.black)//그라운드 컬러 변경
.padding()
Spacer(minLength: 0)//수평,수직 줄바꿈
Image(image)//이미지 삽입
.resizable()//크기 확대
.aspectRatio(contentMode: .fit)//이미지 크기
Text(title)
.font(.title)//폰트 크기변경
.fontWeight(.bold)//폰트 두께 변경
.foregroundColor(.black)//색깔 변경
.padding(.top)
//Change with your Own Thing..
Text(detail)
.fontWeight(.semibold)
.kerning(1.3)//자간조정
.multilineTextAlignment(.center)//텍스트를 중앙으로 결집
Spacer(minLength: 220)//minimun Spacing When phone is reducing수직위치 조정
}
.background(bgColor.cornerRadius(10).ignoresSafeArea())
}
}
var totalPages = 3

Use data to define amount of objects in Array

I'm working on a checklist in SwiftUI and want to define the amount of checkboxes in my data. So for example, in case of fruit, the user should tick 3 checkboxes whereas water requires more checks because you need to drink more glasses of water a day.
I'm using "for each" in a HStack to make an array of checkboxes:
HStack {
ForEach(0 ..< 3) { index in
VStack {
Button(action: {
self.checked[index].toggle()
})
I want to replace the ( 0..<3) with checklist.steps which is where the amount (INT) is defined.
let checklistData = [
Checklist(title: "Fruit", instruction: "1 vakje = 1 stuk of 100g", steps: Int(4)),
Is there a way to define the amount of checkboxes in my data?
Thanks in advance <3
Solution
The following code worked for me:
HStack {
ForEach(0 ..< checklist.steps) { index in
VStack {
Button(action: {
self.checked[index].toggle()
})
i am not sure whether you want this?
HStack {
ForEach(0 ..< checklistData[x].steps) { index in
VStack {
Button(action: {
self.checked[index].toggle()
})
ok, here my better answer, thanks for your code:
struct ChecklistView: View {
var checklist: Checklist
#State var currentProgress: Float = 0.0
#State var checked = \[false, false, false, false\]
var body: some View {
ZStack(alignment: .leading) {
// RoundedRectangle(cornerRadius: 20)
// .foregroundColor(.red).opacity(0.5)
// .frame(width: 200)
VStack {
HStack(alignment: .top) {
checklist.image
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 40, height: 40)
.padding(.trailing)
VStack(alignment: .leading) {
Text(checklist.title)
.font(.system(size: 16, weight: .medium))
.padding(.bottom, 4)
Text(checklist.instruction.uppercased()).font(.system(size: 12))
HStack {
ForEach(0 ..< checklist.steps) { index in
VStack {
Button(action: {
self.checked\[index\].toggle()
}) {
ZStack {
RoundedRectangle(cornerRadius: 8)
.foregroundColor(self.checked\[index\] ? Color("LightGreen") : .gray )
.frame(width: 40, height: 40)
Image(systemName: self.checked\[index\] ? "checkmark" : "plus")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 16, height: 16)
.foregroundColor(self.checked\[index\] ? .white : .white)
}
}
}
}
}
}
}.frame(width: 350, alignment: .leading)
}
}
.frame(width: 350)
.padding(.bottom, 16)
.cornerRadius(8)
.padding(.top, 20)
}
}
// MARK: data checklist
struct Checklist: Identifiable, Hashable {
func hash(into hasher: inout Hasher) {
hasher.combine(id)
hasher.combine(title)
}
var id = UUID().uuidString
var title: String
var instruction: String
var image: Image
var steps: Int
}
struct ContentView: View {
let checklistData = \[
Checklist(title: "Fruit", instruction: "1 vakje = 1 stuk of 100g", image: Image("kiwi"), steps: Int(4)),
Checklist(title: "Fruit", instruction: "1 vakje = 1 stuk of 100g", image: Image("kiwi"), steps: Int(2))
\]
var body: some View {
List(checklistData, id: \.self) { checkList in
ChecklistView(checklist: checkList)
}
}
}