I'm working with NavigatonView and NavigationLink, I've made my view like that :
ScrollView{
VStack{
// MARK: - Survey and tips Navigation
HStack(spacing:9){
NavigationLink(destination:Container.sharedContainer.resolve(SurveyListView.self,argument: $VM.occurrences)!){
iconHome(image: "img-survey", icon: "icon-survey", text: "surveys_upper_case")}
NavigationLink(destination:Container.sharedContainer.resolve(SurveyListView.self,argument: $VM.occurrences)!){
iconHome(image: "img-tip", icon: "icon-tip", text: "tips_upper_case")}
}
.frame(maxWidth: .infinity,maxHeight: .infinity)
}
.padding(.horizontal,37)
.background(Color.red)
}
iconHome is another struct which implements view protocol :
struct iconHome : View{
var image:String
var icon:String
var text:LocalizedStringKey
var body : some View{
GeometryReader{ geometry in
ZStack{
Image(self.image)
.renderingMode(.original)
.resizable()
.frame(width: geometry.size.width ,height:geometry.size.width)
.aspectRatio(contentMode: .fit)
.cornerRadius(20)
VStack{
Image(self.icon)
.renderingMode(.original)
.resizable()
.frame(width: geometry.size.width / 5,height:geometry.size.width / 5 )
.aspectRatio(contentMode: .fit)
Text(self.text)
.foregroundColor(.white)
.modifier(OpenSansBoldModifier(fontSize: 12))
}
}
.shadow(radius: 5, x: 5, y: 5)
}
}
}
I've got a strange result :
I don't why but my HStack which contains my two navigation link have only red rectangle high ... so it's hard to click on him ... any idea about why the HStack doesn't have the good high ?
It seems there is missing a Fill mode for the geometry
GeometryReader{ geometry in
....
}.aspectRatio(contentMode: .fill)
Related
I'm creating an iOS minesweeper game and want to have a bar at the top with three pieces of information:
The high score (next to a trophy symbol) [LEFT]
The current time (next to a timer symbol) [CENTER]
The number of bombs left (next to a bomb symbol) [RIGHT]
However, the elements are not evenly aligned as you can see in the picture at the bottom- I'm guessing the spacers are automatically the same size. So, because the text on the left side of the screen is wider than that on the right, the middle text is offset to the right.
How can I align these items so the center time/image is perfectly in the middle with the other objects on the left/right?
My code looks like:
struct GameView: View {
#EnvironmentObject var game: Game
#State var labelHeight = CGFloat.zero
var body: some View {
VStack(alignment: .center, spacing: 10) {
Text("MINESWEEPER")
.font(.system(size: 25, weight: .bold, design: .monospaced))
.kerning(3)
.foregroundColor(.red)
HStack(alignment: .center, spacing: 5) {
Image(systemName: "rosette")
.resizable()
.aspectRatio(contentMode: .fit)
.foregroundColor(.black)
Text(game.highScoreString)
.font(.system(size: 15, weight: .bold, design: .monospaced))
.foregroundColor(.black)
Spacer()
Image(systemName: "timer")
.resizable()
.aspectRatio(contentMode: .fit)
.foregroundColor(.black)
Text(game.timerString)
.font(.system(size: 15, weight: .bold, design: .monospaced))
.foregroundColor(.black)
Spacer()
Image("top_bomb")
.resizable()
.aspectRatio(contentMode: .fit)
Text(String(game.bombsRemaining))
.font(.system(size: 15, weight: .bold, design: .monospaced))
.foregroundColor(.black)
.overlay(GeometryReader(content: { geometry in
Color.clear
.onAppear(perform: {self.labelHeight = geometry.frame(in: .local).size.height})
}))
}
.frame(width: UIScreen.main.bounds.width * 0.9, height: labelHeight, alignment: .center)
BoardView()
}
}
}
Instead of aligning by spacers, move each part into separate view, say LeftHeader, CenterHeader, and RightHeader and use frame alignment, which gives exact separation by equal sizes, like
HStack {
LeftHeader()
.frame(maxWidth: .infinity, alignment: .leading)
CenterHeader()
.frame(maxWidth: .infinity, alignment: .center)
RightHeader()
.frame(maxWidth: .infinity, alignment: .trailing)
}
This can be achieved using a ZStack , HStack and Spacers.
ZStack{
HStack{
Text("Left")
Spacer()
}
HStack{
Text("Center")
}
HStack{
Spacer()
Text("Right")
}
}
Even if you remove left ( or right ) HStack, it won't affect the other components or the layout.
You can make it look bit nicer by adding paddings like this,
ZStack{
HStack{
Text("Left").padding([.leading])
Spacer()
}
HStack{
Text("Center")
}
HStack{
Spacer()
Text("Right").padding([.trailing])
}
}
I think you can create more one stack.
My solution like:
/**
* spacing is an example
* minWidth calculated by fontSize
*/
HStack(alignment: .center, spacing: 20){
HStack{ Image, Text }.frame(minWidth: 30)
HStack{ Image, Text }.frame(minWidth: 30)
HStack{ Image, Text }.frame(minWidth: 30)
}
Hope it useful for you.
I want to have a view with rounded corners and a custom background colour, but the latter overflows in the corners, as showed here:
This is my code:
var id:Int
var body: some View {
VStack(alignment: .leading) {
Text(name)
.font(.title2)
.bold()
.foregroundColor(.accentColor)
Text("Index numéro " + String(id))
.foregroundColor(.accentColor.opacity(0.75))
}
.padding()
.frame(width: UIScreen.width*0.9,
height: UIScreen.height*0.1,
alignment: .leading)
.overlay(RoundedRectangle(cornerRadius: 20)
.stroke(Color.accentColor, lineWidth: 3))
.background(Color.accentColor.opacity(0.1))
}
Thanks in advance, I hope you guys will help me solve this issue!
You can use a RoundedRectangle with strokeBorder and then a background of an additional RoundedRectangle with a fill modifier. This technique is detailed here: SwiftUI: How to draw filled and stroked shape?
var body: some View {
VStack(alignment: .leading) {
Text(name)
.font(.title2)
.bold()
.foregroundColor(.accentColor)
Text("Index numéro " + String(id))
.foregroundColor(.accentColor.opacity(0.75))
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
.background(
RoundedRectangle(cornerRadius: 20)
.strokeBorder(Color.accentColor, lineWidth: 3)
.background(
RoundedRectangle(cornerRadius: 20).fill(Color.accentColor.opacity(0.3))
)
)
.padding()
}
Note: I've also changed your .frame modifier and replaced it with padding, which generally would be a more flexible solution that doesn't rely on measuring the screen size, but it's inconsequential to this answer
Simply replace this code:
.background(Color.accentColor.opacity(0.1).cornerRadius(20.0))
In iOS 15 there is a .background() modifier clipped to shape.
func background<S, T>(_ style: S, in shape: T, fillStyle: FillStyle = FillStyle()) -> some View where S : ShapeStyle, T : InsettableShape
Try this:
struct ContentView: View {
var body: some View {
VStack(alignment: .leading) {
Text("name")
.font(.title2)
.bold()
.foregroundColor(.accentColor)
Text("Index numéro " + "1")
.foregroundColor(.accentColor.opacity(0.75))
}
.padding()
.frame(width: 200,
height: 100,
alignment: .leading)
.overlay(RoundedRectangle(cornerRadius: 20)
.stroke(Color.accentColor, lineWidth: 3))
.background(Color.accentColor.opacity(0.1), in: RoundedRectangle(cornerRadius: 20))
}
}
I am trying to create below design using swiftUI but not able to find proper approach.
I tried but my image is not covering back button but coming below to back button
Expected design
VStack(spacing: 16) {
VStack {
ZStack(alignment: .topTrailing) {
Image("defaultImage")
.resizable()
.frame( height: 200)
Button("Cancel", action: {
print("cancel")
})
.padding(12)
}
}
}
My Design :(
You might want to do a lot of try in SwiftUI to get a custom design right. gave it a quick try for you and added comments on the code so you can understand most of what i did.
this is the quick result i got (with a random background i got on internet)
import SwiftUI
struct FirstView: View{
var body: some View{
NavigationView {
VStack{
NavigationLink(destination: BackgroundImageNav()){
Text("Go to the view")
.font(.title)
}
}
}
}
}
struct BackgroundImageNav: View {
#Environment(\.presentationMode) var presentationMode
// remplacing the back button action, (only problem is that this disable the swipe back)
var body: some View {
ZStack {
// ZStack help you having your image behind the other views
VStack {
Image("defaultImage")
.resizable()
.frame(height: 180)
.edgesIgnoringSafeArea(.all)
Spacer()
// having a spacer that push the image on the top and ignoring the safe area
}
VStack(alignment:.leading){
HStack{
Button(action:{
presentationMode.wrappedValue.dismiss()
// back button action
}){
Image(systemName:"chevron.left")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 30, height: 30)
.foregroundColor(.white)
}
Spacer()
Rectangle()
.fill(Color.white)
.frame(width: 200, height: 80, alignment: .center)
Spacer()
Button(action:{
// do something
}){
Image(systemName: "xmark")
.resizable()
.frame(width: 24, height: 24)
.foregroundColor(.white)
}
}.padding()
Text("Title")
.bold()
.font(.title)
.foregroundColor(.black)
.background(Color.white)
.padding(24)
Spacer()
}
}.navigationBarHidden(true)
// removing the bar to customize the back button
}
}
struct backgroundImage_Previews: PreviewProvider {
static var previews: some View {
FirstView()
}
}
Everytime I am going back to the start screen it seems that it stacks itself. You can see what i mean in the screenshot i attached. I added some borders so you can see what i mean
The code of the Start screen:
struct StartScreen: View {
var body: some View {
NavigationView{
VStack() {
Image("Headline").resizable().scaledToFit()
Image("GreenMonster")
.resizable()
.scaledToFit()
.frame(alignment: .top)
NavigationLink(destination: Game(monster: monster)) {
Text("Spielen")
.frame(width: 200, height: 50, alignment: .center)
.font(.title)
.padding()
.background(Color.blue)
.cornerRadius(40)
.foregroundColor(.white)
.padding(10)
.overlay(
RoundedRectangle(cornerRadius: 40)
.stroke(Color.blue, lineWidth: 5)
)
}.isDetailLink(false)
/*
NavigationLink(destination: Settings()){
Image("Settingswheel").resizable().scaledToFit().frame(width: 50, height: 50).offset(x: 150)
}
*/
}
}.navigationBarBackButtonHidden(true).border(Color.green)
}
}
and the code to go back is:
struct DefeatedView: View {
#EnvironmentObject var helper: Helper
var body: some View {
NavigationView(){
VStack(){
Text("BESIEGT!").foregroundColor(.green).font(.title).bold()
Image(monster[0].imageURL).resizable().scaledToFit()
NavigationLink(destination: StartScreen()){
Text("Zum Start").frame(width: 120, height: 6, alignment: .center)
.padding()
.background(Color.blue)
.cornerRadius(40)
.foregroundColor(.white)
.padding(10)
.overlay(
RoundedRectangle(cornerRadius: 40)
.stroke(Color.blue, lineWidth: 5)
)
}
}
}.navigationBarBackButtonHidden(true)
}
}
Thanks for the help i am new into SwiftUI
Add this to your DefeatedView
#Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
And then instead of using a NavigationLink again, use a button and push your View manually back to your Start View
Button(action: {
//Push navigation view back
self.presentationMode.wrappedValue.dismiss()
})
{
Text("Zum Start").frame(width: 120, height: 6, alignment: .center)
.padding()
.background(Color.blue)
.cornerRadius(40)
.foregroundColor(.white)
.padding(10)
.overlay(
RoundedRectangle(cornerRadius: 40)
.stroke(Color.blue, lineWidth: 5)
)
}
Edit:
As you pushed your NavigationView twice, calling presentation mode only once will indeed push back to your Game View. Here is a possible solution with ObservableObject.
class ViewHelper : ObservableObject
{
#Published var finishedGame : Bool = false
}
struct StartScreen: View {
#EnvironmentObject var viewHelper : ViewHelper
var body: some View {
NavigationLink(destination: Game(), isActive: self.$viewHelper.finishedGame) {
Text("Spielen")
And then when the gameplay Is finished, change the finishedGame variable.
struct DefeatedView: View {
#EnvironmentObject var viewHelper : ViewHelper
var body: some View {
NavigationView(){
VStack(){
Text("BESIEGT!").foregroundColor(.green).font(.title).bold()
Button(action: {
self.viewHelper.finishedGame = false
})
{
Text("Zum Start").frame(width: 120, height: 6, alignment: .center)
.padding()
.background(Color.blue)
.cornerRadius(40)
.foregroundColor(.white)
.padding(10)
.overlay(
RoundedRectangle(cornerRadius: 40)
.stroke(Color.blue, lineWidth: 5)
)
}
}
}.navigationBarBackButtonHidden(true)
I found the solution to my Problem on this Site:
https://thinkdiff.net/ios/swiftui-how-to-pop-to-root-view/
I have been trying to create onboarding flow using horizontal lists etc. I have created a view called OnboardingViewand inside it I have a VStack with image, and two Text views.
struct OnboardingView: View {
var body: some View {
GeometryReader { geometry in
VStack(spacing: 10) {
Spacer()
Image("1")
.resizable()
.frame(width: 300, height: 300)
.clipShape(Circle())
.padding(20)
Text("Travel the World")
.frame(width: geometry.size.width, height: 20, alignment: .center)
.font(.title)
Text("Explore the world and get to know different cultures and people from all around the world")
.lineLimit(nil)
.padding(.leading, 15)
.padding(.trailing, 15)
.font(.system(size: 16))
.frame(width: geometry.size.width, height: 50, alignment: .center)
.multilineTextAlignment(.center)
Spacer()
}.background(Color.red)
}
}
}
This is what I get with the above code:
Now I am trying to add this view inside a ScrollView, using HStack.
struct ContentView: View {
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack {
OnboardingView()
OnboardingView()
}
}.background(Color.black)
}
}
The result of the above code is absurd! This is what I get. How to go about fixing this? Help will be appreciated! Thanks.
One possible solution would be to set the width of the OnboardingView to geometry.size.width:
var body: some View {
GeometryReader { geometry in
ScrollView(.horizontal, showsIndicators: false) {
HStack {
OnboardingView()
.frame(width: geometry.size.width)
OnboardingView()
.frame(width: geometry.size.width)
}
}.background(Color.black)
}
}
Result