【问题标题】:Mask rectangle with a text in SwiftUI在 SwiftUI 中用文本遮罩矩形
【发布时间】:2020-01-30 10:15:53
【问题描述】:

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

这是我尝试过的:

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

反之亦然:

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

这两个样本都给了我想要的相反结果。这意味着只有文本以白色显示,矩形被“屏蔽”。

我还想到了一种替代方法,我只需将TextRectangle 组合在ZStack 中。具有前景色的矩形和具有背景色的文本。这将产生相同的效果。但是我不想这样做,因为这对我来说似乎是一种黑客行为。例如,如果我想在背景中添加渐变或图像,则此方法效果不佳。

在 SwiftUI 中有没有好的方法可以做到这一点?如果是通过UIViewRepresentable,我不介意。

【问题讨论】:

标签: ios swift mask swiftui


【解决方案1】:

实际上,即使它在您看来像是 hack,但 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)
    }
}

如果你愿意,你可以根据里面的文字调整矩形的大小

【讨论】:

  • 上图其实只是一小块区域的截图。绿色背景也可以是渐变或图像。我只想剪出一段矩形的文字。
  • 你到底是什么意思?如果您希望背景为全屏,则可以避免设置背景的大小。然后,如果您希望它是图像或其他东西,您可以使用变量来选择背景颜色、图像或渐变,或者更好的是,根据背景类型选择不同的 init
  • 我认为 OP 想要的是一个带有“孔”的矩形,即他的文本的形状。矩形不需要知道通过这些孔会显示什么;他想要一个不是字母形状的形状,而是一个矩形,带有字母形状的孔。我也在寻找同样的东西。
  • 有人找到解决方案了吗?我和 OP 在同一条船上,我想从视图中删除文本(背景中有渐变......)
【解决方案2】:

请先参考this anwser,然后你就会明白我做的如下代码:

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

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

我对@9​​87654327@ 的自定义扩展:

import SwiftUI

extension Gradient {
    
    // general linear gradient ---------------------------
    
    public static func linear(
        from start: UnitPoint, 
        to     end: UnitPoint, 
        colors    : [Color]       // use array
    ) -> LinearGradient 
    {
        LinearGradient(
            gradient  : Gradient(colors: colors), 
            startPoint: start, 
            endPoint  : end
        )
    }
    
    public static func linear(
        from start: UnitPoint, 
        to     end: UnitPoint, 
        colors    : Color...     // use variadic parameter
    ) -> LinearGradient 
    {
        linear(from: start, to: end, colors: colors)
    }
    
    // specialized linear gradients ------------------------
    
    // top to bottom
    public static func vertical(_ colors: Color...) -> LinearGradient {
        linear(from: .top, to: .bottom, colors: colors)
    }
    
    // leading to trailing
    public static func horizontal(_ colors: Color...) -> LinearGradient {
        linear(from: .leading, to: .trailing, colors: colors)
    }
    
    // top leading to bottom trailing
    public static func diagonal(_ colors: Color...) -> LinearGradient {
        linear(from: .topLeading, to: .bottomTrailing, colors: colors)
    }
    
    // top leading to bottom trailing
    public static func diagonal2(_ colors: Color...) -> LinearGradient {
        linear(from: .bottomLeading, to: .topTrailing, colors: colors)
    }
}

【讨论】:

  • 您的代码不起作用,因为“'Gradient' 没有成员 'diagonal'”
  • @Chris 如果你好奇的话,我已经在上面添加了Gradient 的自定义扩展,你可以看看。
【解决方案3】:

我也有类似的要求。实际上,我的要求是文本是多行文本,可以向上滚动以一次显示一行(与某人叙述文本时间同步。背景是图像。

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

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

【讨论】:

    猜你喜欢
    • 2012-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-26
    • 2014-11-17
    • 2021-03-28
    • 2020-08-07
    • 2012-12-30
    相关资源
    最近更新 更多