渐变作为SwiftUI中Text的前景色

时间:2019-11-22 09:35:29

标签: swift user-interface swiftui

在SwiftUI中是否可以使用渐变作为Text的foregroundColor?

感谢您的提前解答!

6 个答案:

答案 0 :(得分:17)

我想这应该有所帮助。适用于文本,图像和任何其他视图。

import SwiftUI

// MARK: - API
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension View {
    public func foreground<Overlay: View>(_ overlay: Overlay) -> some View {
        _CustomForeground(overlay: overlay, for: self)
    }
}

// MARK: - Implementation
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
private struct _CustomForeground<Content: View, Overlay: View>: View {
    let content: Content
    let overlay: Overlay
    
    internal init(overlay: Overlay, for content: Content) {
        self.content = content
        self.overlay = overlay
    }
    
    var body: some View {
        content.overlay(overlay).mask(content)
    }
}

我个人最喜欢这种方法。但您也可以将其组合为:

import SwiftUI

// MARK: - API
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension View {
    public func foreground<Overlay: View>(_ overlay: Overlay) -> some View {
        self.overlay(overlay).mask(self)
    }
}

用法示例?

// MARK: - Example
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
struct GradientTextDemo: View {
    var body: some View {
        Text("Gradient foreground")
            .foreground(makeGradient())
            .padding(.horizontal, 32)
            .padding(.vertical)
            .background(Color.black)
            .cornerRadius(12)
    }
    
    func makeGradient() -> some View {
        LinearGradient(
            gradient: .init(colors: [.red, .orange]),
            startPoint: .topLeading,
            endPoint: .bottomTrailing
        )
    }
}

enter image description here

See gist

答案 1 :(得分:4)

您可以将任意渐变或其他类型的视图分配为自定义尺寸的蒙版,例如:

Text("Gradient is on FIRE !!!")
    .selfSizeMask(
        LinearGradient(
            gradient: Gradient(colors: [.red, .yellow]),
            startPoint: .bottom,
            endPoint: .top)
    )

具有以下简单的小扩展名:

extension View {
    func selfSizeMask<T: View>(_ mask: T) -> some View {
        ZStack {
            self.opacity(0)
            mask.mask(self)
        }.fixedSize()
    }
}

Demo


?奖金1

您可以将其应用于任何类型的view

Bonus 1


?奖金2

此外,您可以在其上应用所有渐变或任何视图:

Bonus 2

答案 2 :(得分:2)

无需使用import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D theta = np.linspace(0, 2.*np.pi, 200) phi = np.linspace(0, 1.*np.pi, 200) theta, phi = np.meshgrid(theta, phi) # major and minor radius R0, a = 3., 1. lw = 0.05 # Width of line # Cue the unpleasantness - the circle must also be drawn as a toroid x_circle = (R0 + lw*np.cos(theta)) * np.cos(phi) y_circle = (R0 + lw*np.cos(theta)) * np.sin(phi) z_circle = lw * np.sin(theta) c_circle = np.full_like(x_circle, (1.0, 1.0, 1.0, 1.0), dtype=(float,4)) # Delay meshgrid until after circle construction x_torus = (R0 + a*np.cos(theta)) * np.cos(phi) y_torus = (R0 + a*np.cos(theta)) * np.sin(phi) z_torus = a * np.sin(theta) c_torus = np.full_like(x_torus, (0.0, 0.5, 1.0, 1.0), dtype=(float, 4)) # Create the bridge, filled with transparency x_bridge = np.vstack([x_circle[-1,:],x_torus[0,:]]) y_bridge = np.vstack([y_circle[-1,:],y_torus[0,:]]) z_bridge = np.vstack([z_circle[-1,:],z_torus[0,:]]) c_bridge = np.full_like(z_bridge, (0.0, 0.0, 0.0, 0.0), dtype=(float, 4)) # Join the circle and torus with the transparent bridge X = np.vstack([x_circle, x_bridge, x_torus]) Y = np.vstack([y_circle, y_bridge, y_torus]) Z = np.vstack([z_circle, z_bridge, z_torus]) C = np.vstack([c_circle, c_bridge, c_torus]) fig = plt.figure() ax = fig.gca(projection='3d') ax.plot_surface(X, Y, Z, rstride=1, cstride=1, facecolors=C, linewidth=0) ax.view_init(elev=15, azim=270) ax.set_xlim( -3, 3) ax.set_ylim( -3, 3) ax.set_zlim( -3, 3) ax.set_axis_off() plt.show() ,就可以轻松地以纯SwiftUI完成此操作。您需要用文本遮盖渐变:

UIViewRepresentable

enter image description here

答案 3 :(得分:1)

csv中,您可以像下面一样使用feature的概念来实现

GradientView

SwiftUI

GradientLabelWrapper:

struct GradientView: View {
    var body: some View {
        VStack {
            GradientLabelWrapper(width: 150) //  you can give as you want
                .frame(width: 200, height: 200, alignment: .center) // set frame as you want
        }
    }
}

UILabel:扩展名

struct GradientLabelWrapper: UIViewRepresentable {

    var width: CGFloat
    var text: String?
    typealias UIViewType = UIView

    func makeUIView(context: UIViewRepresentableContext<GradientLabelWrapper>) -> UIView {

        let label = UILabel()
        label.lineBreakMode = .byWordWrapping
        label.numberOfLines = 0
        label.preferredMaxLayoutWidth = width
        label.text = text ?? ""
        label.font = UIFont.systemFont(ofSize: 25) //set as you need
        label.applyGradientWith(startColor: .red, endColor: .blue)
        return label
    }

    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<GradientLabelWrapper>) {
    }
} 

答案 4 :(得分:0)

您可以使用它来使渐变成为文本的前景色:

Text("Hello World")
                .padding()
                .foregroundColor(.white)
                .background(LinearGradient(gradient: Gradient(colors: [.white, .black]), startPoint: .top, endPoint: .bottom))

希望这会有所帮助:)您也可以使用以下链接作为参考:https://www.hackingwithswift.com/quick-start/swiftui/how-to-render-a-gradient

答案 5 :(得分:0)

我会说在纯SwiftUI中是不可能的。您需要使用经过修改的UILabel并通过UIViewRepresentable进行包装,才能在SwiftUI中使用它。

我从先前的答案中抓取了代码,为水平渐变创建了一个基本示例:

class GradientLabel: UILabel {
    var gradientColors: [CGColor] = []

    override func drawText(in rect: CGRect) {
        if let gradientColor = drawGradientColor(in: rect, colors: gradientColors) {
            self.textColor = gradientColor
        }
        super.drawText(in: rect)
    }

    private func drawGradientColor(in rect: CGRect, colors: [CGColor]) -> UIColor? {
        let currentContext = UIGraphicsGetCurrentContext()
        currentContext?.saveGState()
        defer { currentContext?.restoreGState() }

        let size = rect.size
        UIGraphicsBeginImageContextWithOptions(size, false, 0)
        guard let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(),
                                        colors: colors as CFArray,
                                        locations: nil) else { return nil }

        let context = UIGraphicsGetCurrentContext()
        context?.drawLinearGradient(gradient,
                                    start: CGPoint.zero,
                                    end: CGPoint(x: size.width, y: 0),
                                    options: [])
        let gradientImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        guard let image = gradientImage else { return nil }
        return UIColor(patternImage: image)
    }
}

struct MyTextView: UIViewRepresentable {

    var width: CGFloat

    func makeUIView(context: Context) -> GradientLabel {
        let label = GradientLabel()
        label.gradientColors = [UIColor.blue.cgColor, UIColor.red.cgColor]
        label.lineBreakMode = .byWordWrapping
        label.numberOfLines = 0
        label.preferredMaxLayoutWidth = width
        label.text = "Here's a lot of text for you to display. It won't fit on the screen."
        return label
    }

    func updateUIView(_ view: GradientLabel, context: Context) {
    }
}

struct ContentView: View {

    var body: some View {
        GeometryReader { geometry in
            MyTextView(width: geometry.size.width)
        }
    }
}

Reference to the GradientLabel class

Reference to wrapping a UILabel to use in SwiftUI

我希望这会有所帮助!