add borders to 2 sides only - swiftui

I have a View stack as Expanded white part and I have given the corner radius to it, I need to give radius to only top left and top right
My Code
struct loginView: View {
#State private var stringOfTextField: String = String()
var body: some View {
ZStack {
LinearGradient(colors: [Color("primarycolor"), Color("secondarycolor")],
startPoint: .top,
endPoint: .center).ignoresSafeArea()
VStack() {
Text("Login").foregroundColor(.white).font(
.system(size: 18)
)
Image("logo")
Spacer()
}
VStack {
Spacer(minLength: 230)
VStack{
HStack {
Image(systemName: "person").foregroundColor(.gray)
TextField("Enter Email", text: $stringOfTextField)
} .padding()
.overlay(RoundedRectangle(cornerRadius: 10.0).strokeBorder(Color.gray, style: StrokeStyle(lineWidth: 1.0)))
.padding(.bottom)
TextField("Enter Password", text: $stringOfTextField)
.padding()
.overlay(RoundedRectangle(cornerRadius: 10.0).strokeBorder(Color.gray, style: StrokeStyle(lineWidth: 1.0)))
.padding(.bottom)
Text("Forgot Password ?")
.frame(maxWidth: .infinity, alignment: .trailing)
.padding(.bottom)
Button(action: {
print("sign up bin tapped")
}) {
Text("SIGN IN")
.frame(minWidth: 0, maxWidth: .infinity)
.font(.system(size: 18))
.padding()
.foregroundColor(.white)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color("secondarycolor"), lineWidth: 1)
)
}
.background(Color("secondarycolor"))
.cornerRadius(25)
Spacer()
}
.padding(.vertical, 60)
.padding(.horizontal, 20)
.background(Color.white)
.cornerRadius(30)
}
}
.ignoresSafeArea(edges: [.bottom])
}
}
Preview
You can see on the end it's showing white border because border is given to all sides, I need to give it to top 2 sides only. I try by Roundedcorner, but that was not working.

You can try like this
struct ContentView: View {
var body: some View {
VStack{
//....
//your content
//.....
}
.clipShape(CustomCorner(corners: [.topLeft, .topRight], radius: 25))
}
}
struct CustomCorner: Shape {
var corners : UIRectCorner
var radius : CGFloat
func path(in rect: CGRect)->Path{
let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
return Path(path.cgPath)
}
}

Related

Keep VStack in center of the screen in SwiftUI

I'm new on SwiftUI and I don't know how to manage my views.
I have this code:
import SwiftUI
struct ContentView: View {
#State private var email: String = ""
#State private var passord: String = ""
var body: some View {
ZStack {
Image("corner")
.resizable()
.scaledToFill()
VStack {
VStack { //VStack1
TextField("Email", text: $email)
.frame(width: 300, height: 40)
.textFieldStyle(.roundedBorder)
.bold(true)
SecureField("Password", text: $passord)
.frame(width: 300, height: 40)
.textFieldStyle(.roundedBorder)
.bold(true)
Button {
//Do something
} label: {
Text("Forgot your password ?")
.underline()
.foregroundColor(.white)
}
}
VStack { // VStack2
Text("Not registered ?")
.font(.title2)
.foregroundColor(.white)
Button("Sign up") {}
.frame(width: 200, height: 37)
.foregroundColor(.white)
.background(Color(.orange))
.cornerRadius(20)
}
}
}
}
}
I want to place the VStack2 in the bottom of the screen and keep the VStack1 on the center of the screen.
How I can do that. I've try to search but I don't find the solution on StackOverflow.
I tried to play with Spacer() and padding() but I have not a good result.
Screen
A simple way to do this would be to add VStack1 to the ZStack which will place it in the centre of the screen. Then add wrap VStack2 in another VStack with a Spacer to push it to the bottom of the screen, e.g.
ZStack {
Image("corner")
VStack { //VStack1
// etc
}
VStack {
Spacer()
VStack { // VStack2
// etc
}
}
}
Simple put
HStack {
Spacer()
VStack {
Text("Centered")
}
Spacer()
}
import SwiftUI
struct ContentView: View {
#State private var email: String = ""
#State private var passord: String = ""
var body: some View {
VStack {
Spacer()
middleView()
Spacer()
bottomView()
.paddind(.bottom, 12)
}
.ignoresSafeArea()
.background {
Image("corner")
.resizable()
.scaledToFill()
}
}
func middleView() -> some View {
VStack(spacing: 20) {
TextField("Email", text: $email)
.frame(width: 300, height: 40)
.textFieldStyle(.roundedBorder)
.bold(true)
SecureField("Password", text: $passord)
.frame(width: 300, height: 40)
.textFieldStyle(.roundedBorder)
.bold(true)
Button {
//Do something
} label: {
Text("Forgot your password ?")
.underline()
.foregroundColor(.white)
}
}
}
func bottomView() -> some View() {
VStack {
Text("Not registered ?")
.font(.title2)
.foregroundColor(.white)
Button("Sign up") {}
.frame(width: 200, height: 37)
.foregroundColor(.white)
.background(Color(.orange))
.cornerRadius(20)
}
}
}

Swift ScrollView goes on top again

I have a simple screen in which content are going on bottom so I use ScrollView on it issue is when I release scroll its going to top again. I need to replace scrollview to something Because I try with with List but that's not working properly with background image so I use Scrollview
struct signupView: View {
#StateObject var signUpViewModel = signupViewModel()
var body: some View {
ZStack{
GeometryReader { geo in
ScrollView {
VStack{
VStack{
Image("Untitled-2")
.resizable()
.frame(width: geo.size.width * 0.45, height: geo.size.width * 0.45)
}
.padding(.top, geo.size.height * 0.03)
HStack(spacing: 0){
VStack{
Text("Student")
.foregroundColor(signUpViewModel.isStudent ? Color("secondarycolor") : Color("authColorText"))
Rectangle()
.fill(signUpViewModel.isStudent ? Color("secondarycolor") : Color("authColorText"))
.frame(width: geo.size.width * 0.4, height: 2)
}
.onTapGesture {
signUpViewModel.isStudent = true
}
VStack{
Text("Facilitator")
.foregroundColor(!signUpViewModel.isStudent ? Color("secondarycolor") : Color("authColorText"))
Rectangle()
.fill(!signUpViewModel.isStudent ? Color("secondarycolor") : Color("authColorText"))
.frame(width: geo.size.width * 0.4, height: 2)
}
.onTapGesture {
signUpViewModel.isStudent = false
}
}
VStack{
VStack{
Spacer().frame(height: geo.size.height * 0.7)
TextField("", text: $signUpViewModel.nameField)
.modifier(PlaceholderStyle(showPlaceHolder: signUpViewModel.nameField.isEmpty,
placeholder: "Name"))
.padding()
.background(RoundedRectangle(cornerRadius: 50).fill(Color("primarycolorwithopacity")))
.foregroundColor(Color("authColorText"))
.foregroundColor(Color("authColorText")).font(Font.headline.weight(.medium))
.padding(.bottom, 10)
TextField("", text: $signUpViewModel.emailField)
.modifier(PlaceholderStyle(showPlaceHolder: signUpViewModel.emailField.isEmpty,
placeholder: "Email"))
.padding()
.background(RoundedRectangle(cornerRadius: 50).fill(Color("primarycolorwithopacity")))
.foregroundColor(Color("authColorText"))
.foregroundColor(Color("authColorText")).font(Font.headline.weight(.medium))
.padding(.bottom, 10)
SecureField("", text: $signUpViewModel.passwordField)
.modifier(PlaceholderStyle(showPlaceHolder: signUpViewModel.passwordField.isEmpty,
placeholder: "Password"))
.padding()
.background(RoundedRectangle(cornerRadius: 50).fill(Color("primarycolorwithopacity")))
.foregroundColor(Color("authColorText"))
.foregroundColor(Color("authColorText")).font(Font.headline.weight(.medium))
.padding(.bottom, 10)
SecureField("", text: $signUpViewModel.passwordRepeatField)
.modifier(PlaceholderStyle(showPlaceHolder: signUpViewModel.passwordRepeatField.isEmpty,
placeholder: "Repeat Password"))
.padding()
.background(RoundedRectangle(cornerRadius: 50).fill(Color("primarycolorwithopacity")))
.foregroundColor(Color("authColorText"))
.foregroundColor(Color("authColorText")).font(Font.headline.weight(.medium))
.padding(.bottom, 10)
TextField("", text: $signUpViewModel.countryField)
.modifier(PlaceholderStyle(showPlaceHolder: signUpViewModel.countryField.isEmpty,
placeholder: "Select Country"))
.padding()
.background(RoundedRectangle(cornerRadius: 50).fill(Color.black))
.foregroundColor(Color("authColorText"))
.foregroundColor(Color("authColorText")).font(Font.headline.weight(.medium))
.padding(.bottom, 10)
.overlay(
ZStack{if signUpViewModel.hasOptions {
Spacer().frame(height: 70)
VStack(alignment: .center) {
ForEach(signUpViewModel.countryoptions, id: \.self) {d in
Text(d).frame(maxWidth: .infinity).padding(8).onTapGesture {
signUpViewModel.countryField = d
signUpViewModel.hasOptions = false
}
Divider()
}
}.frame(maxWidth: .infinity).background(RoundedRectangle(cornerRadius: 6).foregroundColor(.white).shadow(radius: 4))
}
}.offset(y: 50)
, alignment: .topLeading)
.overlay(
Image("Untitled-42")
.resizable()
.foregroundColor(.white)
.frame(width: 15, height: 10).padding().padding(.bottom,2).onTapGesture {
signUpViewModel.hasOptions = !signUpViewModel.hasOptions
}
, alignment: .trailing).zIndex(1)
SecureField("", text: $signUpViewModel.passwordRepeatField)
.modifier(PlaceholderStyle(showPlaceHolder: signUpViewModel.passwordRepeatField.isEmpty,
placeholder: "Repeat Password"))
.padding()
.background(RoundedRectangle(cornerRadius: 50).fill(Color("primarycolorwithopacity")))
.foregroundColor(Color("authColorText"))
.foregroundColor(Color("authColorText")).font(Font.headline.weight(.medium))
.padding(.bottom, 10)
}.padding([.top, .leading, .trailing], 8).frame(height: 45)
}
}
.padding()
}
}
}.background(Image("Untitled-7").resizable()).edgesIgnoringSafeArea(.all)
}
}
When I release its going back to top. Don't get it why its happening on this. Or I need to use something else
One way of doing it is with introspect package, you can add the package here https://github.com/siteline/SwiftUI-Introspect.git
scrollView.bounces = false
Here's how I would do it with Introspect:
struct ContentView: View {
var body: some View {
ScrollView {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
}
.padding()
}.introspectScrollView { scrollView in
scrollView.bounces = false
}
}
}
also don't forget the imports
import Introspect

SwiftUI transition is not working when in HSack

I have a VStack which wraps a number elements wrapped inside another HStack, I want to add a simple bottom to top transition on load, but it does not have any effect on the output:
var body: some View {
let bottomPadding = ScreenRectHelper.shared.bottomPadding + 150
GeometryReader { geometry in
ZStack {
VisualEffectViewSUI(effect: UIBlurEffect(style: .regular))
.edgesIgnoringSafeArea(.all)
VStack(alignment: .center) {
Spacer()
HStack {
VStack(alignment: .leading, spacing: 15) {
ForEach(items, id: \.id) { item in
HStack(alignment: .center) {
Image(item.imageName)
.resizable()
.scaledToFit()
.frame(width: 24)
Spacer()
.frame(width: 15)
Text("\(item.text)")
.foregroundColor(.white)
.transition(.move(edge: .bottom))
}
.contentShape(Rectangle())
.onTapGesture {
/// Do something
}
}
}
}
.frame(width: geometry.size.width, height: nil, alignment: .center)
}.zIndex(1)
.frame(width: geometry.size.width, height: nil, alignment: .center)
.padding(.bottom, bottomPadding)
}.background(
LinearGradient(gradient: Gradient(colors: [gradientColor.opacity(0),gradientColor]), startPoint: .top, endPoint: .bottom)
)
}.edgesIgnoringSafeArea(.all)
}
This SwiftUI view is added to a UIKit view controller and that view controller is presented modally.
.transition only works if you insert something conditionally into the view.
I'm not totally sure what you want to achieve, but if you want the whole view to slide from the bottom, this would work.
struct ContentView: View {
#State private var showText = false
let bottomPadding: CGFloat = 150
var body: some View {
ZStack {
if showText { // conditional view to make .transition work
// VisualEffectViewSUI(effect: UIBlurEffect(style: .regular))
// .edgesIgnoringSafeArea(.all)
VStack(alignment: .center) {
Spacer()
HStack {
VStack(alignment: .leading, spacing: 15) {
ForEach(0 ..< 3) { item in
HStack(alignment: .center) {
Image(systemName: "\(item).circle")
.resizable()
.scaledToFit()
.frame(width: 24)
Spacer()
.frame(width: 15)
Text("Item \(item)")
.foregroundColor(.white)
}
}
}
}
}
.frame(maxWidth: .infinity)
.padding(.bottom, bottomPadding)
.background(
LinearGradient(gradient: Gradient(colors: [.blue.opacity(0), .blue]),
startPoint: .top, endPoint: .bottom)
)
.edgesIgnoringSafeArea(.all)
.transition(.move(edge: .bottom)) // here for whole modal view
}
Button("Show modal") {
withAnimation {
showText.toggle()
}
}
}
}
}

Responsive design swiftUI

How can I make sure that my UI looks good on different devices such as the iPhone 13 and the iPhone 8?
Here is my code that is unresponsive
It is just a simple layout
import SwiftUI
let grey = Color(red: 0.928, green: 0.928, blue: 0.928)
let i = 0
var columns: [GridItem] =
Array(repeating: .init(.flexible()), count: 3)
let categories: [Category] = [
.init(name: "Pasta", imageName: "square.and.arrow.up"),
.init(name: "Asian", imageName: "square.and.arrow.down.fill"),
.init(name: "Main", imageName: "rectangle.portrait.and.arrow.right"),
.init(name: "Quick", imageName: "pencil.slash"),
.init(name: "Veg", imageName: "scribble.variable"),
.init(name: "Dessert", imageName: "pencil.circle.fill"),
]
struct SearchView: View {
var body: some View {
VStack{
Text("Search For Recipes")
.font(.system(size: 35))
.padding(.top, 80)
.padding(.horizontal, -70)
.frame(width: 200, height: 10, alignment: .leading)
Spacer()
SearchBox()
.padding(.top, 50)
Spacer()
Recomend()
.padding(.top)
.padding(.leading, -100)
Spacer()
LazyVGrid(columns: columns){
ForEach((0...5), id: \.self) { _ in
MenuChoice()
}
}
.padding(20)
.padding(.bottom, 100)
.padding(.top)
.frame(maxWidth: .infinity)
.frame(height:500)
}
}
}
struct SearchBox: View {
var body: some View{
VStack{
HStack{
Image(systemName: "magnifyingglass")
Text("Search for recipes")
}
.frame(width: 200, height: 10, alignment: .leading)
.padding(.trailing, 60)
}
.frame(width: 300, height: 50)
.background(grey)
.cornerRadius(8)
.padding(.trailing, 50)
}
}
struct MenuChoice: View{
var body: some View{
ZStack{
ForEach(categories, id: \.self) { category in
VStack{
Spacer()
.frame(width: 100, height: 100)
.background(Color(red: 50, green: 207, blue: 255))
.cornerRadius(5)
.shadow(color: .init(.sRGB, white: 0.7, opacity: 1), radius: 4, x: 0.0, y: 2)
.padding(.bottom)
.overlay(
VStack(spacing: 8) {
Image(systemName: category.imageName)
.padding(.top, -10)
.font(.system(size: 30))
Text(category.name)
.foregroundColor(.black)
.font(.system(size: 20))
.padding(.top, 5)
}
)
}
}
}
}
}
struct Recomend: View{
var body: some View{
Button {
print("Image tapped!")
} label: {
ZStack{
RoundedRectangle(cornerRadius: 8)
.frame(width: 250, height: 50)
.foregroundColor(grey)
Text("See All Recomended Recipes")
.foregroundColor(Color.black)
}
}
}
}
struct SearchView_Previews: PreviewProvider {
static var previews: some View {
SearchView()
.previewDevice(PreviewDevice(rawValue: "iPhone 13"))
SearchView()
.previewDevice(PreviewDevice(rawValue: "iPhone 8"))
}
}
On the iPhone 13 it looks good but on the iPhone 8 it looks squashed and not good.
How can i make my code responsive for different screen heights/widths?
Following the comments you could go in this direction:
This adapts nicely and also is a lot less code.
struct ContentView: View {
var body: some View {
VStack{
Text("Search For Recipes")
.font(.system(size: 35))
.frame(maxWidth: .infinity, alignment: .leading)
SearchBox()
.padding(.bottom)
.padding(.bottom)
Recomend()
Spacer()
LazyVGrid(columns: columns, spacing: 18){
ForEach(categories) { category in
MenuChoice(category: category)
}
}
Spacer()
}
.padding()
}
}
struct SearchBox: View {
var body: some View{
VStack {
HStack{
Image(systemName: "magnifyingglass")
Text("Search for recipes")
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
.background(grey)
.cornerRadius(8)
}
}
struct MenuChoice: View{
let category: Category
var body: some View{
VStack(spacing: 8) {
Image(systemName: category.imageName)
.padding(.top)
.font(.system(size: 30))
Text(category.name)
.foregroundColor(.black)
.font(.system(size: 20))
.padding(.bottom)
}
.frame(width: 100, height: 100)
.background(Color(red: 50, green: 207, blue: 255))
.cornerRadius(5)
.shadow(color: .init(.sRGB, white: 0.7, opacity: 1), radius: 4, x: 0.0, y: 2)
}
}
struct Recomend: View{
var body: some View{
Button {
print("Image tapped!")
} label: {
Text("See All Recomended Recipes")
.padding()
.foregroundColor(Color.black)
.frame(maxWidth: .infinity, alignment: .leading)
.background(grey)
.cornerRadius(8)
}
}
}

how to make a VStack span all space?

I'm trying to eliminate the space that the top stack covers, but as much as I need the image to cover the space where the time is shown and the battery is not successful, does anyone have any idea how to do it?
I leave a photo of what I want to eliminate, it is circled with yellow, I also leave my code.
import SwiftUI
struct LoginView: View {
#ObservedObject var LoginViewM:LoginViewModel
var body: some View {
GeometryReader{ geo in
NavigationView{
VStack(spacing:10){
topImage
.frame(width: geo.size.width, height: 270)
.padding(.bottom)
VStack{
usernameTextField
errorUsernameLabel
passwordTextField
errorPasswordLabel
}
.aspectRatio(contentMode: .fill)
Spacer(minLength: 10)
VStack{
buttonLogin
}
Spacer(minLength: 10)
VStack{
textInBottom
}
Spacer(minLength: 10)
}
.padding(.all)
.background(Color.black)
}
.padding(.all)
.background(Color.black)
.frame(width: geo.size.width, height: geo.size.height,alignment: .bottom)
}
.padding(.vertical)
.background(Color.black, alignment: .bottom)
.ignoresSafeArea(.all)
}
}
extension LoginView{
var topImage:some View{
Image("header2")
.resizable()
.overlay( ImageOverlay() , alignment: .center)
.background(Color.black)
}
var usernameTextField:some View{
HStack{
TextField("Phone number, username or email", text: $LoginViewM.username)
.font(.custom("Montserrat-Regular", fixedSize: 16.4))
.foregroundColor(.white)
.placeholder(Text("Phone number, username or email").padding().foregroundColor(.white), show: LoginViewM.password.isEmpty)
.background(Color.black)
// need to hide this icon
Image("menuCloseSmall")
.padding(.init(top: 0, leading: 0, bottom: 0, trailing: 14))
}
.overlay(RoundedRectangle(cornerRadius: 4).stroke(Color.gray, lineWidth: 3))
.clipShape(RoundedRectangle(cornerRadius: 4))
.padding()
}
var errorUsernameLabel:some View{
Text(" Hey you, no user found. Make sure you’ve provided the \n correct information.")
.foregroundColor(.redBlueParrot)
.font(.custom("Montserrat-Regular", size: 12.1))
}
var passwordTextField:some View{
HStack{
TextField("Password", text: $LoginViewM.password)
.font(.custom("Montserrat-Regular", fixedSize: 16.4))
.padding(.bottom)
.foregroundColor(.white)
.placeholder(Text("Password").padding().foregroundColor(.white), show: LoginViewM.username.isEmpty)
.background(Color.black)
Image("editShow")
.padding()
}
.overlay(RoundedRectangle(cornerRadius: 4).stroke(Color.gray, lineWidth: 3))
.clipShape(RoundedRectangle(cornerRadius: 4))
.padding()
}
var errorPasswordLabel:some View{
Text(" Incorrect password for \(LoginViewM.password). Please try again.")
.foregroundColor(.redBlueParrot)
.font(.custom("Montserrat-Regular", size: 12.1))
}
var buttonLogin:some View{
Button(action: {
}) {
Text("Log In")
}
.font(.custom("Montserrat-Medium", size: 16))
.foregroundColor(Color.ligthGray)
.frame(width: 174, height: 42, alignment: .center)
.overlay(RoundedRectangle(cornerRadius: 18)
.stroke(Color.gray, lineWidth: 1.5))
}
var textInBottom:some View{
HStack(spacing:5){
Text("Forgot")
.foregroundColor(.white)
.font(.custom("Montserrat-Regular", size: 16))
Button("password"){
}
.foregroundColor(.yellow)
.font(.custom("Montserrat-Regular", size: 16))
Text("or")
.foregroundColor(.white)
.font(.custom("Montserrat-Regular", size: 16))
Button("username"){
}
.foregroundColor(.yellow)
.font(.custom("Montserrat-Regular", size: 16))
}
.padding()
}
}
struct LoginView_Previews: PreviewProvider {
static var previews: some View {
LoginView(LoginViewM: LoginViewModel())
}
}
That gap is for navigation bar's title gap. You can set .navigationTitle("Example") to see that. If you want to hide it you need to set .navigationBarHidden(true).
NavigationView {
VStack(spacing: 10) {
// ...
}
.navigationBarHidden(true) // <-- Here
}