【问题标题】:Custom cross-hatched background shape or view in SwiftUISwiftUI 中的自定义交叉阴影背景形状或视图
【发布时间】:2020-07-28 09:12:57
【问题描述】:

我正在尝试创建一个阴影交叉影线。但到目前为止,我可以通过添加图片来做到这一点。

如何创建一个自定义视图,在该视图中绘制线条而不用图像填充?

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

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

【问题讨论】:

    标签: swiftui draw shapes


    【解决方案1】:

    感谢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

    【讨论】:

      【解决方案2】:

      我不确定这是否是您想要的,但 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过滤器可能是你想要的更多。

      【讨论】:

      • 它适用于圆形,但是当您需要将阴影线旋转成其他形状时,它会变成this
      • 是的,它只生成垂直条纹。因此,如果您想将最终结果旋转到任意角度,它实际上只适用于圆形。要使其适用于任何形状,您可以在图像创建阶段应用旋转变换。
      • 谢谢。根据您的回答添加轮换,希望我理解正确))请查看我的回答
      • 让我感到困扰的一件事(看起来它在@flyer2001 的解决方案中已修复)是​​旋转传递给 ImagePaint 的图像并不像您想象的那样工作。看起来这会在引擎盖下吐出一个 SwiftUI.ModifiedContent<SwiftUI.Image, SwiftUI._RotationEffect> 视图,而不是一个新的 SwiftUI.Image。
      • 如果您需要最终结果是图像而不是视图,请调整函数以将旋转或任何变换应用于 CIImage,然后将 CGImage 边界设置为您最终图像的大小想。然后从那个 CGImage 初始化 SwiftUI Image。
      【解决方案3】:

      我不确定您想要什么,但另一种可能的解决方案是在 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)
          }
      }
      

      【讨论】:

        【解决方案4】:

        感谢Cenk Bilgenflyer2001,我编辑了代码并想出了这段代码,以便可以与视图一起使用。

        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)
        

        希望对某人有所帮助。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-07-13
          • 1970-01-01
          • 2021-04-10
          • 2012-04-16
          • 1970-01-01
          • 1970-01-01
          • 2012-10-03
          相关资源
          最近更新 更多