SwiftUI 中的自定义交叉影线背景形状或视图

Custom cross-hatched background shape or view in SwiftUI

我正在尝试创建阴影交叉影线。但到目前为止,我可以通过添加图像来完成。

如何创建自定义视图,其中将绘制线条而不用图像填充?

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()
    }
}

这是现在的样子。想要在另一个视图或形状之上绘制线条,而不添加不透明度和图像图案填充。

我不确定这是你想要的,但 CoreImage 可以生成剥离图案。

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)!
  }
}

然后使用 ImagePaint

 Circle().fill(
   ImagePaint(image: Image(decorative: CGImage.stripes(colors: (.blue, .yellow), width: 80, ratio: 0.25), scale: 1))
   )
   .rotationEffect(.degrees(-30))

CILineScreen过滤器可能更符合您的要求。

我不确定你想要什么,但另一种可能的解决方案是在 ZStack 线性渐变和阴影中使用不同的背景来产生凸起或凹陷的效果。为此,它有助于你的背景有一点颜色,而不是白白的。一些样本:

代码:

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)
    }
}

感谢 Cenk Bilgen 条纹图案。稍微调整一下,以便您可以旋转任何形状的舱口。

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))
    }
}

和用法

struct ContentView: View {

    var body: some View {
        VStack {
            Rectangle()
                .stripes(angle: 30)
            Circle().stripes()
            Capsule().stripes(angle: 90)
        }
    }
}

Output image link

感谢 Cenk Bilgen and flyer2001,我编辑了代码并提出了这段代码,因此它可以与视图一起使用。

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()
}

以及用法:

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)

希望对某人有所帮助。