I'm trying to align text in two different HStacks but having difficulties getting the second (shorter length) one to align with the first HStack. I've tried using .frame(minWidth:0, maxWidth: .infinity) and Spacer()'s but can't find a solution. Is this possible at all?
struct ContentView: View {
var body: some View {
VStack(alignment: .leading) {
HStack {
Spacer()
Text("Lorem ipsum dolor")
.font(.system(size: 22, weight: .ultraLight, design: .rounded))
Spacer()
}.padding()
.overlay(
Rectangle()
.stroke(Color(#colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1)), style: StrokeStyle(lineWidth: 0.5, dash: [5.0])))
HStack {
Spacer()
Text("Lorem ipsum")
.font(.system(size: 22, weight: .ultraLight, design: .rounded))
Spacer()
}.padding()
.overlay(
Rectangle()
.stroke(Color(#colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1)), style: StrokeStyle(lineWidth: 0.5, dash: [5.0])))
}
}
}
I attempted to use a HorizontalAlignment guide, but they became off-centred:
struct ContentView: View {
var body: some View {
VStack(alignment: .controlLeadingEdge) {
HStack {
Text("Lorem ipsum dolor")
.font(.system(size: 22, weight: .ultraLight, design: .rounded))
.alignmentGuide(.controlLeadingEdge, computeValue: { d in d[HorizontalAlignment.leading] })
}.padding()
.frame(minWidth: 0, maxWidth: 300)
.overlay(
Rectangle()
.stroke(Color(#colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1)), style: StrokeStyle(lineWidth: 0.5, dash: [5.0])))
HStack {
Text("Lorem ipsum")
.font(.system(size: 22, weight: .ultraLight, design: .rounded))
.alignmentGuide(.controlLeadingEdge, computeValue: { d in d[HorizontalAlignment.leading] })
}.padding()
.frame(minWidth: 0, maxWidth: 300)
.overlay(
Rectangle()
.stroke(Color(#colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1)), style: StrokeStyle(lineWidth: 0.5, dash: [5.0])))
}
}
}
extension HorizontalAlignment {
private enum ControlAlignment: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
return context[HorizontalAlignment.controlLeadingEdge]
}
}
static let controlLeadingEdge = HorizontalAlignment(ControlAlignment.self)
}
Here is one way to do it:
Repeat the first text, give it .opacity(0) and put it in a ZStack with .leading alignment
struct ContentView: View {
var body: some View {
let firstText = Text("Loren ipsum dolor")
let secondText = Text("Loren ipsum")
VStack(alignment: .leading) {
HStack {
Spacer()
firstText
.font(.system(size: 22, weight: .ultraLight, design: .rounded))
Spacer()
}.padding()
.overlay(
Rectangle()
.stroke(Color(#colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1)), style: StrokeStyle(lineWidth: 0.5, dash: [5.0])))
HStack {
Spacer()
ZStack(alignment: .leading) {
firstText.opacity(0)
secondText
}
.font(.system(size: 22, weight: .ultraLight, design: .rounded))
Spacer()
}.padding()
.overlay(
Rectangle()
.stroke(Color(#colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1)), style: StrokeStyle(lineWidth: 0.5, dash: [5.0])))
}
}
}
Related
I am trying to make a list of buttons/links that all have the same width, regardless of text content. Unfortunately, it seems like the Link() items do not respect the .frame(maxWidth) settings and just do whatever they want. I cannot find any resources online that describe how to do this, so I am turning here to see if anyone can help me determine the best way to line these up to be the same width.
My code below:
VStack(alignment: .center) {
Button {
bypass.toggle()
} label: {
Text("Push to Bypass")
.frame(maxWidth: .infinity)
}
.font(.title2)
.fontWeight(.bold)
.buttonStyle(.borderedProminent)
.foregroundColor(Color(hue: 1.0, saturation: 1.0, brightness: 1.0, opacity: 0.825))
.tint(Color(red: 0.005, green: 0.669, blue: 0.791))
Link("Settings", destination: URL(string: "http://192.168.10.1/TSettings")!)
.frame(maxWidth: .infinity)
.font(.title2)
.fontWeight(.bold)
.buttonStyle(.borderedProminent)
.foregroundColor(.white)
.tint(Color(red: 0.005, green: 0.669, blue: 0.791))
Link("Recorder Settings", destination: URL(string: "http://192.168.10.1/VB")!)
.frame(maxWidth: .infinity)
.font(.title2)
.fontWeight(.bold)
.buttonStyle(.borderedProminent)
.foregroundColor(.white)
.tint(Color(red: 0.005, green: 0.669, blue: 0.791))
Link("Download Data", destination: URL(string: "http://192.168.10.1/ddownload")!)
.frame(maxWidth: .infinity)
.font(.title2)
.fontWeight(.bold)
.buttonStyle(.borderedProminent)
.foregroundColor(.white)
.tint(Color(red: 0.005, green: 0.669, blue: 0.791))
Link("Download Audio", destination: URL(string: "http://192.168.10.1/generate")!)
.frame(maxWidth: .infinity)
.font(.title2)
.fontWeight(.bold)
.buttonStyle(.borderedProminent)
.foregroundColor(.white)
.tint(Color(red: 0.005, green: 0.669, blue: 0.791))
}.fixedSize(horizontal: true, vertical: false)
What it currently looks like:
Add this to your struct:
#Environment(\.openURL) var openURL
Use a regular button and apply the desired modifiers:
Button {
openURL(URL(string: "http://192.168.10.1/TSettings")!)
} label: {
Text("Settings")
.frame(maxWidth: .infinity)
}
Get rid of .fixedSize(horizontal: true, vertical: false) at the end and use Link with label: init:
Link(destination: URL(string: "http://192.168.10.1/TSettings")!) {
Text("Settings")
.frame(maxWidth: .infinity)
}
.font(.title2)
.fontWeight(.bold)
.buttonStyle(.borderedProminent)
.foregroundColor(.white)
.tint(Color(red: 0.005, green: 0.669, blue: 0.791))
I want the space progress bar between the edge of the phone to be 24. I want the size of the space to be 24. I also want the text to be aligned with the end of the bar.
my code is like this:
struct SeasonsDetailsProgressBar: View {
var bar : Float = 0.5
var body: some View {
GeometryReader{ proxy in
HStack(spacing: 22){
Text("Step")
.bold()
.font(.system(size: 12))
Spacer()
VStack(alignment: .leading){
HStack{
Text("Total steps taken")
.font(.system(size: 10))
Spacer()
Text("3.214.629 / 5.000.0000")
.font(.system(size: 10))
}
ZStack(alignment:.leading){
RoundedRectangle(cornerRadius: 4, style: .continuous)
.frame(width: proxy.size.width, height: 4)
.foregroundColor(Color.black.opacity(0.1))
RoundedRectangle(cornerRadius: 4, style: .continuous)
.frame(width: proxy.size.width * CGFloat(bar), height: 4)
.foregroundColor(Color.init(red: 0.965, green: 0.224, blue: 0.49))
}.padding(.trailing ,
proxy.size.width * 0.9)
}
}
.padding(.leading, 25)
.padding(.trailing, 25)
}
}
}
proxy.size.width gives us a width of screen you have to minus size of label and spacing between them.
struct SeasonsDetailsProgressBar: View {
var bar : Float = 0.5
var body: some View {
GeometryReader{ proxy in
HStack(spacing: 0) { // -> No spacing
Text("Step")
.bold()
.font(.system(size: 12))
.frame(width: 50, alignment: .leading) // exact width of "Step" label
.padding(.leading, 24). // exactly 24 from left side
VStack(alignment: .leading){
HStack{
Text("Total steps taken")
.font(.system(size: 10))
Spacer()
Text("3.214.629 / 5.000.0000")
.font(.system(size: 10))
.padding(.trailing, 24) // exactly 24 from left side
}
ZStack(alignment:.leading){
RoundedRectangle(cornerRadius: 4, style: .continuous)
.frame(width: (proxy.size.width - 98), height: 4) // width of screen -(24 left + 50 label + 24 right)
.foregroundColor(Color.black.opacity(0.1))
RoundedRectangle(cornerRadius: 4, style: .continuous) // width-98
.frame(width: (proxy.size.width - 98) * CGFloat(bar), height: 4)
.foregroundColor(Color.init(red: 0.965, green: 0.224, blue: 0.49))
}
}
}
} // no paddings
}
}
The problem is that you use top GeometryReader which is occupied all available space and its width is full screen width.
You need to use another GeometryReader inside your ZStack which width will be available space for your progress bar. Also you need to setup height, because otherwise it will occupied all available space like Spacer component
ZStack(alignment: .leading) {
GeometryReader { proxy in
RoundedRectangle(cornerRadius: 4, style: .continuous)
.frame(width: proxy.size.width, height: 4)
.foregroundColor(Color.black.opacity(0.1))
RoundedRectangle(cornerRadius: 4, style: .continuous)
.frame(width: proxy.size.width * CGFloat(bar), height: 4)
.foregroundColor(Color.init(red: 0.965, green: 0.224, blue: 0.49))
}.frame(height: 4)
}
And its better to use padding directly on your label and progress bar, finally it will be:
struct SeasonsDetailsProgressBar: View {
var bar : Float = 0.5
var body: some View {
HStack(spacing: 22) {
Text("Step")
.bold()
.font(.system(size: 12))
.padding(.leading, 24)
Spacer()
VStack(alignment: .leading) {
HStack{
Text("Total steps taken")
.font(.system(size: 10))
Spacer()
Text("3.214.629 / 5.000.0000")
.font(.system(size: 10))
}
ZStack(alignment:.leading) {
GeometryReader{ proxy in
RoundedRectangle(cornerRadius: 4, style: .continuous)
.frame(width: proxy.size.width, height: 4)
.foregroundColor(Color.black.opacity(0.1))
RoundedRectangle(cornerRadius: 4, style: .continuous)
.frame(width: proxy.size.width * CGFloat(bar), height: 4)
.foregroundColor(Color.init(red: 0.965, green: 0.224, blue: 0.49))
}.frame(height: 4)
}
}.padding(.trailing, 24)
}
}
}
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)
}
}
}
I've created a progress circle for my App, that will count down from 28 to 0 (number of days until Payday)
I've got the circles to display how I'd like, but I want it to animate when I navigate to the view, is there a way to do this?
More a UI question, but do you think maybe an arrow within the circle to indicate the direction would look any good?
P.s - any ideas how to truncate all the zeros off my Double?
image of how this looks
Thanks!
ZStack {
let progressPeriod = Double(PayData.daysUntilPay) ?? 0
let progressPeriod2 = 1 - (progressPeriod / 28)
Circle()
.stroke(lineWidth: 30.0)
.opacity(0.3)
.foregroundColor(Color(red: 0.941, green: 0.426, blue: 0.004))
.frame(width:150)
.frame(height: 200)
Circle()
.trim(from: 0.0, to: progressPeriod2)
.stroke(style: StrokeStyle(lineWidth: 30.0, lineCap: .round, lineJoin: .round))
.foregroundColor(Color(red: 0.941, green: 0.426, blue: 0.004))
.rotationEffect(Angle(degrees: 270.0))
.frame(width:150)
.frame(height: 200)
Text("\(progressPeriod) days")
.bold()
}
Try to combine following code with your countdown:
struct ContentView: View {
#State var progressValue: Float = 0.0
var body: some View {
ZStack {
Color.yellow
.opacity(0.1)
.edgesIgnoringSafeArea(.all)
VStack {
ProgressBar(progress: self.$progressValue)
.frame(width: 150.0, height: 150.0)
.padding(40.0)
Button(action: {
self.incrementProgress()
}) {
HStack {
Image(systemName: "plus.rectangle.fill")
Text("Increment")
}
.padding(15.0)
.overlay(
RoundedRectangle(cornerRadius: 15.0)
.stroke(lineWidth: 2.0)
)
}
Spacer()
}
}
}
func incrementProgress() {
let randomValue = Float([0.012, 0.022, 0.034, 0.016, 0.11].randomElement()!)
self.progressValue += randomValue
}
}
struct ProgressBar: View {
#Binding var progress: Float
var body: some View {
ZStack {
Circle()
.stroke(lineWidth: 20.0)
.opacity(0.3)
.foregroundColor(Color.red)
Circle()
.trim(from: 0.0, to: CGFloat(min(self.progress, 1.0)))
.stroke(style: StrokeStyle(lineWidth: 20.0, lineCap: .round, lineJoin: .round))
.foregroundColor(Color.red)
.rotationEffect(Angle(degrees: 270.0))
.animation(.linear)
Text(String(format: "%.0f %%", min(self.progress, 1.0)*100.0))
.font(.largeTitle)
.bold()
}
}
}
I'm trying to get the last styling done on my Widget but I just can't seem to get the number "+1.75" and the Orange dot to align with the "Wheat" text. How do I properly align this row?
I've tried using Spacer() but it just doesn't seem to work?
struct CropPriceView: View {
#Environment(\.widgetFamily) var family: WidgetFamily
var body: some View {
if family == .systemSmall{
VStack{
HStack{
VStack{
HStack{
Circle()
.fill(Color(#colorLiteral(red: 0.9333333333, green: 0.6784313725, blue: 0.262745098, alpha: 1)))
.frame(width: 10, height: 10)
VStack(alignment: .leading){
Text("Wheat")
.foregroundColor(Color(#colorLiteral(red: 0.168627451, green: 0.1647058824, blue: 0.1647058824, alpha: 1)))
.font(Font.custom("TPFrank-Bold", size: 14))
.lineSpacing(0.18)
Text("MATIF")
.font(Font.custom("TPFrank-Regular", size: 12))
.foregroundColor(Color(#colorLiteral(red: 0.5921568627, green: 0.5921568627, blue: 0.5921568627, alpha: 1)))
.lineSpacing(0.15)
}
Spacer()
}
}
Spacer()
Text("+1.75")
.foregroundColor(Color(#colorLiteral(red: 0.4431372549, green: 0.7490196078, blue: 0.3882352941, alpha: 1)))
.font(Font.custom("TPFrank-Medium", size: 14))
.lineSpacing(0.35)
}
Spacer()
HStack{
Spacer()
Text("168.02")
.foregroundColor(Color(#colorLiteral(red: 0.168627451, green: 0.1647058824, blue: 0.1647058824, alpha: 1)))
.font(Font.custom("TPFrank-Bold", size: 22))
.lineSpacing(0.55)
}
}.padding(EdgeInsets(top: 15, leading: 15, bottom: 15, trailing: 15))
}
}
}
Hopefully this helps, let me know if it works for you. I believe you were looking for .firstTextBaseline to align with the first Text.
struct Example_WidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
VStack {
HStack(alignment: .firstTextBaseline) {
Image(systemName: "circle.fill")
.resizable()
.frame(width: 10, height:10)
.foregroundColor(Color.orange)
VStack (alignment: .leading){
Text("Wheat")
.font(.subheadline)
.fontWeight(.bold)
.allowsTightening(true)
Text("MATIF")
.font(.footnote)
.fontWeight(.bold)
.foregroundColor(Color.secondary)
.allowsTightening(true)
}
Spacer()
Text("+1.75")
.font(.footnote)
.fontWeight(.bold)
.foregroundColor(Color.green)
.allowsTightening(true)
}
Spacer()
HStack {
Spacer()
Text("168.02")
.font(.title)
.fontWeight(.bold)
}
}
.padding(.all, 10)
}
}
Set HStack alignment: .top
var body: some View {
if family == .systemSmall{
VStack{
HStack(alignment: .top) { //<<--Here
VStack{
HStack(alignment: .top) { //<<--Here