I'm trying to add a new view inside the Scroll View that contains a button every time that I click in the blue button in the bottom
]1
Here i create the scroll view with 2 buttons, and want to add more after I click in the button on the right
HStack{
ScrollView(.horizontal, content: {
HStack{
PageStep()
PageStep()
}
})
Button(action: {
self.addNewStep = true
}) {
Image(systemName: "plus.square")
.frame(width: 75, height: 75)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.blue, lineWidth: 5)
)
}.buttonStyle(PlainButtonStyle()).padding(.trailing, 10)
}
.padding(.leading, 10)
.frame(minWidth: 0, maxWidth: UIScreen.main.bounds.width, minHeight: 80, alignment: .bottom)
.foregroundColor(Color.blue)
struct PageStep: View {
var stepPossition = String()
var body: some View {
Button(action: {
print("Entrou")
}){
Rectangle()
.fill(Color.red)
.frame(width: 75, height: 75)
.cornerRadius(10)
}
}
}
Here is possible approach. Tested with Xcode 11.4.
struct ContentView: View {
#State private var steps = 2 // pages counter
var body: some View {
HStack{
ScrollView(.horizontal, content: {
HStack{
// create available pages
ForEach(0..<steps, id: \.self) { i in
PageStep(stepPossition: "\(i)").id(i) // inject
}
}
})
Button(action: {
self.steps += 1 // << add next page
}) {
Image(systemName: "plus.square")
.frame(width: 75, height: 75)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.blue, lineWidth: 5)
)
}.buttonStyle(PlainButtonStyle()).padding(.trailing, 10)
}
.padding(.leading, 10)
.frame(minWidth: 0, maxWidth: UIScreen.main.bounds.width, minHeight: 80, alignment: .bottom)
.foregroundColor(Color.blue)
}
}
struct PageStep: View {
var stepPossition: String
var body: some View {
Button(action: {
print("Entrou")
}){
Rectangle()
.fill(Color.red)
.frame(width: 75, height: 75)
.cornerRadius(10)
}
}
}
Related
I am seeing an odd crash with some beta testers on TestFlight. It seems to be at app startup, but prior to the point where we are making any network calls. There does not seem to be a lot on the internet regarding this, and symbolication isn't helping much either. Just curious if anyone has run into this before and what they did about it? I have had it happen on two tester's devices and I have never been able to recreate the issue locally.
For a bit of context, I am creating a list view on my main screen with a LazyVGrid that contains a LazyHGrid inside of it that are both filled using published vars from my viewmodel, though this seems to happen prior to any of those being created.
Thanks for any ideas / help
Edit: Some more details / actual code:
The view with the grids:
import SwiftUI
import Kingfisher
struct FeaturedView: View {
#EnvironmentObject var viewRouter: ViewRouter
#EnvironmentObject var tabRouter : TabRouter
#StateObject var loadingViewModel = LoadingViewModel()
private let imageProcessor = SVGImgProcessor()
private let playerManager = PlayerManager.shared
private var gridItemLayout = [GridItem(.flexible())]
let userDefaults = UserDefaults.standard
var body: some View {
let padding: CGFloat = 20
let paddingHStack: CGFloat = 25
GeometryReader { geometry in
ZStack(alignment: .top){
Color(hex:"#00091C").edgesIgnoringSafeArea(.all)
VStack {
HStack {
HStack {
Text("Hello, \(loadingViewModel.name)")
.frame(alignment: .leading)
.multilineTextAlignment(.center)
.font(Font.custom("poppins-medium", size: 20))
.foregroundColor(Color(hex:"#667C95"))
.padding(.leading, 15)
.padding(.top, 15)
.padding(.bottom, 15)
Image("PremiumStar")
.resizable()
.frame(width: 15.0, height: 15.0)
.opacity(userDefaults.isSubscriber() ? 1 : 0)
}
Spacer()
Button(action: {
print("Settings Clicked")
viewRouter.currentPage = .settingsFlow
}) {
Image("Settings")
.resizable()
.frame(width: 22.0, height: 22.0)
.padding(15)
}
}
ScrollView(showsIndicators: false) {
VStack(spacing: 10) {
LazyVGrid(columns: gridItemLayout, spacing: 17) {
ForEach(loadingViewModel.getCategories()) { category in
Text(category.title)
.foregroundColor(.white)
.font(Font.custom("poppins-bold", size: 30))
ZStack {
KFImage(URL(string: (category.background?.svg!)!))
.resizable()
.setProcessor(imageProcessor)
.frame(width: geometry.size.width - padding, height: 175, alignment: .topLeading)
.aspectRatio(contentMode: .fill)
.clipped()
.cornerRadius(5)
ScrollView(.horizontal, showsIndicators: false) {
LazyHGrid(rows: gridItemLayout, spacing: 20){
ForEach(loadingViewModel.getSoundsForCategory(category: category.key)) { sound in
Button(action: {
playerManager.play(sound: sound)
}) {
VStack {
ZStack{
RoundedRectangle(cornerRadius: 5).frame(width: 90, height: 90)
.foregroundColor(Color.black)
.opacity(0.85)
ZStack(alignment:.bottomTrailing){
KFImage(URL(string: sound.icon.png!)!)
.resizable()
.renderingMode(.template)
.foregroundColor(.white)
.frame(width: 75, height: 75)
.aspectRatio(contentMode: .fill)
.clipped()
Image("LockIcon")
.frame(width: 12, height: 12, alignment: .bottomTrailing)
.aspectRatio(contentMode: .fill)
.clipped()
.hidden(loadingViewModel.isSubscriber || sound.tier != 2)
}
}
Text(sound.name)
.foregroundColor(.white)
.font(Font.custom("poppins-regular", size: 12))
.lineLimit(1)
.frame(minWidth: 0, idealWidth: 90, maxWidth: 100, alignment: .center)
}
}
}
}.padding(.horizontal)
}
.frame(width: geometry.size.width - paddingHStack, height: 175, alignment: .topLeading)
}
Button("Explore All"){
print("Explore All \(category.title) Tapped")
tabRouter.categoryKey = category.key
tabRouter.hasChanged = true
tabRouter.currentTab = 1
}
.font(Font.custom("poppins-bold", size: 15))
.foregroundColor(Color.white)
}
}.padding(.bottom, 120)
}
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
FeaturedView()
}
}
That is loaded by this tab view, which goes between two screens which share the same viewModel / load the same data
// MARK: Tab View
var body: some View {
ZStack{
TabView(selection: $selectedTab){
FeaturedView()
.environmentObject(tabRouter)
.tabItem{
Label("FEATURED", image: "Featured")
}.tag(0)
.overlay(
VStack {
if (showOverlay && !isExpanded){
playView
} else {
EmptyView()
}
}
)
SoundView()
.environmentObject(tabRouter)
.tabItem{
Label("SOUNDS", image: "Sounds")
}.tag(1)
.overlay(
VStack {
if (showOverlay && !isExpanded){
playView
} else {
EmptyView()
}
}
)
}
}
}
I am complete beginner with SwiftUI and I can't wrap my head around how to connect these images with views that represents lines. Now I simply have 3 VStacks with image and text and put them into a HStack, but don't know how to connect these images with a line shown in red in the picture I attached. Note that there's some space between the line and the image. I need general direction and some hints, full working code not necessary.
Thank you.
How's this?
In SwiftUI, you use HStacks and VStacks to stack your Views. For the red line, a Rectangle should do. Here's the code:
struct ContentView: View {
var body: some View {
HStack { /// horizontal stack
VStack {
Image(systemName: "face.smiling")
.font(.system(size: 80))
.padding()
.border(Color.black, width: 5)
Text("Text TEXTEXT")
}
Rectangle()
.fill(Color.red)
.frame(height: 5)
VStack {
Image(systemName: "face.smiling")
.font(.system(size: 80))
.padding()
.border(Color.black, width: 5)
Text("Text TEXTEXT")
}
Rectangle()
.fill(Color.red)
.frame(height: 5)
VStack {
Image(systemName: "face.smiling")
.font(.system(size: 80))
.padding()
.border(Color.black, width: 5)
Text("Text TEXTEXT")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.previewLayout(.fixed(width: 800, height: 200))
}
}
Version 1.0.0
I decided to give my answer which is same like aheze answer with this difference that you can have CustomVerticalAlignment as well! As I see in your Image in question you want that also:
with CustomVerticalAlignment: In center!
without CustomVerticalAlignment: off center!
import SwiftUI
struct ContentView: View {
var body: some View {
HStack(alignment: .customVerticalAlignment) {
VStack {
Image(systemName: "star")
.resizable()
.scaledToFit()
.frame(width: 50, height: 50, alignment: .center)
.padding()
.border(Color.black, width: 5)
.alignmentGuide(.customVerticalAlignment) { d in d[VerticalAlignment.center] }
Text("Text")
}
Capsule()
.fill(Color.red)
.frame(height: 5)
.alignmentGuide(.customVerticalAlignment) { d in d[VerticalAlignment.center] }
VStack {
Image(systemName: "star")
.resizable()
.scaledToFit()
.frame(width: 50, height: 50, alignment: .center)
.padding()
.border(Color.black, width: 5)
.alignmentGuide(.customVerticalAlignment) { d in d[VerticalAlignment.center] }
Text("Text")
}
Capsule()
.fill(Color.red)
.frame(height: 5)
.alignmentGuide(.customVerticalAlignment) { d in d[VerticalAlignment.center] }
VStack {
Image(systemName: "star")
.resizable()
.scaledToFit()
.frame(width: 50, height: 50, alignment: .center)
.padding()
.border(Color.black, width: 5)
.alignmentGuide(.customVerticalAlignment) { d in d[VerticalAlignment.center] }
Text("Text")
}
}
.padding()
}
}
extension VerticalAlignment {
struct CustomVerticalAlignment: AlignmentID {
static func defaultValue(in d: ViewDimensions) -> CGFloat {
d[VerticalAlignment.center]
}
}
static let customVerticalAlignment = VerticalAlignment(CustomVerticalAlignment.self)
}
Update Version 2.0.0
About this version: I would say it does the same job of version 1.0.0 in less code and also Text and Line are not depending on VStack or eachother any moere!
import SwiftUI
struct ContentView: View {
var body: some View {
HStack {
image.overlay(text.offset(y: 40), alignment: .bottom)
capsule
image.overlay(text.offset(y: 40), alignment: .bottom)
capsule
image.overlay(text.offset(y: 40), alignment: .bottom)
}
.padding(50)
}
var image: some View {
return Image(systemName: "star.fill")
.resizable()
.scaledToFit()
.padding(10)
.shadow(radius: 10)
.frame(width: 50, height: 50, alignment: .center)
.foregroundColor(Color.red)
.background(Color.yellow)
.border(Color.black, width: 5)
}
var capsule: some View {
return Capsule()
.fill(Color.red)
.frame(height: 5)
}
var text: some View {
return Text("Hello World!")
.lineLimit(1)
.fixedSize()
}
}
You could define a Shape that represents your line.
I used the spacing parameter of HStack to do the spacing:
struct MyLine : Shape {
func path(in rect: CGRect) -> Path {
Path { path in
path.move(to: CGPoint(x: 0, y: rect.midY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.midY))
}
}
}
struct ContentView: View {
var body: some View {
HStack(spacing: 10) {
VStack {
Image(systemName: "pencil")
Text("Label")
}
MyLine().stroke(Color.red)
VStack {
Image(systemName: "pencil")
Text("Label 2")
}
MyLine().stroke(Color.red)
VStack {
Image(systemName: "pencil")
Text("Label 3")
}
}
}
}
You could add a lineWidth parameter to make the stroke thicker:
.stroke(Color.red, lineWidth: 4)
Also, if you didn't using spacing on the HStack, you could using a padding modifier on either the VStacks or the MyLines to get the spacing.
I created a simple collection with navigation from the last page to the next screen.
How to correctly write transition condition if I need to apply the .fullScreenCover modifier to the button on the last page array index?
How do I correctly place the background image on the first collection screen so that it is not on the following screens, by convention, if the index is the first?
import SwiftUI
struct IntroView: View {
#ObservedObject var viewModel = IntroViewModel()
#State private var tabSelection = 0
#State private var isLastPage = false
var body: some View {
ZStack {
TabView(selection: $tabSelection) {
ForEach(0..<viewModel.pages.endIndex) { index in
VStack {
Image("icnDE")
.padding(.bottom, 20)
.padding(.top, 50)
.frame(alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
Text(viewModel.pages[index].name)
.font(Font.custom("TeXGyreAdventor-Bold", size: 32))
.foregroundColor(.white)
.multilineTextAlignment(.center)
.padding(.horizontal,30)
Button(action: {
self.tabSelection += 1
self.isLastPage = false
}) {
Text(viewModel.pages[index].buttonName)
.font(Font.custom("TeXGyreAdventor-Bold", size: 18))
.frame(width: 335, height: 56, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.foregroundColor(Color.white)
.background(Color.blue)
.cornerRadius(12)
.padding(.top, 50)
}
if tabSelection == viewModel.pages.count - 1, isLastPage == true {
Button(action: {
self.tabSelection += 1
self.isLastPage = false
}) {
Text(viewModel.pages[index].buttonName)
.font(Font.custom("TeXGyreAdventor-Bold", size: 18))
.frame(width: 335, height: 56, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.foregroundColor(Color.white)
.background(Color.blue)
.cornerRadius(12)
.padding(.top, 50)
}.fullScreenCover(isPresented: $isLastPage, content: {
LoginView()})
}
}
}
}
.tabViewStyle(PageTabViewStyle())
.indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .always))
.tabViewStyle(PageTabViewStyle.init(indexDisplayMode: .never))
.edgesIgnoringSafeArea(.all)
}
.background(
Image("imgHappypeople")
.resizable()
.edgesIgnoringSafeArea(.all)
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
}
}
struct Intro_Previews: PreviewProvider {
static var previews: some View {
IntroView()
.previewDevice("iPhone 11")
}
}
For open full screen cover, Add .fullScreenCover at the top of the ZStack and no need to add a condition for two-button.
struct IntroView: View {
#ObservedObject var viewModel = IntroViewModel()
#State private var tabSelection = 0
#State private var isLastPage = false
var body: some View {
ZStack {
TabView(selection: $tabSelection) {
ForEach(0..<viewModel.pages.endIndex) { index in
VStack {
Image("icnDE")
.padding(.bottom, 20)
.padding(.top, 50)
.frame(alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
Text(viewModel.pages[index].name)
.font(Font.custom("TeXGyreAdventor-Bold", size: 32))
.foregroundColor(.white)
.multilineTextAlignment(.center)
.padding(.horizontal,30)
Button(action: {
if self.tabSelection == viewModel.pages.count - 1 {
self.isLastPage = true
} else {
self.tabSelection += 1
} //<-- Use this condition
}) {
Text("\(index)")
.font(Font.custom("TeXGyreAdventor-Bold", size: 18))
.frame(width: 335, height: 56, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.foregroundColor(Color.white)
.background(Color.blue)
.cornerRadius(12)
.padding(.top, 50)
}
}
}
}
.tabViewStyle(PageTabViewStyle())
.indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .always))
.tabViewStyle(PageTabViewStyle.init(indexDisplayMode: .never))
.edgesIgnoringSafeArea(.all)
}
.fullScreenCover(isPresented: $isLastPage, content: {
Text("Details")}) //<== Use fullScreenCover here
.background(
Image("imgHappypeople")
.resizable()
.edgesIgnoringSafeArea(.all)
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
}
}
I'm developing a iOS app relies on SwiftUI.
I have a ZStack view and inside it, I call a another view along with a button.
ZStack(alignment: .bottomTrailing) {
ImageStepView(data: self.data[randomImageNum])
Button(action: { self.showFavorites = true }) {
HStack {
Image(systemName: "suit.heart.fill")
Text("FAVORITES")
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 15)
.foregroundColor(.white)
.padding()
.background(Color.black.opacity(0.6))
.cornerRadius(5)
.padding(.horizontal, 20)
}
}
ImageStepView.swift
struct ImageStepView: View {
var data: ImageDataModel
var body: some View {
GeometryReader { geometry in
ScrollView(.vertical) {
VStack{
Image(data.image)
.resizable()
.border(Color.white, width: 5)
.overlay(
Rectangle().stroke(Color.white, lineWidth: 4))
.shadow(color: Color.gray, radius: 10, x: 10, y: 10)
.scaledToFit()
.frame(height: geometry.size.height-110)
} .padding()
VStack{
VStack(alignment: .leading) {
HStack{...}
HStack {...}
}
Spacer()
.frame(height: 50)
VStack(alignment: .leading){
VStack{
HStack {...}
HStack {...}
HStack {...}
}
}
}.padding()
}.background(Color("Color").ignoresSafeArea(.all))
.frame(width: geometry.size.width)
.frame(minHeight: geometry.size.height)
}
}
}
ImageStepView has a ScroolView, that's why Button not appears on the end of ScroolView, it appears on bottom of the screen.
What I want is to show The Button not on bottom of the screen but end of the ImageStepView.
You can make the ImageStepView accept a generic parameter - a view to be injected:
struct ImageStepView<Injected: View>: View {
var data: ImageDataModel
var injectedView: () -> Injected
var body: some View {
GeometryReader { geometry in
ScrollView(.vertical) {
// ScrollView contents
injectedView()
}
.background(Color("Color").ignoresSafeArea(.all))
.frame(width: geometry.size.width)
.frame(minHeight: geometry.size.height)
}
}
}
and pass the injected view to the ImageStepView:
ImageStepView(data: self.data[randomImageNum]) {
Button(action: { self.showFavorites = true }) { ... }
}
or
ImageStepView(data: self.data[randomImageNum]) { EmptyView() }
I'm currently trying to handle SwiftUI by following a tutorial, but somehow I can't solve one issue:
I created another View, namely my HomeView.swift - this file contains the following code:
import SwiftUI
struct Home: View {
var menu = menuData
#State var show = false
var body: some View {
ZStack {
ZStack(alignment: .topLeading) {
Button(action: { self.show.toggle() }) {
HStack {
Spacer()
Image(systemName: "list.dash")
.foregroundColor(Color("primary"))
}
.padding(.trailing, 20)
.frame(width: 90, height: 60)
.background(Color.white)
.cornerRadius(30)
.shadow(color: Color("shadow"), radius: 10, x: 0, y: 10)
}
Spacer()
}
ZStack(alignment: .topTrailing) {
Button(action: { self.show.toggle() }) {
HStack {
Spacer()
Image(systemName: "map.fill")
.foregroundColor(Color("primary"))
}
.padding(.trailing, 20)
.frame(width: 44, height: 44)
.background(Color.white)
.cornerRadius(30)
.shadow(color: Color("shadow"), radius: 10, x: 0, y: 10)
}
Spacer()
}
MenuView(show: $show)
}
}
}
struct Home_Previews: PreviewProvider {
static var previews: some View {
Home()
}
}
struct MenuRow: View {
var text: String?
var image: String?
var body: some View {
HStack {
Image(systemName: image ?? "")
.foregroundColor(Color("third"))
.frame(width: 32, height: 32, alignment: .trailing)
Text(text ?? "")
.font(Font.custom("Helvetica Now Display Bold", size: 15))
.foregroundColor(Color("primary"))
Spacer()
}
}
}
struct Menu: Identifiable {
var id = UUID()
var title: String
var icon: String
}
let menuData = [
Menu(title: "My Account", icon: "person.crop.circle.fill"),
Menu(title: "Reservations", icon: "house.fill"),
Menu(title: "Sign Out", icon: "arrow.uturn.down")
]
struct MenuView: View {
var menu = menuData
#Binding var show: Bool
var body: some View {
VStack(alignment: .leading, spacing: 20) {
ForEach(menu) { item in
MenuRow(text: item.title, image: item.icon)
}
Spacer()
}
.padding(.top, 20)
.padding(30)
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.white)
.cornerRadius(30)
.padding(.trailing, 60)
.shadow(radius: 20)
.rotation3DEffect(Angle(degrees: show ? 0 : 60), axis: (x: 0, y: 10, z: 0))
.animation(.default)
.offset(x: show ? 0 : -UIScreen.main.bounds.width)
.onTapGesture {
self.show.toggle()
}
}
}
As you can see, right in the beginning, inside of my Home struct, I tried to align two ZStacks - one .topLeading and one .topTrailing. Reading the docs, this should change its position, but somehow it doesn't. Both stack stay centered.
BTW I haven't particularly touched ContenView.swift yet.
Actually, for either inner ZStack, you need to set frames. This can make them reach edges.
ZStack{
ZStack{
Button(action: { self.show.toggle() }) {
HStack {
Spacer()
Image(systemName: "list.dash")
.foregroundColor(Color("primary"))
}
.padding(.trailing, 20)
.frame(width: 90, height: 60)
.background(Color.white)
.cornerRadius(30)
.shadow(color: Color("shadow"), radius: 10, x: 0, y: 10)
}
Spacer()
}.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
ZStack{
Button(action: { self.show.toggle() }) {
HStack {
Spacer()
Image(systemName: "map.fill")
.foregroundColor(Color("primary"))
}
.padding(.trailing, 20)
.frame(width: 44, height: 44)
.background(Color.white)
.cornerRadius(30)
.shadow(color: Color("shadow"), radius: 10, x: 0, y: 10)
}
Spacer()
}.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
MenuView(show: $show)
}
struct Home: View {
var menu = menuData
#State var show = false
var body: some View {
ZStack {
VStack {
HStack {
Button(action: { self.show.toggle() }) {
HStack {
Spacer()
Image(systemName: "list.dash")
.foregroundColor(Color("primary"))
}
.padding(.trailing, 20)
.frame(width: 90, height: 60)
.background(Color.white)
.cornerRadius(30)
.shadow(color: Color("shadow"), radius: 10, x: 0, y: 10)
}
Spacer()
Button(action: { self.show.toggle() }) {
HStack {
Spacer()
Image(systemName: "map.fill")
.foregroundColor(Color("primary"))
}
.padding(.trailing, 20)
.frame(width: 44, height: 44)
.background(Color.white)
.cornerRadius(30)
.shadow(color: Color("shadow"), radius: 10, x: 0, y: 10)
}
}
Spacer()
}
MenuView(show: $show)
}
}
}
Is this the layout that you are looking for? With VStack and HStack you can align the views to the top and on both edges