SwiftUI linear gauge - swiftui

UPDATE
I was able to get something closer to what I'm trying to achieve. Still needs work. I attempted to use a rounded rectangle for the mask but doesn't look good. Any suggestions would be greatly appreciated.
GeometryReader { geometry in
RoundedRectangle(cornerRadius: 7.5, style: .circular)
.fill(LinearGradient(gradient: Gradient(colors: [Color.green, Color.yellow, Color.orange, Color.red, Color.purple]), startPoint: .bottom, endPoint: .top))
.frame(width: geometry.size.width, height: geometry.size.height, alignment: .bottomLeading)
.overlay(Color.black.opacity(0.35).cornerRadius(7.5))
RoundedRectangle(cornerRadius: 7.5, style: .circular)
.fill(LinearGradient(gradient: Gradient(colors: [Color.green, Color.yellow, Color.orange, Color.red, Color.purple]), startPoint: .bottom, endPoint: .top))
.frame(width: geometry.size.width, height: geometry.size.height, alignment: .bottomLeading)
.mask(
VStack {
Spacer()
Rectangle()
// Adjust value 1 to needs
.frame(width: geometry.size.width, height:geometry.size.height * (CGFloat(1) / CGFloat(11)), alignment: .bottom)
})
}
.frame(width: 15, height: .infinity)
.padding(.all, 10)
Older
I'm attempting to create a simple gauge like the linear gauge in watchOS. The trouble I'm having is figuring out the proper way to overlay the current value. The outcome I'd like to see is below. I'm sure there's a better way.
Outcome
Mine look absolutely terrible
ZStack {
LinearGradient(gradient: Gradient(colors: [Color.green, Color.yellow, Color.orange, Color.red, Color.purple]), startPoint: .bottom, endPoint: .top)
GeometryReader { metrics in
Circle()
.stroke(Color.white,lineWidth: 2)
.frame(minHeight: 0, maxHeight: metrics.size.height)
.foregroundColor(Color.clear)
.position(x: metrics.size.width / 2 ,y: metrics.size.height * (CGFloat(10) / CGFloat(11)))
}
}
.cornerRadius(7.5)
.frame(minWidth: 0, maxWidth: 15)

I've solved my solution. I managed to create the same view by making a custom ProgressView style.

Related

Why does the blur modifier affect views differently?

I spent a lot of time trying to figure out whether this was a bug, but I couldn't for the life of me figure out why a shape wasn't blurring correctly.
VStack {
//Doesn't blur right
Capsule()
.fill(LinearGradient(colors: [Color.red, Color.blue], startPoint: .leading, endPoint: .trailing))
.frame(width: 80, height: 40)
.blur(radius: 10)
//Does blur correctly
HStack {
}
.frame(width: 80, height: 40)
.background(
LinearGradient(colors: [Color.red, Color.blue], startPoint: .leading, endPoint: .trailing)
)
.clipShape(Capsule())
.blur(radius: 10)
}
When you apply blur to an empty HStack, it blurs correctly, while using a blur to a shape doesn't. Can someone explain why it differs?
Firstly, two of them looks like the same but it's not the same.
From Apple docs, Capsule() is subclass of Shape which is subclass of View.
The first one, when you using Capsule() directly then you .fill means just like you add subview LinearGradient with colors into Capsule(). That's the reason I think .blur not working correctly when you call directly from Capsule().
The second one, you .blur correctly because the view was directly be drawn on view ( just like drawing in layer of view in normal UIKit)
The view hierarchy show the different between two of them.
The solution:
You can do like your second view
HStack {
}
.frame(width: 80, height: 40)
.background(
LinearGradient(colors: [Color.red, Color.blue], startPoint: .leading, endPoint: .trailing)
)
.clipShape(Capsule())
.blur(radius: 10)
Or you can make linear gradient with mask
HStack {
LinearGradient(gradient: Gradient(colors: [.red, .blue]), startPoint: .leading, endPoint: .trailing)
.mask(
Capsule()
.blur(radius: 10)
.frame(width: 80, height: 40)
)
}.frame(width: 200, height: 60)
Both will act the same like you draw on the layer of view
UPDATE
If you need it works directly, just use only one color then call .foregroundColor then blur will continue work - But only one color at the time only.
Capsule()
.foregroundColor(Color.red)
.blur(radius: 10)
.frame(width: 80, height: 40)

Implementing Box-Shadow in SwiftUI

I'm trying to implement the following css code in SwiftUI
background: #FFFFFF;
box-shadow: 0px 10px 20px rgba(223, 227, 240, 0.2);
border-radius: 27.5px;
It should look like this (barely visible shadow below the button):
Here's the code i'm currently experimenting with:
import SwiftUI
struct RegisterView: View {
var body: some View {
VStack(spacing: 0) {
HStack(alignment: .top) {
Text("Register")
.font(.custom("NotoSans-Regular", size: 24))
.fontWeight(.bold)
.foregroundColor(Color.tertiaryTitleColor)
}
.frame(width: 299, height: 39)
.padding(.top, 78)
HStack {
BroviderButton(imageName: "googleLogo")
BroviderButton(imageName: "facebookLogo")
}
.padding(.top, 47)
Spacer()
}
.frame(maxHeight: .infinity)
.frame(maxWidth: .infinity)
.background(Color.loginBackgroundColor)
.ignoresSafeArea()
}
}
struct RegisterView_Previews: PreviewProvider {
static var previews: some View {
RegisterView()
}
}
struct BroviderButton: View {
var imageName: String
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 27.5)
.stroke(Color.dropShadowColor,lineWidth: 1)
.frame(width: 133, height: 56, alignment: .center)
.shadow(color: .black, radius: 2, x: 1, y: 2)
VStack {
VStack {
Image(imageName)
.resizable()
.scaledToFit()
}
.frame(height: 28, alignment: .center)
}
.frame(width: 133, height: 56, alignment: .center)
.foregroundColor(.blue)
.background(Color.loginButtonBackgroundColor)
.cornerRadius(27.5)
.opacity(1)
}
}
}
And here's how it looks like atm:
I don't understand how to translate x:10 & y:20 to SwiftUI code and also can't find the way to blur the shadow (20%). Any ideas are welcome.
Also can't figure out why that RoundedRectangle stays visible after i put VStack on top of it.. I was thinking it will hide everything but the shadow below the button.
As far as I see there are two issues here to be fixed (although of course I don't have your colors and images):
ZStack {
RoundedRectangle(cornerRadius: 27.5)
.fill(Color.dropShadowColor) // << here fill, not stroke !!
.frame(width: 133, height: 56, alignment: .center)
.shadow(color: .black, radius: 2, x: 0, y: 2) // << no offset by x
VStack {
VStack {
Image(imageName)
.resizable()
.scaledToFit()
}
.frame(height: 28, alignment: .center)
}
.frame(width: 133, height: 56, alignment: .center)
.foregroundColor(.blue)
.background(Color.loginButtonBackgroundColor) // << should be opaque color !!
.cornerRadius(27.5)
.opacity(1)
}

How to mask a view with a (different) size constrained view in SwiftUI?

I'm trying to mask a RoundedRectangle (filled with a gradient) with some view to receive the following result:
The code I'm using is as follows:
VStack(alignment: .leading) {
RoundedRectangle(cornerRadius: 10)
.fill(LinearGradient(gradient: self.gradient, startPoint: .leading, endPoint: .trailing))
.frame(width: geometry.size.width, alignment: .top)
.mask(
//WHAT TO PLACE HERE?
)
RoundedRectangle(cornerRadius: 10)
.fill(LinearGradient(gradient: self.gradient, startPoint: .leading, endPoint: .trailing))
.frame(width: geometry.size.width, height: 8)
}
However when putting Rectangle().frame(width: geometry.size.width * 0.4, alignment: .leading) inside the .mask, it ignores the given frame width and simply displays the full RoundedRectangle:
I've tried other views inside the .mask to check if the clipping works at all.
I tried Circle().frame(alignment: .leading), which resulted in:
Notice how this doesn't adhere to the given alignment.
Then I tried Text("MASKING MASKING").frame(alignment: .trailing), which resulted in:
Notice how this does adhere to the given alignment.
I'm very confused as to how I can go about achieving the desired result since SwiftUI masking behaviour seems very unintuitive.
Here is demo of solution. Tested with Xcode 11.4 / iOS 13.4
.mask(
VStack {
Rectangle().frame(width: geometry.size.width * 0.4)
}.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
)

What is the purpose of .compositingGroup() in SwiftUI?

I cannot figure out what compositingGroup() is. At first, I thought it is something like Merging layers in Photoshop. But it was not. Because .shadow() effects to the overlay and background views respectively even if I use .compositingGroup().
So far, I've found 2 differences when I use .compositingGroup()
Text doesn't have shadows.
The shadow size of the overlay view is slightly smaller than the above one.
What is the purpose of compositingGroup?
struct ContentView: View {
var body: some View {
VStack(spacing: 50) {
Text("Without\ncompositing")
.font(.largeTitle)
.bold()
.padding()
.foregroundColor(Color.white)
.background(RoundedRectangle(cornerRadius: 30).fill(Color.red))
.padding()
.padding()
.overlay(RoundedRectangle(cornerRadius: 30).stroke(lineWidth: 10))
.shadow(color: .blue, radius: 5)
Text("With\ncompositing")
.font(.largeTitle)
.bold()
.padding()
.foregroundColor(Color.white)
.background(RoundedRectangle(cornerRadius: 30).fill(Color.red))
.padding()
.padding()
.overlay(RoundedRectangle(cornerRadius: 30).stroke(lineWidth: 10))
.compositingGroup() // <--- I added .compositingGroup() here.
.shadow(color: .blue, radius: 5)
}
}
}
This modifier makes the following modifiers be applied to the view as a whole and not to each particular subview separately
Here's an example to better illustrate this:
struct ContentView: View {
let circles: some View = ZStack {
Circle()
.frame(width: 100, height: 100)
.foregroundColor(.red)
.offset(y: -25)
Circle()
.frame(width: 100, height: 100)
.foregroundColor(.blue)
.offset(x: -25, y: 25)
Circle()
.frame(width: 100, height: 100)
.foregroundColor(.green)
.offset(x: 25, y: 25)
}
var body: some View {
VStack(spacing: 100) {
circles
circles
.opacity(0.5)
circles
.compositingGroup()
.opacity(0.5)
}
}
}
So in your case the shadow is applied to the whole view rather than separately to the Text and overlaying RoundedRectangle
Use it when wanting to apply effects like opacity or shadow to a group of views and not each contained element by itself.
It seems like that .shadow() modifier will add both inner and outer shadow. It means that if the view is not "solid", for example, it has a "hole", .shadow() will add shadow like this:
RoundedRectangle(cornerRadius: 30)
.stroke(lineWidth: 10)
.frame(width: 300)
.shadow(color: .blue, radius: 5)
Click to see the image
So, if you do not want the inner shadow, you need to make your view be "solid", like this:
RoundedRectangle(cornerRadius: 30)
.stroke(lineWidth: 10)
.frame(width: 300)
.background(RoundedRectangle(cornerRadius: 30).fill(.white))
.shadow(color: .blue, radius: 5)
Click to see the image
However, something goes wrong again, the inner shadow doesn't disappear.
That's because I forgot to apply the .compositingGroup() modifier.
As #ramzesenok mentioned, .compositingGroup() makes the following modifiers be applied to the view as a whole and not to each particular subview separately.
So, change the code a little bit:
RoundedRectangle(cornerRadius: 30)
.stroke(lineWidth: 10)
.frame(width: 300)
.background(RoundedRectangle(cornerRadius: 30).fill(.white))
.compositingGroup()
.shadow(color: .blue, radius: 5)
Click to see the image
There is only outer shadow now.

How can we Blur a Shape with Gradient in SwiftUI?

Was trying to blur my circle with a gradient can't figure out why it doesn't work. Anybody have ideas or is this not supported yet?
struct GradientExperiment: View {
var body: some View {
VStack {
//blur works
Circle()
.fill(Color(.blue))
.frame(width: 50, height: 50)
.blur(radius: 10)
//blur does not work
Circle()
.fill(LinearGradient(gradient: Gradient(colors: [.blue,.red]), startPoint: .bottom, endPoint: .top))
.frame(width: 50, height: 50)
.blur(radius: 10)
}
}
}
Add a mask to your LinearGradient and use the Circle as a mask:
LinearGradient(gradient: Gradient(colors: [.blue, .red]), startPoint: .bottom, endPoint: .top)
.mask(
Circle()
.frame(width: 50, height: 50)
.blur(radius: 10)
)