Bad quality of circle stroke with gradient - swiftui

Circle()
.trim(from: viewModel.lenghts[index].from, to: viewModel.lenghts[index].to)
.stroke(someGradient, style: StrokeStyle(lineWidth: 12.0, lineCap: .round, lineJoin: .round))
.rotationEffect(Angle(degrees: 270.0))
Tried set a gradient in .stroke initializer, tried to use .fill(Gradient..)
And my circle stroke looks pixelated. When use just a color (foregraundColor(Color..) all is ok. My gradient: AngularGradient(gradient: Gradient(colors: [CustomColors.red, CustomColors.pink]), center: .center, angle: .degrees(270))
What am I doing wrong? Tried linear, angular gradients

It seems like you've zoomed in. Try a bigger shape for better quality.
Compare these two visually identical rings and see the difference of zooming in and out:
High Quality
This one zoomed out to match the size that you are seeing.
Low Quality
This one zoomed in to match the size that you are seeing.
You can see the difference even in the text.
Full Demo code:
var body: some View {
ZStack {
Text("Low quality")
.font(.system(size: 8))
Circle()
.trim(from: 0.1, to: 0.9)
.stroke(g, style: StrokeStyle(lineWidth: 10, lineCap: .round, lineJoin: .round))
.frame(width: 60, height: 60)
.padding(8)
}
ZStack {
Text("Hight quality")
.font(.system(size: 50))
Circle()
.trim(from: 0.1, to: 0.9)
.stroke(g, style: StrokeStyle(lineWidth: 60, lineCap: .round, lineJoin: .round))
.frame(width: 400, height: 400)
.padding(50)
}
}
Note that you can use .scaleEffect modifier to scale it with code (if you really need to)

Related

Xcode Playgrounds liveview not working, code from swift playgrounds

I wanted to build a Playground which stores my notes for my next examination using either Xcode playgrounds or just Swift Playgrounds. However, I attempted to build a list with an arrow at the side for navigation in Playgrounds, but with the lack of information after searching lots of tutorials, there's no way I could create a navigation list on
Swift Playgrounds. So I switched to Xcode playgrounds. But I could not seem to load the LiveView.
Note: no errors shown. Xcode playgrounds displays "Successful" but nothing is shown.
import SwiftUI
import PlaygroundSupport
//--------------------------------------------------------------//
// Weighted Asesstment III Notes
// Subject: Science
// Written in: English & SwiftUI
struct IntroView: View {
// arrays cus I wanna put this to a list
let expTech = ["Chromatography","Evaporation", "Distillation", "Dissolving","Magnetic Attraction", "Filtration"]
let expUse = ["Chromatography",
"Method: Paper",
"Used for: ",
"Evaporation",
"Method: Heating",
"Used for: Separating salt & water",
"Distillation",
"Method: Heating of liquid mixture with different boiling points",
"eg. Used for: Obtaining pure water from alcohol",
"Dissolving",
"Method",
"Used for",
"Magnetic Attraction",
//I need complete silence lol
//Iron nickel cobalt steel
"Method: Magnets (I,N,C,S)",
"Used for: Separating magnetic objects from non magnets",
"Filtration",
"Method: Filtering/Use of filter paper",
"Used for: Separating mixture of insoluble solid from liquid"]
var body: some View {
// ZStack
ZStack {
// background color
Color.white
// VStack
VStack {
LinearGradient(gradient: Gradient(colors: [.white, .gray, .black]), startPoint: .leading, endPoint: .trailing)
.mask(Text("WA 3 Sci Notes: M5C7")
.font(.system(size: 30, weight: .bold, design: .monospaced)))
.foregroundColor(Color.black)
.offset(x: 10, y: -325)
.padding()
Text("Types of experimental techniques")
.foregroundColor(Color.black)
.offset(x: -100, y: -710)
.font(.system(size: 25, weight: .semibold))
}
ZStack {
Rectangle()
.frame(width: 340, height: 240)
.cornerRadius(20)
.scaledToFill()
.shadow(color: Color.black, radius: 10)
.offset(x: -100, y: -120)
// list
List(expTech, id: \.self) { expTech in Text(expTech) }
.frame(width: 350, height: 250, alignment: .center)
.cornerRadius(25)
.offset(x: -100, y: -120)
}
Text("Uses of Experimental Techniques")
.foregroundColor(Color.blue)
.offset(x: 80, y: 40)
.font(.system(size: 25, weight: .semibold))
ZStack {
VStack {
Rectangle()
.frame(width: 600, height: 250)
.cornerRadius(20)
.scaledToFill()
.shadow(color: Color.black, radius: 5)
.offset(x: 5, y: 530)
}
}
}
}
}
PlaygroundPage.current.setLiveView(IntroView())
The issue could be that your View doesn't render properly. I put your code into an Xcode project (not a storyboard) and it looks like this:
You can even see it rendering weirdly in the playground:

How to add icon to circle progress bar tip?

I'm trying to add an icon to the tip of the progress bar in the circle. This is the code I have so far:
Circle()
.trim(from: 0, to: 0.6)
.stroke(style: StrokeStyle(lineWidth: 24, lineCap: .round, lineJoin: .round))
.rotationEffect(.degrees(-90))
.foregroundColor(.green)
.frame(width: 300, height: 300)
.padding()
This ends up looking like this:
But I'm trying to add an icon to the tip of the progress like this:
How can I achieve this in SwiftUI?
Use the left arrow icon and put it into the ZStack and set y offset in minus.
struct DemoView: View {
#State private var angle: Double = -90
var body: some View {
ZStack {
Circle()
.trim(from: 0, to: 0.6)
.stroke(style: StrokeStyle(lineWidth: 24, lineCap: .round, lineJoin: .round))
.rotationEffect(.degrees(angle))
.foregroundColor(.green)
Image("left-arrow")
.offset(y: -150)
.rotationEffect(.degrees(305 + angle))
}.frame(width: 300, height: 300)
.padding()
}
}
I have added an image arrow using progress code from here
struct ProgressBar: View {
#Binding var progress: Float
var body: some View {
GeometryReader { geo in
ZStack(alignment: .center) {
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()
Image("left-arrow")
.offset(y: -(geo.size.height/2))
.rotationEffect(.degrees(Double(self.progress) * 360))
.animation(.linear)
}
}
}
}

Circle view animation misbehavior

I've created a Loading view on swiftUI, using this code:
var body: some View {
ZStack {
Circle()
.trim(from: 0, to: 0.2)
.stroke(AngularGradient(gradient: .init(colors: [settings.colors[self.settings.accentColor]]), center: .center), style: StrokeStyle(lineWidth: 6, lineCap: .round))
.rotationEffect(Angle(degrees: isLoading ? 360 : 0))
Circle()
.trim(from: 0.2, to: 0.4)
.stroke(AngularGradient(gradient: .init(colors: [.red]), center: .center), style: StrokeStyle(lineWidth: 6, lineCap: .round))
.rotationEffect(Angle(degrees: isLoading ? -360 : 0))
.frame(height: aspect / 2)
Circle()
.trim(from: 0.5, to: 0.7)
.stroke(AngularGradient(gradient: .init(colors: [settings.colors[self.settings.accentColor]]), center: .center), style: StrokeStyle(lineWidth: 6, lineCap: .round))
.rotationEffect(Angle(degrees: isLoading ? 360 : 0))
Circle()
.trim(from: 0.7, to: 0.9)
.stroke(AngularGradient(gradient: .init(colors: [.red]), center: .center), style: StrokeStyle(lineWidth: 6, lineCap: .round))
.rotationEffect(Angle(degrees: isLoading ? -360 : 0))
.frame(height: aspect / 2)
.onAppear() {
self.isLoading = true
}
}
.animation(Animation.linear(duration: 0.5).repeatForever(autoreverses: false))
.frame(width: aspect, height: aspect, alignment: .center)
}
On the swiftui preview canvas everything seems fine:
But when run on simulator or real device, animation seems broken and it modifies view location while animating like this:
Anyone knows if this is a bug, or I'm missing something. Thanks in advance.

SwiftUI: vertical align dynamic sized Rectangle with Spacer in SwiftUI

It seems there is a potential bug in SwiftUI. I am trying to put a rectangle with opacity 0.5 on top of an image.
When I try to fix the transparent rectangle on top, from 100px width, it goes down instead of sticking to the top.
Here is the code:
ZStack {
VStack {
Image("movistar")
.resizable(capInsets: EdgeInsets(), resizingMode: .stretch)
.scaledToFit()
.cornerRadius(8)
.padding(15)
.frame(minWidth: Global.SCREEN_WIDTH)
}
VStack {
HStack {
Rectangle()
.fill(Color(red: 0, green: 0, blue: 0, opacity: 0.5))
.frame(width: 110, height: Global.SCREEN_WIDTH / 4)
}
Spacer()
}
.scaledToFit()
.cornerRadius(8)
.padding(15)
.frame(width: Global.SCREEN_WIDTH, height: Global.SCREEN_WIDTH)
There is no bug here. If you add a .background to all of your layers, you will see that because of the way you set up the view (ie. Spacer, scaledToFit, etc.) the actual frames of the views are not necessarily the edges of the image. You also have not set the alignment of any of the Stacks or Frames.
There are many ways to do what you are trying to do, but I believe this is the simplest:
var body: some View {
Image("movistar")
.resizable(capInsets: EdgeInsets(), resizingMode: .stretch)
.scaledToFit()
.cornerRadius(8)
.frame(minWidth: UIScreen.main.bounds.width)
.overlay(
Rectangle()
.fill(Color(red: 0, green: 0, blue: 0, opacity: 0.5))
.frame(width: 110, height: UIScreen.main.bounds.width / 4)
, alignment: .top
)
}
Finally got into a solution: .scaleToFit() was messing with the VStack(). After deleting, it worked perfectly. I also got rid of the HStack().

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.