Go to SwiftUi View File "Home" with button - ios

I'm trying to go to my SwiftUi View File Home by clicking my button in iOS 16:
I already read Apple documentation and searched in Google and YouTube but I didn't got the answer.
Here is my code:
import SwiftUI
import CoreData
struct ContentView: View {
var body: some View {
VStack (alignment: .leading) {
Text("Welcome To").font(.system(size: 45)).fontWeight(.heavy).foregroundColor(.primary)
Text("Pumping Fitness").font(.system(size: 45)).fontWeight(.heavy).gradientForeground(colors: [.red, .yellow])
Spacer()
VStack (alignment: .leading, spacing: 24) {
HStack (alignment: .center, spacing: 20)
{
Image(systemName: "dumbbell.fill").resizable().frame(width: 40, height: 30).gradientForeground(colors: [.red, .orange])
VStack (alignment: .leading, spacing: 4) {
Text("Track your workouts").bold().font(.system(size: 22)).padding(.top, 10.0)
Text("Easily track your progress during you are working out").font(.subheadline).padding(.bottom, 10.0)
}
}
HStack (alignment: .center, spacing: 20)
{
Image(systemName: "timer").resizable().frame(width: 40, height: 40).gradientForeground(colors: [.red, .orange])
VStack (alignment: .leading, spacing: 4) {
Text("Auto rest timer").bold().font(.system(size: 22))
Text("Start your rest time with one single tap").font(.subheadline).padding(.bottom, 10.0)
}
}
HStack (alignment: .center, spacing: 20)
{
Image(systemName: "figure.run").resizable().frame(width: 40, height: 50).gradientForeground(colors: [.red, .orange])
VStack (alignment: .leading, spacing: 4) {
Text("Add your own exercises").bold().font(.system(size: 22))
Text("Create your own type of exercises at a glance").font(.subheadline)
}
}
}
Spacer()
Spacer()
//HStack creado para poder alinear el boton al centro.
HStack(alignment: .center) {
Button(action: {} ) {
Text("Start Pumping").fontWeight(.black).foregroundColor(.white)
}
.padding()
.frame(width: 280, height: 60)
.background(LinearGradient(gradient: Gradient(colors: [Color.red, Color.yellow]), startPoint: .leading, endPoint: .trailing))
.cornerRadius(17)
}.padding(.leading)
}.padding(.all, 40)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}
extension View {
public func gradientForeground(colors: [Color]) -> some View {
self.overlay(LinearGradient(gradient: .init(colors: colors), startPoint: .topLeading, endPoint: .topTrailing))
.mask(self)
}
}
Do you know how can I do it? All the YouTube videos I saw were using a list, and I want to show this "welcome page" then go to my home page.

You need to refactor your code a bit. Use ContentView as your module for navigationStack. Separate ContentView code to WelcomeView and use it as Follows:
struct WelcomeView: View {
#Binding var gotoSomewhere: Bool // I recommend you to read some articles about #Sate and #Binding property wrappers
var body: some View {
VStack (alignment: .leading) {
Text("Welcome To").font(.system(size: 45)).fontWeight(.heavy).foregroundColor(.primary)
Text("Pumping Fitness").font(.system(size: 45)).fontWeight(.heavy).gradientForeground(colors: [.red, .yellow])
Spacer()
VStack (alignment: .leading, spacing: 24) {
HStack (alignment: .center, spacing: 20)
{
Image(systemName: "dumbbell.fill").resizable().frame(width: 40, height: 30).gradientForeground(colors: [.red, .orange])
VStack (alignment: .leading, spacing: 4) {
Text("Track your workouts").bold().font(.system(size: 22)).padding(.top, 10.0)
Text("Easily track your progress during you are working out").font(.subheadline).padding(.bottom, 10.0)
}
}
HStack (alignment: .center, spacing: 20)
{
Image(systemName: "timer").resizable().frame(width: 40, height: 40).gradientForeground(colors: [.red, .orange])
VStack (alignment: .leading, spacing: 4) {
Text("Auto rest timer").bold().font(.system(size: 22))
Text("Start your rest time with one single tap").font(.subheadline).padding(.bottom, 10.0)
}
}
HStack (alignment: .center, spacing: 20)
{
Image(systemName: "figure.run").resizable().frame(width: 40, height: 50).gradientForeground(colors: [.red, .orange])
VStack (alignment: .leading, spacing: 4) {
Text("Add your own exercises").bold().font(.system(size: 22))
Text("Create your own type of exercises at a glance").font(.subheadline)
}
}
}
Spacer()
Spacer()
//HStack creado para poder alinear el boton al centro.
HStack(alignment: .center) {
Button(action: {
print("Tap Tap")
gotoSomewhere = true
} ) {
Text("Start Pumping").fontWeight(.black).foregroundColor(.white)
}
.padding()
.frame(width: 280, height: 60)
.background(LinearGradient(gradient: Gradient(colors: [Color.red, Color.yellow]), startPoint: .leading, endPoint: .trailing))
.cornerRadius(17)
}.padding(.leading)
}.padding(.all, 40)
}
}
Then update your contentView with NavigationStack like this:
import SwiftUI
import CoreData
struct ContentView: View {
#State var gotoHomePage: Bool = false // this is important
var body: some View {
NavigationStack {
VStack {
WelcomeView(gotoSomewhere: $gotoHomePage)
NavigationLink(isActive: $gotoHomePage) {
HomeView() // This is your Home View you want to navigate to
.navigationBarBackButtonHidden(true) // if you want a back button then pass false or comment this modifier
} label: {
EmptyView()
}
}
.navigationBarHidden(true)
}
}
}
Hope this helps.

If you're looking to use a navigation stack, then you'd want to wrap your View body inside a NavigationView. If you're targeting iOS 16+ you should use NavigationStack instead (https://developer.apple.com/documentation/swiftui/migrating-to-new-navigation-types). Hacking with Swift also has an article on NavigationStack.
There is some more useful info on NavigationView on the Hacking with Swift blog here, as well as other resources you should be able to find online. There are some examples there you could use in your situations such as:
struct ContentView: View {
#State private var isShowingDetailView = false
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: Text("Second View"), isActive: $isShowingDetailView) { EmptyView() }
Button("Tap to show detail") {
self.isShowingDetailView = true
}
}
.navigationTitle("Navigation")
}
}
}
This question is pretty similar: How to show NavigationLink as a button in SwiftUI

Related

How can I scale notification text based on length?

I am making a notification feed where you can view the WHOLE comment a user posts on a blog but I am unable to get the whole multi-line comment to show without affecting the spacing/alignment of elements to match the other notifications.
I was able to make the whole comment show by typing in a constant .frame(height: 100) modifier but then EVERY comment notification has that sized of frame.
Is there a way to make the below VStack scale it's frame dynamically based on the comment length or is there a better way based on my code??
Thank you!
struct CommentNotificationCell: View {
#EnvironmentObject var session: SessionStore
var activity: CommentActivity?
var body: some View {
HStack(spacing: 10) {
if let activity = activity {
VStack {
KFImage(URL(string: "\(url)" )!)
Spacer()
}
// Should I scale this VStack's frame height dynamically based
// on the length of the comment..?
VStack(alignment: .leading, spacing: 3) {
HStack(spacing: 3) {
Text(activity.username)
.font(.caption)
Text("commented on your post:")
.font(.caption)
}
Text(activity.comment)
.font(.caption)
Text(activity.createdAt.timeAgoDisplay())
.font(.caption)
}.padding(.leading, 10)
Spacer()
VStack {
ZStack {
Image(systemName: "bubble.left.fill")
.font(.system(size: 13, weight: .regular))
.foregroundColor(.blue)
.zIndex(1)
.offset(x: -25,y: -20)
KFImage(URL(string: "\(url)" )!)
.zIndex(0)
}
Spacer()
}
}
}
}
}
I think your code is already doing that – scaling dynamically based on text length. I cleaned it up a little and for me it works. Is there some other place where you set a specific .frame?
struct ContentView: View {
#State private var test = false
let username = "funnytest"
let shortComment = "kjfdglkj ewoirewoi oikdjsfgdlsfgj 0ewiropsdisg"
let longComment = "kjfdglkj ewoirewoi oikdjsfgdlsfgj 0ewiropsdisg poksaf#ldsifsgali oeirpo dgiodfig odfi ofdgifgpoüi fhfdi mfoidgho miohgfm ogifhogif hiogfh fgihi oogihofgi hofgiho fgopihfgoih pfdgihdfg podfgihofgiho po fdgdfiopugiouü"
var body: some View {
VStack {
CommentNotificationCell(username: username, comment: shortComment)
CommentNotificationCell(username: username, comment: longComment)
}
}
}
struct CommentNotificationCell: View {
var username: String
var comment: String
var body: some View {
HStack(spacing: 10) {
Image(systemName: "person.circle")
.resizable().scaledToFit().frame(width: 60)
VStack(alignment: .leading, spacing: 3) {
HStack(spacing: 3) {
Text(username)
.font(.caption).bold()
Text("commented on your post:")
.font(.caption)
}
Text(comment)
.font(.caption)
Text("8 hours ago")
.font(.caption)
}.padding(.leading, 10)
Spacer()
ZStack {
Image(systemName: "bubble.left.fill")
.font(.system(size: 13, weight: .regular))
.foregroundColor(.blue)
.zIndex(1)
.offset(x: -25,y: -20)
// Image("URL(string: "\(url)" )!")
// .zIndex(0)
}
}
}
}
I found out the fix was to remove the Spacer() in the first VStack
VStack {
KFImage(URL(string: "\(url)" )!)
}

Navigation View not working properly in SwiftUI on iPad

I am trying to create an navigation view that works on both iPhone and iPad. Currently I have it working on the iPhone however when running it on the iPad the navigation view doesnt present my main view properly. See below:
This is when I load the app
If I press products (top left) it opens up the products tab.
When I click on a product it goes to this screen
If I click Product 1 (seen on 3rd image) It opens all the details into another navigation bar.
What I am trying to achieve is that image 4 isn't in a navigation tab and instead it is the full screen. I tried removing NavigationView from my code which seems to fix the problem and makes it full screen. However, I then lose the navigation view buttons to allow the user to view other products.
Here is a shortened version my code (without all the text/image details):
var body: some View {
NavigationView {
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .center, spacing: 20) {
ProductHeaderView(product: product)
VStack(alignment: .leading, spacing: 15) {
Text(product.title)
.font(.largeTitle)
.fontWeight(.heavy)
.foregroundColor(product.gradientColors[1])
Text(product.headline)
.font(.headline)
.multilineTextAlignment(.leading)
}
.padding(.horizontal, 20)
.frame(maxWidth: 640, alignment: .center)
}
.navigationBarTitle(product.title, displayMode: .inline)
.navigationBarHidden(true)
}
.edgesIgnoringSafeArea(.top)
}
}
}
Thank you in advance for your help :)
EDIT:
Here is the ProductHeaderView.swift code:
var body: some View {
ZStack {
LinearGradient(gradient: Gradient(colors: product.gradientColors), startPoint: .topLeading, endPoint: .bottomTrailing)
TabView{
ForEach(0..<product.images.count, id: \.self) { item in
Image(product.images[item])
.resizable()
.scaledToFit()
.shadow(color: Color(red: 0, green: 0, blue: 0, opacity: 0.15), radius: 8, x: 6, y: 8)
.scaleEffect(isAnimatingImage ? 1.0 : 0.6)
}//: FOR LOOP
}//: TAB VIEW
.tabViewStyle(PageTabViewStyle())
.padding(.vertical, 0)
} //: ZSTACK
.frame(height: 414)
.onAppear(){
withAnimation(.easeOut(duration: 0.5)){
isAnimatingImage = true
}
}
}
Example project: https://github.com/spoax94/productsMinimal.git
Just add this line as a modifier in your NavigationView:
.navigationViewStyle(StackNavigationViewStyle())
As I commented there should be only one NavigationView, so here fixed ProductDetailView with removed redundant NavigationView.
Tested with Xcode 12
struct ProductDetailView: View {
var product: Product
var products: [Product] = productData
#State var showingPreview = false
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .center, spacing: 20) {
ProductHeaderView(product: product)
VStack(alignment: .leading, spacing: 15) {
Text(product.title)
.font(.largeTitle)
.fontWeight(.heavy)
Text(product.headline)
.font(.headline)
.multilineTextAlignment(.leading)
Text("Learn More About \(product.title)".uppercased())
.fontWeight(.bold)
.padding(0)
Text(product.description)
.multilineTextAlignment(.leading)
.padding(.bottom, 10)
}
.padding(.horizontal, 20)
.frame(maxWidth: 640, alignment: .center)
}
.navigationBarTitle(product.title, displayMode: .inline)
.navigationBarHidden(true)
}
.edgesIgnoringSafeArea(.top)
}
}
I figured out the problem. I removed the navigationView and also the 2 lines
.navigationBarTitle(product.title, displayMode: .inline)
.navigationBarHidden(true)
As this was hiding the navigation buttons at the top of my view.

SwiftUI list not showing all items

I am trying to show a list of side-scrolling list in iOS. Some of the lists show but others don't, even though they all have data. I put a debug point and see that the ForEach with EventItemView is called for every section. I am unsure of what I am doing wrong.
EventScreen
struct EventScreen: View {
#State
var currentPage: Int = 0
var viewControllers =
IncentiveSource().getFeaturedIncentives().map({ incentive in
UIHostingController(rootView: EventFeatureView(event: incentive.toEvent()))
})
var response: [EventSection] = IncentiveSource().getIncentives().sections.toEventSections()
var body: some View {
NavigationView {
List {
EventViewController(controllers: self.viewControllers, currentPage: self.$currentPage)
.listRowInsets(EdgeInsets())
.frame(height: 600)
ForEach(self.response) { section in
EventSectionView(eventSection: section)
}
}
.navigationBarTitle(Text("Events").foregroundColor(Color.black), displayMode: .inline)
}
}
}
EventSectionView
struct EventSectionView: View {
var eventSection: EventSection
var body: some View {
VStack(alignment: .leading) {
SectionTextView(text: eventSection.category.typeName())
.frame(alignment: .leading)
ScrollView(.horizontal, showsIndicators: true) {
HStack(alignment: .top, spacing: 0) {
ForEach(self.eventSection.events) { event in
EventItemView(event: event)
}
}
}
}
}
}
struct SectionTextView: View {
var text: String
var body: some View {
return Text(text)
.bold()
.font(.system(size: 18, weight: .heavy))
.foregroundColor(Color(ColorTheme.brandBlue.value))
.padding(.bottom, 4)
}
}
EventItemView
struct EventItemView: View {
var event: Event
var body: some View {
VStack {
Color.red
.frame(width: 100, height: 100, alignment: .center)
.cornerRadius(5)
Text(event.title)
.bold()
.frame(width: 100, alignment: .leading)
.foregroundColor(.white)
.font(.system(size: 10))
Text(event.date)
.frame(width: 100, alignment: .leading)
.foregroundColor(.white)
.font(.system(size: 10))
}
.padding(.trailing, 8)
}
}
It needs to make each horizontal scroller in EventSectionView unique like below
struct EventSectionView: View {
var eventSection: EventSection
var body: some View {
VStack(alignment: .leading) {
SectionTextView(text: eventSection.category.typeName())
.frame(alignment: .leading)
ScrollView(.horizontal, showsIndicators: true) {
HStack(alignment: .top, spacing: 0) {
ForEach(self.eventSection.events) { event in
EventItemView(event: event)
}
}
}.id(UUID().uuidString() // << unique
}
}
}

How to make a VStack Clickable to show alert in SwiftUI

I have a VStack with image and a text, I need to make it clickable so I can show and alert to the user. I have tried onTapGesture method but I'm able to print the statement but alert is not showing up.
Is there any alternate way to get the solution?
I have added the whole swift file for your reference
Code
#State private var hasOneConnected: Bool = false
#State private var showConnectionAlert = false
private var connectionAlert: Alert {
Alert.init(title: Text("Oops!"), message: Text("There is an error."), dismissButton: .default(Text("OK")))
}
var body: some View {
VStack(alignment: .center, spacing: 0) {
// MARK: - Header
VStack(alignment: .center, spacing: 0) {
Spacer().frame(height: 55)
HStack(alignment: .center, spacing: 25) {
Spacer()
Image("Logo")
Spacer(minLength: 5)
Text("Zone Control")
.foregroundColor(.white)
Spacer()
}
Spacer()
}
.frame(height: 100, alignment: .center)
.background(
LinearGradient(
gradient: Gradient(colors: [Color.Heat.primary, Color.Heat.primary.opacity(0.8), Color.Heat.primary.opacity(0.5)]),
startPoint: .top, endPoint: .bottom
)
)
// MARK: - Menu Bar
HStack(alignment: .center, spacing: 10) {
Spacer().frame(maxWidth: 20)
HStack(alignment: .center, spacing: 10) {
Image("batteryModule")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 8)
Text("Pod Status")
.font(.caption)
.foregroundColor(.white)
}
Spacer()
VStack(alignment: .center, spacing: 4) {
Image(self.hasConnected ? "bluetoothConnected" : "bluetoothNotConnected")
Text(self.hasConnected ? "Connected" : "Disconnected")
.font(.caption)
.foregroundColor(.white)
}
.frame(width: 80)
VStack(alignment: .center, spacing: 4) {
Image("batteryStatus")
Text("\(self.batteryLevel)%")
.font(.caption)
.foregroundColor(.white)
}
.frame(width: 60)
Spacer().frame(maxWidth: 10)
}
.padding()
.background(Color.black)
}
.statusBar(hidden: true)
.edgesIgnoringSafeArea(.all)
.onAppear(perform: {
UserDefaults.standard.set(true, forKey: "onboarding")
self.update()
})
.onReceive(self.skiinBLE.objectWillChange) { _ in
self.update()
}
}
func update() {
self.hasOneConnected = self.topLayer.cbPeripheral?.state != .disconnected
self.batteryLevel = self.topLayer.moduleInformation?.batteryLevel.rawValue ?? 0
}
For making empty containers tappable (for example Spacer()), you should use .contentShape(Rectangle()) modifier:
VStack(spacing: 4) {
Image(systemName: "antenna.radiowaves.left.and.right")
Text("Connected")
Spacer()
}
.contentShape(Rectangle())
.onTapGesture {
print("The whole VStack is tappable now!")
}
I have simplified your code to just bare essentials. You don't really need to add a tap gesture, you can wrap the whole element (e.g. a VStack) in a Button and handle triggering the alert from there. Important bit is to remember to set showConnectionAlert back to false when the user taps OK on the Alert. The side effect of wrapping it in a Button is that everything inside will be rendered in the tint colour. That's why I have applied .foregroundCololor() to the VStack (with some images you might also have to add .renderingMode(.original) modifier):
struct ContentView: View {
#State private var showConnectionAlert = false
var body: some View {
Button(action: { self.showConnectionAlert = true }) {
VStack(spacing: 4) {
Image(systemName: "antenna.radiowaves.left.and.right")
Text("Connected")
} .foregroundColor(.primary)
}
.alert(isPresented: $showConnectionAlert) {
Alert(title: Text("Nice"),
message: Text("The alert is showing!"),
dismissButton: Alert.Button.default(Text("OK"),
action: { self.showConnectionAlert = false }))
}
}
}

Swift UI Creating List Bubble Effect

I am trying to recreate the following list effect in SwiftUI,
List(){
TaskRowComponent(coreRouter: CoreRouter())
}
.listRowBackground(ColorScheme().field())
.cornerRadius(10)
.padding()
import SwiftUI
struct TaskRowComponent: View{
#ObservedObject var coreRouter: CoreRouter;
var body: some View {
VStack{
Text("This is a row!")
}
.listRowBackground(Color.green)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 80, maxHeight: 80, alignment: .leading)
}
}
I am trying to piece together how to create this effect where the two rows are stacked on top of each other, I do not want them to be packed how list view currently does it. I tried to add padding but it doesn't seem to work, any help would be much appreciated.
Something like this should get you going:
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack {
VStack {
List {
ForEach([1, 2], id: \.self) { item in
ZStack {
Rectangle()
.blendMode(.overlay)
.frame(height: 100)
.background(LinearGradient(gradient: Gradient(colors: [Color.blue, Color.purple]), startPoint: .leading, endPoint: .trailing))
.cornerRadius(9)
HStack {
VStack {
Text("Bikram Sinkemana")
.font(.system(size: 15))
.foregroundColor(.white)
Text("Kiran Regmi")
.font(.system(size: 15))
.foregroundColor(.white)
}
Text("3:1")
.font(.system(size: 30))
.foregroundColor(.white)
VStack {
Text("Bikram Sinkemana")
.font(.system(size: 15))
.foregroundColor(.white)
Text("Sagun Karanjit")
.font(.system(size: 15))
.foregroundColor(.white)
}
}
}
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Resources