在SwiftUI中使用文本遮罩矩形

时间:2019-10-01 06:48:12

标签: ios swift mask swiftui

我想使用SwiftUI实现以下目标:

masked rectangle

这是我尝试过的:

Rectangle().frame(width: 200, height: 100).foregroundColor(.white).mask(Text("test"))

反之亦然:

Text

这两个样本都给了我所需结果的反结果。这意味着只有文本显示为白色,矩形被“遮盖了”。

我还想到了将RectangleZStack合并到UIViewRepresentable中的替代方法。具有前景色的矩形,文本为背景的矩形。这将产生相同的效果。但是我不想这样做,因为这对我来说似乎是一个hack。例如,如果我想在背景上添加渐变色或图像,则此方法效果不佳。

在SwiftUI中是否有很好的方法?我不介意是否通过superman = { name: 'Superman', 'real name': 'Clark Kent', height: 75, weight: 235, hero: true, villain: false, allies: {superHero: 'Batman', superPower: 'He is rich', sidekick: 'robin'} }

3 个答案:

答案 0 :(得分:1)

实际上,即使您看起来像是黑客一样,这也是SwiftUI的工作方式。

您可以通过创建自定义视图来避免这种“黑客攻击”

一个例子可能是:

public struct BackgroundedText: View {

    var first_color = Color.green
    var second_color = Color.white
    var text_color = Color.green

    var size = CGSize(width: 200, height: 100)
    var xOffset: CGFloat = 50
    var yOffset: CGFloat = 50

    var text = "Hello world!"

    init(_ txt: String, _ txt_color: Color, _ fColor: Color, _ sColor: Color, _ size: CGSize, _ xOff: CGFloat, _ yOff: CGFloat) {
        self.text = txt
        self.text_color = txt_color
        self.first_color = fColor
        self.second_color = sColor
        self.size = size
        self.xOffset = xOff
        self.yOffset = yOff
    }


    public var body: some View {
        ZStack{
            Rectangle()
                .frame(width: self.size.width,
                       height: self.size.height)
                .foregroundColor(self.first_color)

            Rectangle()
            .frame(width: self.size.width - xOffset,
                   height: self.size.height - yOffset)
            .foregroundColor(self.second_color)

            Text(self.text)
                .foregroundColor(self.text_color)

        }
    }
}

因此您可以通过以下方式使用视图:

struct ContentView: View {
    var body: some View {
        BackgroundedText("Hello", .green, .green, .white, CGSize(width: 200, height: 100), 50, 50)
    }
}

如果需要,可以根据内部文本调整矩形大小

答案 1 :(得分:1)

请先参考this anwser,然后您将了解我编写的以下代码:

generator

结果是:

enter image description here

以上代码中使用的密钥扩展名是gen_fn()

import SwiftUI
import PlaygroundSupport

struct ContentView: View {
    var body: some View {
        
        // text used in mask
        let text = Text("Text")
            .font(.system(size: 80, weight: .black, design: .rounded))
            .scaledToFit()                   // center text in view
        
        // container
        return ZStack {
            // background color
            Color.white.grayscale(0.3)
            // text card
            Gradient.diagonal(.yellow, .green)      // my custom extension 
                .inverseMask(text)                    // ⭐️ inverse mask
                // shadow for text
                .shadow(color: Color.black.opacity(0.7), radius: 3, x: 3, y: 3)
                .frame(width: 300, height: 200)
                // highlight & shadow
                .shadow(color: Color.white.opacity(0.9), radius: 18, x: -18, y: -18)
                .shadow(color: Color.black.opacity(0.3), radius: 14, x:  14, y:  14)
        }
    }
}

PlaygroundPage.current.setLiveView(ContentView())

---- [编辑] -----

我对.inverseMask()的自定义扩展名:

import SwiftUI

extension View {
    // view.inverseMask(_:)
    public func inverseMask<M: View>(_ mask: M) -> some View {
        // exchange foreground and background
        let inversed = mask
            .foregroundColor(.black)  // hide foreground
            .background(Color.white)  // let the background stand out
            .compositingGroup()       // ⭐️ composite all layers
            .luminanceToAlpha()       // ⭐️ turn luminance into alpha (opacity)
        return self.mask(inversed)
    }
}

答案 2 :(得分:0)

我有类似的要求。实际上,我的要求是该文本是多行文本,每次向上滚动以显示一行(与某人对文本进行计时。背景是图像。

我通过以下方式解决了它。 ZStack首先带有图像,然后是具有多行文本的Text层,其定位方式是我想要的方式,然后是具有矩形孔的图像的另一层,该位置是我希望文本出现的位置。该方法可能会满足您的需求-您将需要定位孔,更改颜色等以满足您的需求。该代码在图像的大约四分之三处显示了一个矩形孔。

struct TestView : View {    
var body: some View {
    GeometryReader { proxy in
        ZStack {
            Image("MyImage")
                .resizable()
                .scaledToFit()
            Text("Multiline\ntext\nfor\nscrolling")
                .font(.title).foregroundColor(.white)
                .position(x: proxy.size.width * 0.5, y: proxy.size.height * 0.75 )
            Image("MyImage")
            .resizable()
            .scaledToFit()
                .mask(self.makeMask(for: proxy.size))
        }
    }
}
func makeMask(for sz : CGSize) -> some View {
    return VStack(spacing: 0) {
        Rectangle().fill(Color.black)
            .frame(height: sz.height * 0.75 + 4)
        Rectangle().fill(Color.clear)
            .frame(height: 40)
        Rectangle().fill(Color.black)
            .frame(height: sz.height * 0.25 - 40)
    }
}

}