在SwiftUI中创建透明模糊视图

0dxa2lsx  于 11个月前  发布在  Swift
关注(0)|答案(6)|浏览(190)

我的目标是创建一个模糊视图,就像下面图片右上角的视图一样。x1c 0d1x
我尝试了this post的前3个答案,但它们都有同样的问题-模糊视图只有一种颜色,而下面有多种颜色。这是我尝试过的解决方案之一:

import SwiftUI

struct ContentView: View {
    var body: some View {
        ZStack {
            VStack{
                ForEach(0..<20, id: \.self){ num in
                    Rectangle()
                        .frame(height: 20)
                        .padding(.vertical, 6)
                }
            }
            Blur(style:  .systemThinMaterialLight)
                .mask(
                    VStack(spacing: 0) {
                        Rectangle()
                            .frame(width: 347, height: 139)
                            .padding(.top, 0)
                        Spacer()
                    }
                )
                .allowsHitTesting(false)
        }
    }
}

struct Blur: UIViewRepresentable {
    var style: UIBlurEffect.Style = .systemMaterial
    func makeUIView(context: Context) -> UIVisualEffectView {
        return UIVisualEffectView(effect: UIBlurEffect(style: style))
    }
    func updateUIView(_ uiView: UIVisualEffectView, context: Context) {
        uiView.effect = UIBlurEffect(style: style)
    }
}

字符串



正如你所看到的,模糊视图只是一个单一的灰色视图,你甚至看不到模糊视图下面的黑色和白色条纹。
我希望模糊视图更透明,就像你在第一张图片中看到的那样,海洋,沙子和阴影仍然可以通过模糊视图看到。我如何在SwiftUI中创建这样的视图?

zbq4xfa0

zbq4xfa01#

这段代码非常接近你的问题,它只能从iOS 15开始工作:

ZStack{
  Image("background")
  //then comes your look trough button, here just a Rectangle:
  Rectangle()
 .foregroundColor(.secondary)
 .background(.ultraThinMaterial)
 .frame(width: 100, height: 100)
 //then you can add opacity to see a bit more of the background:
 .opacity(0.95)
}

字符串

cu6pst1q

cu6pst1q2#

与其使用两张图片,我更倾向于使用一个绑定,为此,我在资源中添加了一个名为“里昂”的图片。
以下是我的解决方案,减去一些数学:

ContentView

struct ContentView: View {
    @State private var image: UIImage = UIImage(named: "lyon")!
    var body: some View {
        ZStack{
            Image(uiImage: image)
                .resizable()
                .aspectRatio(contentMode: .fill)
            IceCube(image: image)
        }
        .ignoresSafeArea(.all)
    }
}

字符串

IceCube()

此视图执行提升操作:

struct IceCube: View {

    @State private var rectPosition = CGPoint(x: 150, y: 150)
    
    @State private var cutout: UIImage?

    let image: UIImage

    let frameSide: CGFloat = 180

    var body: some View {
        
        Image(uiImage: cutout ?? image)
            .frame(width: frameSide, height: frameSide)
            .blur(radius: 5)
            .clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
            .overlay(RoundedRectangle(cornerRadius: 12, style: .continuous).stroke(Color.red, lineWidth: 3))
            .onAppear(perform: {
                processImage()
            })
            .position(rectPosition)
            .gesture(DragGesture().onChanged({ value in
                self.rectPosition = value.location
                processImage()
            }))
        
    }
    
    func processImage() {
        
        //TODO: - Find the image scale size from ContentView and also figure out how much it begins to the left/top of the screen.
        
        cutout = croppedImage(from: image, croppedTo: CGRect(x: rectPosition.x, y: rectPosition.y, width: frameSide, height: frameSide))
    }
}

//MARK: - image processing functions.

func croppedImage(from image: UIImage, croppedTo rect: CGRect) -> UIImage {
    
    UIGraphicsBeginImageContext(rect.size)
    let context = UIGraphicsGetCurrentContext()
    
    let drawRect = CGRect(x: -rect.origin.x, y: -rect.origin.y, width: image.size.width, height: image.size.height)
    
    context?.clip(to: CGRect(x: 0, y: 0, width: rect.size.width, height: rect.size.height))
    
    image.draw(in: drawRect)
    
    let subImage = UIGraphicsGetImageFromCurrentImageContext()
    
    UIGraphicsEndImageContext()
    return subImage!
}


显然,要避免在真实的项目中强制展开。
如果你需要一些数学上的想法,看看我在GitHub上对图像进行裁剪和调整大小的repo:
https://github.com/Rillieux/PhotoSelectAndCrop/blob/main/Sources/ImageMoveAndScaleSheet%2BExtensions.swift
基本的想法是,当你想从原始图像中裁剪正方形时,原始图像的大小和尺寸可能与你在屏幕上看到的不完全相同。例如,如果你使用.aspectRatio(contentMode: .fit),图像可能会缩小,所以你需要考虑到这一点。我还没有这样做,这就是为什么模糊不匹配的原因。但是,我想你会明白的。


的数据
此外,我这里的例子可以通过使用视图构建器来大大改进,具体来说,就像这样:

struct SafeEdgesBlurContainer<V: View>: View {

    var containedView: V 

    //Snipped code

    var body: some View {
        ZStack {
            Image(uiImage: cutout ?? image)
            containedView
    }

 ...


然后像这样使用它:
IceCube(image: UIImage(named: "lyon")!, containedView: Text("4,7")

fhg3lkii

fhg3lkii3#

此方法将高斯模糊应用于视图。
第一个月
主要参数:

  • 半径:模糊的半径大小。当模糊的半径较大时,模糊会更加漫反射。
  • 不透明:一个布尔值,指示模糊渲染器是否允许模糊输出中的透明度。设置为true可创建不透明模糊,或设置为false可允许透明度。
Image("your_Image")
   .resizable()
   .frame(width: 300, height: 300)
   .blur(radius: 20)

字符串

hfsqlsce

hfsqlsce4#

我为tvOS写了一个可配置的“视图扩展”(但可能也适用于iOS),保存下面两个文件:

import Foundation
import SwiftUI

extension View
{
    /**
     function that creates the background color that "shines" through the 'IceCube'
     */
    func createColorBGLayer(at: CGRect,
                            background: Color,
                            cornerRadius: CGFloat) -> some View
    {
        return background
            .clipShape(
                RoundedRectangle(cornerRadius: cornerRadius)
                    .offset(x: at.minX + 5, y: at.minY + 5)
                    .size(CGSize(width: at.width - 10, height: at.height - 10))
            )
    }
    
    /**
     function that creates the 'IceCube' (a clipped image with blur)
     */
    func createIceLayer(at: CGRect,
                        backgroundOpacity: CGFloat,
                        cornerRadius: CGFloat,
                        blurRadius: CGFloat) -> some View
    {
        return self
            .opacity(backgroundOpacity)
            .clipShape(
                RoundedRectangle(cornerRadius: cornerRadius)
                    .offset(x: at.minX, y: at.minY)
                    .size(CGSize(width: at.width, height: at.height))
            )
            .blur(radius: blurRadius)
    }
    
    /**
     function that creates the text layer in the center of the 'IceCube'
     */
    func createTextLayer(at: CGRect,
                         textString: String,
                         fontSize: CGFloat,
                         fontDesign: Font.Design,
                         fontWeight: Font.Weight,
                         foregroundColor: Color) -> some View
    {
        // calculate render width and height of text using provided font (without actually rendering)
        let sizeOfText: CGSize = textString.sizeUsingFont(fontSize: fontSize, weight: fontWeight)
        let textOffsetX = at.minX + ((at.width - sizeOfText.width) / 2)
        let textOffsetY = at.minY + ((at.height - sizeOfText.height) / 2)
        
        // render text in center of iceCube
        return GeometryReader { proxy in
            Text(textString)
                .font(Font.system(size: fontSize, design: fontDesign))
                .fontWeight(fontWeight)
                .foregroundColor(foregroundColor)
                // put the text in the middle of the blured rectangle
                .offset(x: textOffsetX, y: textOffsetY)
        }
    }
    
    /**
     main function to create the ice cube ontop of this extended view
     */
    func iceCube(at: CGRect,
                 textString: String = "",
                 fontSize: CGFloat = 40,
                 fontWeight: Font.Weight = Font.Weight.regular,
                 fontDesign: Font.Design = Font.Design.rounded,
                 foregroundColor: Color = Color.white,
                 background: Color = Color.white,
                 backgroundOpacity: CGFloat = 0.9,
                 cornerRadius: CGFloat = 30,
                 blurRadius: CGFloat = 8) -> some View
    {
        
        // clipped image at the original position blurred and rounded corner
        return self
            .overlay(
                ZStack {
                // first layer color white for a beat of glare
                createColorBGLayer(at: at, background: background, cornerRadius: cornerRadius)
                
                // second layer a blur round corner clip from the image
                createIceLayer(at: at, backgroundOpacity: backgroundOpacity, cornerRadius: cornerRadius, blurRadius: blurRadius)
                
                // text on top of the blurred part (use geometry to reset text position)
                createTextLayer(at: at, textString: textString, fontSize: fontSize, fontDesign: fontDesign, fontWeight: fontWeight, foregroundColor: foregroundColor)
            })
    }
}

字符串
字符串扩展来计算文本呈现宽度和高度(不呈现),因此可以在extension中使用

import Foundation
import UIKit
import SwiftUI

extension String
{
    func sizeUsingFont(fontSize: CGFloat, weight: Font.Weight) -> CGSize
    {
        var uiFontWeight = UIFont.Weight.regular
        
        switch weight {
        case Font.Weight.heavy:
            uiFontWeight = UIFont.Weight.heavy
        case Font.Weight.bold:
            uiFontWeight = UIFont.Weight.bold
        case Font.Weight.light:
            uiFontWeight = UIFont.Weight.light
        case Font.Weight.medium:
            uiFontWeight = UIFont.Weight.medium
        case Font.Weight.semibold:
            uiFontWeight = UIFont.Weight.semibold
        case Font.Weight.thin:
            uiFontWeight = UIFont.Weight.thin
        case Font.Weight.ultraLight:
            uiFontWeight = UIFont.Weight.ultraLight
        case Font.Weight.black:
            uiFontWeight = UIFont.Weight.black
        default:
            uiFontWeight = UIFont.Weight.regular
        }
        
        let font = UIFont.systemFont(ofSize: fontSize, weight: uiFontWeight)
        let fontAttributes = [NSAttributedString.Key.font: font]
        return self.size(withAttributes: fontAttributes)
    }
}


这样使用它:

import Foundation
import SwiftUI

struct TestView: View
{
    let iceCubePos1: CGRect = CGRect(x: 1100, y: 330, width: 500, height: 200)
    let iceCubePos2: CGRect = CGRect(x: 400, y: 130, width: 300, height: 200)
    let iceCubePos3: CGRect = CGRect(x: 760, y: 50, width: 200, height: 150)

    var body: some View
    {
        Image("SomeImageFromAssets")
            .resizable()
            .iceCube(at: iceCubePos1, textString: "Hello again")
            .iceCube(at: iceCubePos2, textString: "One", fontSize: 60.0, fontWeight: Font.Weight.heavy, fontDesign: Font.Design.rounded, foregroundColor: Color.black, background: Color.black, backgroundOpacity: 0.8, cornerRadius: 0, blurRadius: 9)
            .iceCube(at: iceCubePos3, textString: "U2")
            .ignoresSafeArea(.all)
            .scaledToFit()
    }
}


它应该是这样的:


的数据

g6baxovj

g6baxovj5#

iOS 15+ / macOS 12+
只需在SwiftUI中使用新的foregroundStyle修饰符即可。
范例:

RoundedRectangle(cornerRadius: 5)
   .frame(width: 30, height: 30)
   .foregroundStyle(.ultraThinMaterial)

字符串

juud5qan

juud5qan6#

只需添加两张图片,一张名为“海滩”,第二张名为“海滩1”,然后尝试此.

ZStack {
                
                Image("beach").resizable().frame(width: 400, height: 800, alignment: .center)
                
                VStack{
                    
                    HStack{
                        Spacer()
                        Text(" 4,7 ")
                            .font(Font.system(size:25).bold())
                            .foregroundColor(Color.black)
                            .background(
                       
                                Image("beach1").resizable().frame(width: 80, height: 80, alignment: .center)
                                    .blur(radius: 5)
                                )
                            .frame(width: 80, height: 80, alignment: .center)
                            .overlay(
                                RoundedRectangle(cornerRadius: 15).stroke(Color.black, lineWidth: 3)
                                    )
                            .padding(.top,55.0)
                            .padding(.trailing,15.0)
                    }
                    
                    Spacer()
                }

            }

字符串

相关问题