Reputation: 509
I am trying to create a shaded cross-hatched. But so far I can do it by adding a Image.
How can I create a custom view in which lines will be drawn and not filled with a Image?
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack {
Image("lineFilledBG").resizable().clipShape(Circle())
Circle().stroke()
Circle().foregroundColor(.yellow).opacity(0.3)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
This is how it looks now. Want lines to draw on top of another view or shape, without adding opacity and image pattern filling.
Upvotes: 4
Views: 3188
Reputation: 1445
I'm not sure this is what you want, but CoreImage can generate a striped pattern.
import CoreImage.CIFilterBuiltins
import SwiftUI
extension CGImage {
// width is for total, ratio is second stripe relative to full width
static func stripes(colors: (UIColor, UIColor), width: CGFloat, ratio: CGFloat) -> CGImage {
let filter = CIFilter.stripesGenerator()
filter.color0 = CIColor(color: colors.0)
filter.color1 = CIColor(color: colors.1)
filter.width = Float(width-width*ratio)
filter.center = CGPoint(x: width, y: 0)
let size = CGSize(width: width+width*ratio, height: 1)
let bounds = CGRect(origin: .zero, size: size)
// keep a reference to a CIContext if calling this often
return CIContext().createCGImage(filter.outputImage!.clamped(to: bounds), from: bounds)!
}
}
then use ImagePaint
Circle().fill(
ImagePaint(image: Image(decorative: CGImage.stripes(colors: (.blue, .yellow), width: 80, ratio: 0.25), scale: 1))
)
.rotationEffect(.degrees(-30))
Or CILineScreen
filter may be more what you want.
Upvotes: 2
Reputation: 181
Thank you to Cenk Bilgen and flyer2001, I edited the code and come up with this code so it can be used with a view.
import SwiftUI
import CoreImage.CIFilterBuiltins
extension CGImage {
static func generateStripePattern(
colors: (UIColor, UIColor) = (.clear, .black),
width: CGFloat = 6,
ratio: CGFloat = 1) -> CGImage? {
let context = CIContext()
let stripes = CIFilter.stripesGenerator()
stripes.color0 = CIColor(color: colors.0)
stripes.color1 = CIColor(color: colors.1)
stripes.width = Float(width)
stripes.center = CGPoint(x: 1 - (width * ratio), y: 0)
let size = CGSize(width: width, height: 1)
guard
let stripesImage = stripes.outputImage,
let image = context.createCGImage(stripesImage, from: CGRect(origin: .zero, size: size))
else { return nil }
return image
}
}
struct ViewStripes : ViewModifier {
var stripeColor : Color
var width : CGFloat
var ratio : CGFloat
var angle : Double
var frameW : CGFloat
var frameH : CGFloat
func body(content: Content) -> some View {
if let stripePattern = CGImage.generateStripePattern(colors: (.clear,UIColor(stripeColor)), width: width, ratio: ratio) {
content
.overlay(Rectangle().fill(ImagePaint(image: Image(decorative: stripePattern, scale: 1.0)))
.frame(width: hypotenuse((frameW / 2), frameH / 2) * 2, height: hypotenuse(frameW / 2, frameH / 2) * 2)
//.scaleEffect(1)
.rotationEffect(.degrees(angle))
.mask(content))
}
}
}
extension View {
func drawStripes(stripeColor: Color, width: CGFloat, ratio: CGFloat, angle : Double, frameW : CGFloat, frameH : CGFloat) -> some View {
modifier(ViewStripes(stripeColor: stripeColor, width: width, ratio: ratio, angle: angle, frameW: frameW, frameH: frameH))
}
}
func hypotenuse(_ a: Double, _ b: Double) -> Double {
return (a * a + b * b).squareRoot()
}
And for usage:
Rectangle()
.foregroundColor(Color.Red)
.frame(width:20, height: 20)
//This is the code itself
.drawStripes(stripeColor: Color.white, width: 3, ratio: 0.9, angle: 45, frameW: 20, frameH: 20)
Hope it may be helpful for someone.
Upvotes: 0
Reputation: 509
Thank to Cenk Bilgen for stripe pattern. Tweaked a bit so that you can rotate the hatch for any shape.
import SwiftUI
import CoreImage.CIFilterBuiltins
extension CGImage {
static func generateStripePattern(
colors: (UIColor, UIColor) = (.clear, .black),
width: CGFloat = 6,
ratio: CGFloat = 1) -> CGImage? {
let context = CIContext()
let stripes = CIFilter.stripesGenerator()
stripes.color0 = CIColor(color: colors.0)
stripes.color1 = CIColor(color: colors.1)
stripes.width = Float(width)
stripes.center = CGPoint(x: 1-width*ratio, y: 0)
let size = CGSize(width: width, height: 1)
guard
let stripesImage = stripes.outputImage,
let image = context.createCGImage(stripesImage, from: CGRect(origin: .zero, size: size))
else { return nil }
return image
}
}
extension Shape {
func stripes(angle: Double = 45) -> AnyView {
guard
let stripePattern = CGImage.generateStripePattern()
else { return AnyView(self)}
return AnyView(Rectangle().fill(ImagePaint(
image: Image(decorative: stripePattern, scale: 1.0)))
.scaleEffect(2)
.rotationEffect(.degrees(angle))
.clipShape(self))
}
}
And usage
struct ContentView: View {
var body: some View {
VStack {
Rectangle()
.stripes(angle: 30)
Circle().stripes()
Capsule().stripes(angle: 90)
}
}
}
Upvotes: 6
Reputation: 255
I`m not sure what do you want but another possible solution is using different backgrounds in a ZStack linear gradient and shadows to make convex or concave effect., for this it helps that your background with a little color, not white white. some samples:
The code:
struct ContentView: View {
var body: some View {
ZStack {
Color.yellow.opacity(0.1)
VStack(spacing: 50) {
myCircle()
myCircle1()
}
}
}
}
struct myCircle: View {
var body: some View {
Circle().foregroundColor(Color.clear)
.frame(width: 100, height: 100)
.background(
ZStack {
LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 0.9764705896, green: 0.850980401, blue: 0.5490196347, alpha: 1) ), Color(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1))]), startPoint: .topLeading, endPoint: .bottomTrailing)
Circle()
.stroke(Color.clear, lineWidth: 10)
.shadow(color: Color(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)), radius: 3, x: -5, y: -5)
Circle()
.stroke(Color.clear, lineWidth: 10)
.shadow(color: Color(#colorLiteral(red: 0.9764705896, green: 0.850980401, blue: 0.5490196347, alpha: 1)), radius: 3, x: 3, y: 3)
}
)
.clipShape(Circle())
.shadow(color: Color( #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) ), radius: 20, x: -20, y: -20)
.shadow(color: Color( #colorLiteral(red: 0.9764705896, green: 0.850980401, blue: 0.5490196347, alpha: 1) ), radius: 20, x: 20, y: 20)
}
}
struct myCircle1: View {
var body: some View {
Circle().foregroundColor(Color.clear)
.frame(width: 100, height: 100)
.background(
ZStack {
LinearGradient(gradient: Gradient(colors: [Color( #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) ), Color(#colorLiteral(red: 0.9764705896, green: 0.850980401, blue: 0.5490196347, alpha: 1))]), startPoint: .topLeading, endPoint: .bottomTrailing)
Circle()
.stroke(Color.clear, lineWidth: 10)
.shadow(color: Color(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)), radius: 3, x: -5, y: -5)
Circle()
.stroke(Color.clear, lineWidth: 10)
.shadow(color: Color(#colorLiteral(red: 0.9764705896, green: 0.850980401, blue: 0.5490196347, alpha: 1)), radius: 3, x: 3, y: 3)
}
)
.clipShape(Circle())
.shadow(color: Color( #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) ), radius: 20, x: -20, y: -20)
.shadow(color: Color( #colorLiteral(red: 0.9764705896, green: 0.850980401, blue: 0.5490196347, alpha: 1) ), radius: 20, x: 20, y: 20)
}
}
Upvotes: 2