Neomorphismus mit SwiftUI. Teil 1

Gruß, Chabrowiten! Im Vorfeld des Starts des Fortgeschrittenenkurses „IOS Developer“ haben wir eine weitere interessante Übersetzung vorbereitet.




Nicht-morphisches Design ist vielleicht der interessanteste Trend der letzten Monate, obwohl Apple es in Wahrheit als Designmotiv auf der WWDC18 verwendet hat. In diesem Artikel werden wir untersuchen, wie Sie ein nicht-morphisches Design mit SwiftUI implementieren können, warum Sie dies möglicherweise tun möchten und - was am wichtigsten ist - wie wir dieses Design verfeinern können, um seine Zugänglichkeit zu verbessern.
Wichtig : Neomorphismus - manchmal auch als Neuromorphismus bezeichnet - hat schwerwiegende Folgen für die Zugänglichkeit. Trotz der Versuchung, den ersten Teil dieses Artikels zu lesen und den Rest zu überspringen, fordere ich Sie auf, den Artikel bis zum Ende zu lesen und sowohl die Vor- als auch die Nachteile zu untersuchen, damit Sie das gesamte Bild sehen können .


Grundlagen des Neomorphismus


Bevor wir zum Code übergehen, möchte ich kurz zwei Grundprinzipien dieser Richtung im Design skizzieren, da sie im weiteren Verlauf relevant sein werden:

  1. Der Neomorphismus verwendet Blendung und Schatten, um die Formen von Objekten auf dem Bildschirm zu bestimmen.
  2. Der Kontrast nimmt tendenziell ab; Es werden keine vollständig weißen oder schwarzen Elemente verwendet, mit denen Sie Glanzlichter und Schatten hervorheben können.

Das Endergebnis ist ein Look, der an „extrudierten Kunststoff“ erinnert - ein Design der Benutzeroberfläche, das auf jeden Fall frisch und interessant aussieht, ohne in die Augen zu stoßen. Ich kann nur noch einmal wiederholen, dass das Verringern des Kontrasts und das Verwenden von Schatten zum Hervorheben von Formen die Zugänglichkeit ernsthaft beeinträchtigt, und wir werden später darauf zurückkommen.

Ich denke jedoch immer noch, dass sich die Zeit, die Sie mit dem Erlernen des Neomorphismus in SwiftUI verbringen, lohnt - auch wenn Sie ihn nicht in Ihren eigenen Anwendungen verwenden, ist er wie eine Code-Schreib-Kata, um Ihre Fähigkeiten zu verbessern.

Okay, genug Leerlaufgespräche - fahren wir mit dem Code fort.

Erstellen einer nicht-morphischen Karte


Der einfachste Ausgangspunkt ist das Erstellen einer nicht-morphischen Karte: ein abgerundetes Rechteck, das einige Informationen enthält. Als nächstes werden wir uns ansehen, wie wir diese Prinzipien auf andere Teile von SwiftUI übertragen können.

Beginnen wir mit der Erstellung eines neuen iOS-Projekts mithilfe der Single View-App-Vorlage. Stellen Sie sicher, dass Sie SwiftUI für die Benutzeroberfläche verwenden, und benennen Sie das Projekt Neumorphism.

Tipp: Wenn Sie Zugriff auf die Vorschau von SwiftUI in Xcode haben, empfehle ich, diese sofort zu aktivieren - das Experimentieren ist für Sie viel einfacher.

Zunächst definieren wir eine Farbe, die einen cremigen Farbton darstellt. Dies ist kein reines Grau, sondern ein sehr subtiler Farbton, der der Benutzeroberfläche ein wenig Wärme oder Kühle verleiht. Sie können es dem Asset-Verzeichnis hinzufügen, wenn Sie möchten, aber jetzt ist es einfacher, es im Code zu tun.

Fügen Sie dies Coloraußerhalb der Struktur hinzu ContentView:

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

Ja, es ist fast weiß, aber es ist dunkel genug, um echtes Weiß wie ein Glühen aussehen zu lassen, wenn wir es brauchen.

Jetzt können wir den Körper füllen, ContentViewindem wir ihn bereitstellen ZStack, der den gesamten Bildschirm einnimmt, und unsere neue quasi-weiße Farbe verwenden, um den gesamten Raum auszufüllen:

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

Zur Darstellung unserer Karte verwenden wir ein abgerundetes Rechteck mit einer Auflösung von 300 x 300, um es auf dem Bildschirm schön und klar zu machen. Fügen Sie dies also ZStackunter der Farbe hinzu:

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

Standardmäßig ist es schwarz, aber für die Implementierung des Neomorphismus möchten wir den Kontrast stark reduzieren, sodass wir ihn durch dieselbe Farbe ersetzen, die wir für den Hintergrund verwenden, wodurch die Form tatsächlich unsichtbar wird.

Also ändere es so:

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

Ein wichtiger Punkt: Wir bestimmen die Form mit Schatten, einem dunklen und einem hellen, als würde das Licht Strahlen aus der oberen linken Ecke des Bildschirms werfen.

Mit SwiftUI können wir Modifikatoren mehrmals anwenden, was die Implementierung von Neomorphismus erleichtert. Fügen Sie Ihrem abgerundeten Rechteck die folgenden zwei Modifikatoren hinzu:

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

Sie repräsentieren den Versatz des dunklen Schattens in der unteren rechten Ecke und den Versatz des hellen Schattens in der oberen linken Ecke. Der helle Schatten ist sichtbar, weil wir einen quasi weißen Hintergrund verwendet haben, und jetzt wird die Karte sichtbar.

Wir haben nur ein paar Codezeilen geschrieben, aber wir haben bereits eine nicht-morphische Karte - ich hoffe, Sie werden zustimmen, dass SwiftUI den Prozess überraschend erleichtert!



Erstellen einer einfachen nicht-morphischen Schaltfläche


Von allen Elementen einer Benutzeroberfläche stellt der Neomorphismus ein relativ geringes Risiko für Karten dar. Wenn die Benutzeroberfläche in Ihren Karten klar ist, hat die Karte möglicherweise keinen klaren Rand, was die Zugänglichkeit nicht beeinträchtigt. Tasten sind eine andere Sache, da sie für die Interaktion ausgelegt sind, sodass eine Verringerung des Kontrasts mehr schaden als nützen kann.

Lassen Sie uns damit umgehen, indem wir unseren eigenen Schaltflächenstil erstellen, da SwiftUI auf diese Weise die Verteilung von Schaltflächenkonfigurationen an vielen Stellen ermöglicht. Dies ist viel praktischer als das Hinzufügen vieler Modifikatoren zu jeder von Ihnen erstellten Schaltfläche. Wir können den Stil einfach einmal definieren und an vielen Stellen verwenden.

Wir werden einen Schaltflächenstil definieren, der tatsächlich leer ist: SwiftUI gibt uns die Bezeichnung für die Schaltfläche, die Text, Bild oder etwas anderes sein kann, und wir senden sie ohne Änderungen zurück.

Fügen Sie diese Struktur irgendwo außerhalb hinzu ContentView:

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

Dieser configuration.labelenthält den Inhalt der Schaltfläche, und bald werden wir noch etwas hinzufügen. Definieren wir zunächst eine Schaltfläche, die sie verwendet, damit Sie sehen können, wie sich das Design entwickelt:

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

Auf dem Bildschirm wird nichts Besonderes angezeigt, aber wir können das Problem beheben, indem wir dem Schaltflächenstil unseren nicht-morphischen Effekt hinzufügen. Dieses Mal wird kein abgerundetes Rechteck verwendet, da für einfache Symbole der Kreis besser ist. Wir müssen jedoch einige Einrückungen hinzufügen, damit der Klickbereich für Schaltflächen groß und schön ist.
Ändern Sie Ihre Methode, makeBody()indem Sie einen Einzug hinzufügen und dann unseren nicht-morphischen Effekt als Hintergrund für die Schaltfläche platzieren:

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



Dies bringt uns dem gewünschten Effekt nahe genug, aber wenn Sie die Anwendung ausführen, werden Sie feststellen, dass das Verhalten in der Praxis immer noch nicht perfekt ist - die Taste reagiert beim Drücken nicht visuell, was nur seltsam aussieht.

Um dies zu beheben, müssen wir die Eigenschaft configuration.isPressedin unserem benutzerdefinierten Schaltflächenstil lesen , der angibt, ob die Schaltfläche gerade gedrückt wird oder nicht. Wir können dies verwenden, um unseren Stil zu verbessern und visuell anzuzeigen, ob die Taste gedrückt wird.

Beginnen wir mit einem einfachen: Wir werden GroupSchaltflächen für den Hintergrund verwenden und dann configuration.isPressedentweder einen flachen Kreis überprüfen und zurückgeben, wenn die Schaltfläche gedrückt wird, oder unseren aktuellen abgedunkelten Kreis ansonsten:

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

Da im Zustand ein isPressedKreis mit einer quasi weißen Farbe verwendet wird, wird unser Effekt beim Drücken der Taste unsichtbar.

Warnung: Aufgrund der Art und Weise, wie SwiftUI tippbare Bereiche berechnet, haben wir den Klickbereich für unsere Schaltfläche unbeabsichtigt sehr klein gemacht. Jetzt müssen Sie auf das Bild selbst tippen und nicht auf das unomorphe Design um es herum. Um dies zu beheben, fügen Sie .contentShape(Circle())unmittelbar danach einen Modifikator hinzu .padding(30), der SwiftUI zwingt, den gesamten verfügbaren Speicherplatz zu nutzen.

Jetzt können wir den Effekt der künstlichen Konkavität shadowerzeugen, indem wir den Schatten umdrehen - indem wir zwei Modifikatoren aus dem Basiseffekt kopieren und die X- und Y-Werte gegen Weiß und Schwarz austauschen, wie hier gezeigt:

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

Schätzen Sie das Ergebnis.



Erstellen Sie interne Schatten für einen Klick auf eine Schaltfläche


Unser aktueller Code funktioniert im Prinzip bereits, aber die Leute interpretieren den Effekt anders - einige sehen ihn als konkaven Knopf, andere sehen, dass der Knopf immer noch nicht gedrückt wird, nur das Licht kommt aus einem anderen Winkel.

Die Idee der Verbesserung besteht darin, einen inneren Schatten zu erzeugen, der den Effekt des Drückens der Taste nach innen simuliert. Dies ist nicht Teil des Standard-SwiftUI-Kits, aber wir können es recht einfach implementieren.

Das Erstellen eines inneren Schattens erfordert zwei lineare Verläufe. Diese sind nur die ersten von vielen internen Verläufen, die wir in diesem Artikel verwenden. Daher fügen wir sofort eine kleine Hilfserweiterung für hinzu LinearGradient, um die Erstellung von Standardverläufen zu vereinfachen:

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

Damit können wir einfach eine variable Liste von Farben bereitstellen, um ihren linearen Farbverlauf in diagonaler Richtung wiederherzustellen.

Nun zum wichtigen Punkt: Anstatt unserem gedrückten Kreis zwei umgedrehte Schatten hinzuzufügen, überlagern wir einen neuen Kreis mit einer Unschärfe (Strich) und wenden dann einen weiteren Kreis mit einem Farbverlauf als Maske an. Das ist etwas kniffliger, aber lassen Sie es mich Stück für Stück erklären:

  • Unser Grundkreis ist unser aktueller Kreis mit einem neomorphen Effekt, der mit einer quasi weißen Farbe gefüllt ist.
  • Wir platzieren einen Kreis darüber, eingerahmt von einem grauen Rahmen, und verwischen leicht, um die Ränder zu mildern.
  • Dann wenden wir eine Maske mit einem anderen Kreis auf diesen darüber liegenden Kreis an, diesmal gefüllt mit einem linearen Farbverlauf.

Wenn Sie eine Ansicht als Maske für eine andere anwenden, verwendet SwiftUI den Alphakanal der Maske, um festzulegen, was in der Basisansicht angezeigt werden soll.

Wenn wir also einen verschwommenen grauen Strich zeichnen und ihn dann mit einem linearen Farbverlauf von schwarz nach transparent maskieren, ist der verschwommene Strich auf der einen Seite unsichtbar und nimmt auf der anderen Seite allmählich zu - wir erhalten einen glatten internen Farbverlauf. Um den Effekt stärker zu machen, können wir die schattierten Kreise leicht in beide Richtungen verschieben. Nachdem ich ein wenig experimentiert hatte, stellte ich fest, dass das Zeichnen eines hellen Schattens mit einer dickeren Linie als einer dunklen dazu beiträgt, den Effekt zu maximieren.

Denken Sie daran, dass zwei Schatten verwendet werden, um ein Gefühl der Tiefe im Neomorphismus zu erzeugen: ein Licht und ein Dunkel. Daher werden wir diesen Effekt des inneren Schattens zweimal mit unterschiedlichen Farben hinzufügen.

Ändern Sie den Kreis configuration.isPresswie folgt:

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

Wenn Sie die Anwendung erneut ausführen, werden Sie feststellen, dass das Drücken einer Taste viel ausgeprägter ist und besser aussieht.



Damit endete der erste Teil der Übersetzung. In den kommenden Tagen werden wir die Fortsetzung veröffentlichen und jetzt laden wir Sie ein , mehr über den bevorstehenden Kurs zu erfahren .

All Articles