Here is what I've done, but the problem is with Text background. It can be implemented on white background by setting the Text's background to white as well, but in case of image background it stays "strikedthrough". You can find a source code below where I tried to make it as close to the result as possible. How it could be resolved?
struct CustomTextField: View {
let placeholder: String
#Binding var text: String
var body: some View {
TextField("", text: $text)
.placeholder(when: $text.wrappedValue.isEmpty,
alignment: .leading,
placeholder: {
Text(placeholder)
.foregroundColor(.gray)
.font(.system(size: 20))
.padding(.leading, 15)
})
.foregroundColor(.gray)
.font(.system(size: 20))
.padding(EdgeInsets(top: 15, leading: 10, bottom: 15, trailing: 10))
.background {
ZStack {
RoundedRectangle(cornerRadius: 5)
.stroke(.gray, lineWidth: 1)
Text(placeholder)
.foregroundColor(.gray)
.padding(2)
.font(.caption)
.frame(maxWidth: .infinity,
maxHeight: .infinity,
alignment: .topLeading)
.offset(x: 20, y: -10)
}
}
}
}
Here is a solution using .trim on two RoundedRectangles based on the length of the label text, which should give you the result you want:
struct CustomTextField: View {
let placeholder: String
#Binding var text: String
#State private var width = CGFloat.zero
#State private var labelWidth = CGFloat.zero
var body: some View {
TextField(placeholder, text: $text)
.foregroundColor(.gray)
.font(.system(size: 20))
.padding(EdgeInsets(top: 15, leading: 10, bottom: 15, trailing: 10))
.background {
ZStack {
RoundedRectangle(cornerRadius: 5)
.trim(from: 0, to: 0.55)
.stroke(.gray, lineWidth: 1)
RoundedRectangle(cornerRadius: 5)
.trim(from: 0.565 + (0.44 * (labelWidth / width)), to: 1)
.stroke(.gray, lineWidth: 1)
Text(placeholder)
.foregroundColor(.gray)
.overlay( GeometryReader { geo in Color.clear.onAppear { labelWidth = geo.size.width }})
.padding(2)
.font(.caption)
.frame(maxWidth: .infinity,
maxHeight: .infinity,
alignment: .topLeading)
.offset(x: 20, y: -10)
}
}
.overlay( GeometryReader { geo in Color.clear.onAppear { width = geo.size.width }})
.onChange(of: width) { _ in
print("Width: ", width)
}
.onChange(of: labelWidth) { _ in
print("labelWidth: ", labelWidth)
}
}
}
Here is my version of the TextField.
struct TextInputField: View {
let placeHolder: String
#Binding var textValue: String
var body: some View {
ZStack(alignment: .leading) {
Text(placeHolder)
.foregroundColor(Color(.placeholderText))
.offset(y: textValue.isEmpty ? 0 : -25)
.scaleEffect(textValue.isEmpty ? 1: 0.8, anchor: .leading)
TextField("", text: $textValue)
}
.padding(.top, textValue.isEmpty ? 0 : 15)
.frame(height: 52)
.padding(.horizontal, 16)
.overlay(RoundedRectangle(cornerRadius: 12).stroke(lineWidth: 1).foregroundColor(.gray))
.animation(.default)
}
}
The above code is to create a CustomTextField named TextInputField. If you want to use the about component
struct ContentView: View {
#State var itemName: String = ""
var body: some View {
TextInputField(placeHolder: "Item Name": textValue: $itemName)
}
}
I'm using #ChrisR's answer as a base for my answer, so instead of doing all that calculation with two RoundedRectangles and label's width; you can position the Text on top and give it a background matching the app's background color
struct FloatingTitleTextField: View {
let placeholder: String
#Binding var text: String
var body: some View {
TextField("Placeholder", text: $text)
.foregroundColor(.gray)
.font(.system(size: 20))
.padding(EdgeInsets(top: 15, leading: 10, bottom: 15, trailing: 10))
.background {
ZStack {
RoundedRectangle(cornerRadius: 5)
.stroke(.black, lineWidth: 1)
Text(placeholder)
.foregroundColor(.gray)
.padding(2)
.background()
.frame(maxWidth: .infinity,
maxHeight: .infinity,
alignment: .topLeading)
.offset(x: 20, y: -10)
}
}
}
}
When calling the textfield you do it like this
FloatingTitleTextField(placeholder: "Placeholder", text: $text)
I also found this article very helpful
Related
I want to convert all #AppStorage's to Int(or even to Float) value and after that multiply it. Or probably i should to convert salaryPh and hoursPm to Int values, but i also can't do it. I've tried to create function where i could change String to Int but it didn't help me
I think that i'm doing something wrong, tried to find the solution with same question and found only one, but it didn't help me(link)
import SwiftUI
struct HomeView: View {
var body: some View {
NavigationView {
ExtractedView()
}
}
}
struct ExtractedView: View {
#State var monthString = Date().getMonthString().lowercased().capitalized
#State private var salaryPh: String = ""
#State private var hoursPm: String = ""
#AppStorage("SALARY_KEY") var savedSalary = ""
#AppStorage("HOURS_KEY") var savedHoursPm = ""
var body: some View {
ZStack {
BackgroundView()
CalendarView()
RoundedRectangle(cornerRadius: 40)
.frame(width: 225, height: 100)
.foregroundColor(.blue)
.offset(y: 190)
VStack(alignment: .center, spacing: 13) {
Text("Your netto-salary per hour")
.foregroundColor(Color.white)
.font(Font.system(size: 23, design: .rounded))
.fontWeight(.light)
TextField("Salary", text: $salaryPh)
.frame(width: 100, height: 50)
.background(.black)
.opacity(0.5)
.foregroundColor(Color.white)
.multilineTextAlignment(.center)
.font(.system(.body, design: .monospaced))
.overlay (
RoundedRectangle(cornerRadius: 15)
.stroke(Color.blue, lineWidth: 4)
)
.onChange(of: salaryPh) { salaryPh in
self.savedSalary = salaryPh
}
.onAppear {
self.salaryPh = savedSalary
print("Loaded: \(savedSalary)")
}
}
.offset(y: -150)
VStack(alignment: .center, spacing: 13) {
Text("Hours in this month")
.foregroundColor(Color.white)
.font(Font.system(size: 23, design: .rounded))
.fontWeight(.light)
TextField("Hours", text: $hoursPm)
.foregroundColor(Color.white)
.frame(width: 100, height: 50)
.background(.black)
.opacity(0.5)
.multilineTextAlignment(.center)
.font(.system(.body, design: .monospaced))
.overlay (
RoundedRectangle(cornerRadius: 15)
.stroke(Color.blue, lineWidth: 4)
)
.onChange(of: hoursPm) { hoursPm in
self.savedHoursPm = hoursPm
}
.onAppear {
self.hoursPm = savedHoursPm
print("Loaded: \(savedHoursPm)")
}
}
.offset(y: -20)
VStack(spacing: 20) {
Text("In \(monthString) i make:")
.foregroundColor(Color.white)
.font(Font.system(size: 23, design: .rounded))
.fontWeight(.light)
}
.offset(y: 165)
}
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}
You can get rid of all the conversion by using TextField with value.
TextField(
"Double",
value: $myDouble,
format: .number
)
This setup will work with Int too.
Once the TextField is compatible with numbers you can switch all the Strings to be the correct number type.
https://developer.apple.com/documentation/swiftui/textfield/init(_:value:format:prompt:)-3fh51
I'm new to SwiftUI and iOS and I'm having trouble getting my VStack to stack items properly. Mainly, it looks like the image on the top left is too big. Here's a screenshot of the size of the image in my layout:
This is what it should look like:
This is the SwiftUI code:
#State private var email: String = ""
#State private var password: String = ""
#State private var isEditing = false
var body: some View {
return VStack(content: {
Image("UIBubble")
.resizable()
.frame(width: 217, height: 184, alignment: .leading)
.position(x: 108, y: 92);
Text("PET SUPPORT").font(Font.custom("Permanent Marker", size: 36))
.foregroundColor(Color.petSupportText)
.padding()
.frame(width: .infinity, height: 0, alignment: .center);
Text("EMAIL").font(Font.custom("Permanent Marker", size: 18))
.padding(.top, 20)
.padding(.horizontal, 0)
.frame(width: .infinity, height: 0, alignment: .leading);
TextField("Email", text: $email) {
isEditing in self.isEditing = isEditing
}
.autocapitalization(.none)
.disableAutocorrection(true)
.padding(.horizontal, 30)
.padding(.top, 20);
Divider()
.padding(.horizontal, 30)
.foregroundColor(.black);
Text("PASSWORD").font(Font.custom("Permanent Marker", size: 18)).padding(.top, 50).padding(.horizontal, 0).frame(width: .infinity, height: 20, alignment: .leading);
TextField("Password", text: $password) {
isEditing in self.isEditing = isEditing
}
.autocapitalization(.none)
.disableAutocorrection(true)
.padding(.horizontal, 30)
.padding(.top, 20)
Divider().padding(.horizontal, 30).foregroundColor(.black);
})
.frame(maxWidth: .infinity, maxHeight: .infinity)
.ignoresSafeArea()
}
Image giving me trouble:
The image itself is 217x184.
You should use ZStack for this work or send the Image to background like in code:
Also I made some code for you to make you free to use that file Image and you get same and much more better result with system shape. you can change the Color, shape, size, number of bubble and every thing . .
with usage of contrast: make the Color like your Color
import SwiftUI
struct ContentView: View {
#State private var email: String = ""
#State private var password: String = ""
#State private var showPassword = false
var body: some View {
return VStack(content: {
Spacer()
.frame(height: 100)
Text("PET SUPPORT")
.font(Font.custom("Permanent Marker", size: 36))
.padding()
Group {
HStack {
Text("EMAIL")
.font(Font.custom("Permanent Marker", size: 18))
.padding(.top, 20)
Spacer()
}
TextField("Email", text: $email)
.autocapitalization(.none)
.disableAutocorrection(true)
.keyboardType(.emailAddress) // <<: Here: A type optimized for multiple email address entry (shows space # . prominently).
.padding(.top, 20)
Divider()
.foregroundColor(.black)
}
Group {
HStack {
Text("PASSWORD")
.font(Font.custom("Permanent Marker", size: 18))
.padding(.top, 50)
Spacer()
}
ZStack {
if showPassword {
TextField("Password", text: $password)
}
else {
SecureField("Password", text: $password)
}
}
.frame(height: 20)
.overlay(Image(systemName: showPassword ? "eye.slash" : "eye").onTapGesture { showPassword.toggle() }, alignment: .trailing)
.autocapitalization(.none)
.disableAutocorrection(true)
.padding(.top, 20)
Divider()
.foregroundColor(.black)
}
Group {
Button("Sign Up") {}
.padding()
Button("Login") {}
.padding()
}
Spacer()
})
.padding(.horizontal, 30)
.background(bubble, alignment: .topLeading)
.background(bubble.rotationEffect(Angle(degrees: 180)), alignment: .bottomTrailing)
.ignoresSafeArea()
.statusBar(hidden: true)
}
var bubble: some View { // <<: you can put this part out and use update code for this part
Image("UIBubble")
.resizable()
.scaledToFit()
.frame(width: 217, height: 184, alignment: .leading)
}
}
Update: you can replace that file Image with high quality system shape like this code!
var bubble: some View {
ZStack {
Circle()
.fill(Color(UIColor.systemTeal).opacity(0.2)) // <<: Here: opacity = 0.2
.frame(width: 300, height: 300, alignment: .center)
.offset(x: -100, y: -180)
Circle()
.fill(Color(UIColor.systemTeal).opacity(0.2)) // <<: Here: opacity = 0.2
.frame(width: 300, height: 300, alignment: .center)
.offset(x: -180, y: -100)
}
.contrast(3.0) // <<: Here: This make your Color POP out!
}
Update 2: with this down code your Circle/bubble came to live and they move with smooth animation.
#State private var startAnimation: Bool = false
var bubble: some View {
ZStack {
Circle()
.fill(Color(UIColor.systemTeal).opacity(0.2)) // <<: Here: opacity = 0.2
.frame(width: 300, height: 300, alignment: .center)
.offset(x: startAnimation ? -110 : -100, y: startAnimation ? -180 : -150)
Circle()
.fill(Color(UIColor.systemTeal).opacity(0.2)) // <<: Here: opacity = 0.2
.frame(width: 300, height: 300, alignment: .center)
.offset(x: startAnimation ? -180 : -150, y: startAnimation ? -90 : -100)
}
.contrast(3.0) // <<: Here: This make your Color POP out!
.onAppear() { startAnimation = true }
.animation(Animation.easeInOut(duration: 3.0).repeatForever(autoreverses: true))
}
Seems like you can just use background(alignment:content:) modifier
So, add this background modifiers to your view:
VStack {
...
}
.background(alignment: .topLeading) {
Image("UIBubble")
}
.background(alignment: .bottomTrailing) {
Image("UIBubble2"),
}
In my opinion this solution is the most swifty
I am trying to achieve this layout https://gyazo.com/9714f8f1bff98edb3365338563b28fe8 in Swift UI using V and H Stacks. So far I have achieved this https://gyazo.com/fec9ae229e59a41add6542c9a8ad31af. I haven't found the right approach just yet on what to do here for more manipulation. What properties would help me achieve the desired layout?
Update! Here is what I have figured out so far... Almost there.
UPDATED CODE AND CANVAS HERE: https://gyazo.com/d12b9a831f50c25f8f854dc2143c93dc
import SwiftUI
struct CustomBlock: View {
var leading: String
var trailing: String
var body: some View {
VStack {
HStack (alignment: .firstTextBaseline, spacing: 18){
Button(action: {}){
Text(leading)
.bold()
.font(Font.custom("Helvetica Neue", size: 21.0))
.padding(25)
.foregroundColor(Color.white)
.background(Color.green)
.cornerRadius(12)
.multilineTextAlignment(.center)
.fixedSize()
.frame(width: 150, height: 50, alignment: .center)
}
Button(action: {}){
Text(trailing)
.bold()
.font(Font.custom("Helvetica Neue", size: 21.0))
.padding(25)
.foregroundColor(Color.white)
.background(Color.green)
.cornerRadius(12)
.multilineTextAlignment(.center)
.fixedSize()
.frame(width: 150, height: 50, alignment: .center)
}
}
}.padding()
}
}
struct ContentView: View {
var body: some View {
ZStack {
Color.init(hue: 0.2722, saturation: 0.89, brightness: 0.29, opacity: 1.0) .edgesIgnoringSafeArea(.all)
VStack {
HStack {
StrokeText(text: "Dividend Chaser", width: 0.5, color: .black)
.foregroundColor(.white)
.font(.system(size: 45, weight: .bold))
}
ZStack {
RoundedRectangle(cornerRadius: 25, style: .continuous)
.fill( Color.init(red: 0.33, green: 0.56, blue: 0.27))
.frame(width: 350, height: 240)
}
CustomBlock(leading: "Today's List", trailing: "Tomorrow's List").lineLimit(2)
CustomBlock(leading: "This Month", trailing: "Next Month").lineLimit(2)
CustomBlock(leading: "3% Yeild Or Higher", trailing: "5% Yeild Or Higher").lineLimit(2)
CustomBlock(leading: "App Help", trailing: "More Apps").lineLimit(2)
}
}
}
}
Here is a demo of approach. Tested with Xcode 12 / iOS 14.
struct DemoButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.foregroundColor(.white).font(Font.body.bold())
.frame(maxWidth: .infinity).padding(.vertical, 10)
.background(RoundedRectangle(cornerRadius: 8).fill(Color.green))
.multilineTextAlignment(.center)
.frame(maxWidth: .infinity)
}
}
struct TestButtonsGridLayout: View {
var body: some View {
VStack(spacing: 1) {
HStack(spacing: 1) {
Button("Today's\nList") {}
Button("Tomorrow's\nList") {}
}
HStack(spacing: 1) {
Button("This\nMonth") {}
.overlay(RoundedRectangle(cornerRadius: 8)
.stroke(lineWidth: 4).foregroundColor(.white).padding(2))
Button("Next\nMonth") {}
}
// .. other same here
}.buttonStyle(DemoButtonStyle())
}
}
Note: a) button style can be applied per-button b) show overlay selection can be also moved in style and activated by some state variable.
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
I need to make such a simple thing, but can't figure out how.
So I need to create a View which I already have inside another view. Here how it looks like now ↓
Here's my button:
struct CircleButton: View {
var body: some View {
Button(action: {
self
}, label: {
Text("+")
.font(.system(size: 42))
.frame(width: 57, height: 50)
.foregroundColor(Color.white)
.padding(.bottom, 7)
})
.background(Color(#colorLiteral(red: 0.4509803922, green: 0.8, blue: 0.5490196078, alpha: 1)))
.cornerRadius(50)
.padding()
.shadow(color: Color.black.opacity(0.15),
radius: 3,
x: 0,
y: 4)
}
}
Here's a view which I want to place when I tap the button ↓
struct IssueCardView: View {
var body: some View {
ZStack (alignment: .leading) {
Rectangle()
.fill(Color.white)
.frame(height: 50)
.shadow(color: .black, radius: 20, x: 0, y: 4)
.cornerRadius(8)
VStack (alignment: .leading) {
Rectangle()
.fill(Color(#colorLiteral(red: 0.6550863981, green: 0.8339114785, blue: 0.7129291892, alpha: 1)))
.frame(width: 30, height: 8)
.cornerRadius(8)
.padding(.horizontal, 10)
Text("Some text on card here")
.foregroundColor(Color(UIColor.dark.main))
.font(.system(size: 14))
.fontWeight(.regular)
.padding(.horizontal, 10)
}
}
}
}
Here's a view where I want to place this IssueCardView ↓. Instead of doing it manually like now I want to generate this View with button.
struct TaskListView: View {
var body: some View {
ScrollView(.vertical, showsIndicators: false, content: {
VStack (alignment: .leading, spacing: 8) {
**IssueCardView()
IssueCardView()
IssueCardView()
IssueCardView()
IssueCardView()**
}
.frame(minWidth: 320, maxWidth: 500, minHeight: 500, maxHeight: .infinity, alignment: .topLeading)
.padding(.horizontal, 0)
})
}
}
Although it works with scrollView and stack, You should use a List for these kind of UI issues (as you already mentioned in the name of TaskListView)
struct TaskListView: View {
typealias Issue = String // This is temporary, because I didn't have the original `Issue` type
#State var issues: [Issue] = ["Some issue", "Some issue"]
var body: some View {
ZStack {
List(issues, id: \.self) { issue in
IssueCardView()
}
CircleButton {
self.issues += ["new issue"]
}
}
}
}
I have added let action: ()->() to CircleButton. So I can pass the action to it.