使用SwiftUI的新态。第1部分

敬礼,哈布里沃派!预期将推出高级课程“ IOS Developer”,我们还准备了另一篇有趣的翻译。




尽管实际上,Apple在WWDC18上就将其用作设计主题,但非变形设计也许是近几个月来最有趣的趋势。在本文中,我们将研究如何使用SwiftUI实现非变形设计,为什么要这样做,以及-最重要的是-我们如何优化该设计以增加其可访问性。
重要提示:新态(有时也称为神经形态)会对可访问性产生严重影响,因此尽管有阅读本文第一部分的诱惑,但跳过了其余部分,但我还是恳请您阅读本文末尾,并检查优缺点,以便可以看到整个图片。 。


新形态学的基础


在继续学习代码之前,我想简要概述一下设计方向的两个基本原理,因为在我们前进时它们将是相关的:

  1. Neomorphism使用眩光和阴影来确定屏幕上对象的形状。
  2. 对比度趋于降低;不会使用完全白色或黑色,这样可以突出显示高光和阴影。

最终结果是使人联想到“挤压塑料”的外观-一种用户界面设计,看起来自然而有趣,而不会撞到您的眼睛。我不得不再说一遍,降低对比度并使用阴影突出显示形状会严重影响可访问性,我们将在稍后再次讨论。

但是,我仍然认为花时间学习SwiftUI中的新事物是值得的-即使您不在自己的应用程序中使用新事物,这就像编写kata来帮助提高技能的代码一样。

好了,足够多的闲聊-让我们继续进行代码。

建立非形态图


最简单的起点是创建一个非变形贴图:一个将包含一些信息的圆角矩形。接下来,我们将研究如何将这些原理转移到SwiftUI的其他部分。

让我们首先使用Single View应用程序模板创建一个新的iOS项目。确保将SwiftUI用于用户界面,然后将项目命名为Neumorphism。

提示:如果您有权使用Xcode预览SwiftUI,建议您立即激活它-实验起来会容易得多。

我们将从定义代表乳白色调的颜色开始。这不是纯灰色,而是非常微妙的阴影,为界面增加了些许温暖或凉爽。您可以根据需要将其添加到资产目录,但是现在可以更轻松地在代码中进行操作。在结构之外

添加ColorContentView

extension Color {
    static let offWhite = Color(red: 225 / 255, green: 225 / 255, blue: 235 / 255)
}

是的,它几乎是白色的,但是它足够深,可以在需要时使真实的白色看起来像是发光的。

现在,我们可以ContentView提供它填充ZStack整个屏幕,并使用新的准白色填充整个空间:

struct ContentView: View {
    var body: some View {
        ZStack {
            Color.offWhite
        }
        .edgesIgnoringSafeArea(.all)
    }
}

为了表示我们的地图,我们将使用300x300分辨率的圆角矩形使其在屏幕上清晰美观。因此,将其添加到ZStack下方的颜色下:

RoundedRectangle(cornerRadius: 25)
    .frame(width: 300, height: 300)

默认情况下,它将是黑色,但是对于新变形的实现,我们希望大幅降低对比度,因此我们将其替换为与背景相同的颜色,实际上使形状不可见。

所以像这样改变它:

RoundedRectangle(cornerRadius: 25)
    .fill(Color.offWhite)
    .frame(width: 300, height: 300)

重要的一点是:我们使用阴影,一暗一灯来确定形状,就像光线从屏幕的左上角投射光线一样。

SwiftUI允许我们多次应用修饰符,这有助于实现新态。将以下两个修饰符添加到圆角矩形:

.shadow(color: Color.black.opacity(0.2), radius: 10, x: 10, y: 10)
.shadow(color: Color.white.opacity(0.7), radius: 10, x: -5, y: -5)

它们代表右下角的暗影偏移和左上角的光影偏移。之所以可以看到光影,是因为我们使用了准白色的背景,现在地图变得可见了。

我们只写了几行代码,但是我们已经有了一个非形态映射-我希望您同意SwiftUI出人意料地促进了这一过程!



创建一个简单的非变形按钮


在用户界面的所有元素中,新变态对卡的风险相当低-如果卡内的用户界面清晰,则卡可能没有清晰的边框,并且不会影响可访问性。按钮是另一回事,因为它们被设计为可以交互的,所以降低对比度可能弊大于利。

让我们通过创建我们自己的按钮样式来解决此问题,因为这是SwiftUI允许按钮配置分布在许多地方的方式。这比向您创建的每个按钮添加许多修饰符要方便得多-我们只需定义一次样式即可在很多地方使用它。

我们将定义一个实际上为空的按钮样式:SwiftUI将为我们提供按钮的标签,该标签可以是文本,图像或其他内容,我们将不做任何更改将其发送回去。

在外部添加此结构ContentView

struct SimpleButtonStyle: ButtonStyle {
    func makeBody(configuration: Self.Configuration) -> some View {
        configuration.label
    }
}

configuration.label就是保存按钮内容的内容,很快我们将添加其他内容。首先,让我们定义一个使用它的按钮,以便您可以查看设计的发展情况:

Button(action: {
    print("Button tapped")
}) {
    Image(systemName: "heart.fill")
        .foregroundColor(.gray)
}
.buttonStyle(SimpleButtonStyle())

您不会在屏幕上看到任何特别的东西,但是我们可以通过在按钮样式中添加非变形效果来修复它。这次我们将不使用圆角矩形,因为对于简单的图标来说,圆形会更好,但是我们需要添加一些缩进,以使按钮单击区域大而美观。通过添加一些缩进来
更改方法makeBody(),然后将非变形效果作为按钮的背景:

configuration.label
    .padding(30)
    .background(
        Circle()
            .fill(Color.offWhite)
            .shadow(color: Color.black.opacity(0.2), radius: 10, x: 10, y: 10)
            .shadow(color: Color.white.opacity(0.7), radius: 10, x: -5, y: -5)
    )



这使我们足够接近所需的效果,但是如果您运行该应用程序,您会发现实际上该行为仍然不是完美的-按下按钮时,该按钮没有视觉响应,这看起来很奇怪。

要解决此问题,我们需要configuration.isPressed在自定义按钮样式中读取属性,该属性指示按钮当前是否处于按下状态。我们可以使用它来改进样式,以直观地指示按钮是否被按下。

让我们从一个简单的例子开始:我们将使用Group按钮作为背景,然后检查configuration.isPressed并返回(如果按下了按钮,则返回一个平坦的圆圈),否则返回当前的深色圆圈:

configuration.label
    .padding(30)
    .background(
        Group {
            if configuration.isPressed {
                Circle()
                    .fill(Color.offWhite)
            } else {
                Circle()
                    .fill(Color.offWhite)
                    .shadow(color: Color.black.opacity(0.2), radius: 10, x: 10, y: 10)
                    .shadow(color: Color.white.opacity(0.7), radius: 10, x: -5, y: -5)
            }
        }
    )

由于在状态下isPressed使用了近似白色圆圈,因此按下按钮时我们的效果不可见。

警告:由于SwiftUI计算可点击区域的方式,我们无意中使按钮的点击区域很小-现在您需要点击图像本身,而不是其周围的非变形设计。要解决此问题,请.contentShape(Circle())在之后立即添加修饰符.padding(30),以强制SwiftUI使用所有可用的分接空间。

现在我们可以通过翻转阴影来创建人造凹面效果-通过复制shadow基本效果中的两个修改器,将X和Y值交换为白色和黑色,如下所示:

if configuration.isPressed {
    Circle()
        .fill(Color.offWhite)
        .shadow(color: Color.black.opacity(0.2), radius: 10, x: -5, y: -5)
        .shadow(color: Color.white.opacity(0.7), radius: 10, x: 10, y: 10)
} else {
    Circle()
        .fill(Color.offWhite)
        .shadow(color: Color.black.opacity(0.2), radius: 10, x: 10, y: 10)
        .shadow(color: Color.white.opacity(0.7), radius: 10, x: -5, y: -5)
}

估计结果。



为按钮单击创建内部阴影


原则上,我们当前的代码已经可以使用,但是人们对效果的理解有所不同-有些人将其视为凹形按钮,而另一些人认为按钮仍未被按下,只是光线来自不同的角度。

改进的想法是创建一个内部阴影,该阴影将模拟向内按下按钮的效果。这不是标准SwiftUI工具包的一部分,但是我们可以很容易地实现它。

创建一个内部阴影需要两个线性渐变,它们只是我们将在本文中使用的许多内部渐变中的第一个,因此我们将立即添加一个小的辅助扩展,LinearGradient以简化标准渐变的创建:

extension LinearGradient {
    init(_ colors: Color...) {
        self.init(gradient: Gradient(colors: colors), startPoint: .topLeading, endPoint: .bottomTrailing)
    }
}

这样,我们可以简单地提供一个可变的颜色列表,以返回它们在对角线方向上的线性渐变。

现在,要点很重要:我们不会在已按下的圆上添加两个翻转的阴影,而是要使用模糊(描边)覆盖一个新圆,然后再应用一个渐变的圆作为蒙版。这有点棘手,但让我零散地解释一下:

  • 我们的基本圆是具有新变形效果的当前圆,其中填充了近似白色的颜色。
  • 我们在其顶部放一个圆,并用灰色框框起来,然后稍微模糊以柔化其边缘。
  • 然后,我们将一个带有另一个圆的蒙版应用到叠加在顶部的该圆上,这次填充了线性渐变。

当您将一个视图用作另一个视图的遮罩时,SwiftUI会使用遮罩的Alpha通道来确定应在基本视图中显示的内容。

因此,如果我们绘制一个模糊的灰色笔触,然后用从黑色到透明的线性渐变对其进行遮罩,那么模糊的笔触将在一侧不可见,而在另一侧逐渐增大-我们将获得一个平滑的内部渐变。为了使效果更加明显,我们可以在两个方向上稍微移动阴影圆。经过一些实验,我发现绘制阴影较浅的阴影较浅的阴影有助于最大化效果。

请记住,两个阴影用于在新态中创建深度感:一个亮和一个暗,因此我们将两次用不同的颜色添加内部阴影的这种效果。如下

更改圆configuration.isPress

Circle()
    .fill(Color.offWhite)
    .overlay(
        Circle()
            .stroke(Color.gray, lineWidth: 4)
            .blur(radius: 4)
            .offset(x: 2, y: 2)
            .mask(Circle().fill(LinearGradient(Color.black, Color.clear)))
    )
    .overlay(
        Circle()
            .stroke(Color.white, lineWidth: 8)
            .blur(radius: 4)
            .offset(x: -2, y: -2)
            .mask(Circle().fill(LinearGradient(Color.clear, Color.black)))
    )

如果再次运行该应用程序,您将看到按按钮的效果更加明显并且看起来更好。



至此,翻译的第一部分结束了。在接下来的几天里,我们将发布续篇,现在,我们邀请您进一步了解即将开始的课程

All Articles