Site icon AppTractor

Дизайн-система в SwiftUI

Что такое дизайн-система?

Система дизайна (Design System) — это комплексный набор рекомендаций, компонентов и инструментов, которые используются для создания последовательных и целостных пользовательских интерфейсов на разных платформах и в разных продуктах. Она служит единым источником истины для дизайна и разработки, помогая командам поддерживать единый внешний вид и ощущение от своих продуктов.

В двух словах, дизай-система нужна нам для того, чтобы обеспечить единый внешний вид и восприятие различных продуктов на разных платформах, обеспечивая бесшовный пользовательский опыт.

Благодаря согласованности мы можем получить эффективность, которая ускоряет процесс проектирования и разработки за счет предоставления многократно используемых компонентов и шаблонов.

Улучшение сотрудничества между дизайнерами и разработчиками получается за счет предоставления общего языка и набора инструментов, уменьшения недопонимания и обеспечения согласованности.

OK, нам нужно внедрить систему проектирования в наш проект. Сделаем это на примере. Шаблон дизайна я взял из Figma Free Mobile App Template. Для этого проекта я выбрал приложение Podcast.

Для создания системы дизайна нам нужно создать такие основы, как цвет, типографика и интервалы.

Дизайн-система

Цвета

Для цвета я использую Ассеты, в которых объявляю все цвета, которые мне нужны, как на картинке ниже:

Цвета

Затем мы создаем расширение для работы с цветовыми маркерами:

import SwiftUI

public extension Color {
  static let accent1 = Color("Accent 1", bundle: .module)
  static let accent2 = Color("Accent 2", bundle: .module)
  static let accent3 = Color("Accent 3", bundle: .module)
  
  static let grayScale1 = Color("Grayscale 1", bundle: .module)
  static let grayScale2 = Color("Grayscale 2", bundle: .module)
  static let grayScale3 = Color("Grayscale 3", bundle: .module)
  static let grayScale4 = Color("Grayscale 4", bundle: .module)
  static let grayScale5 = Color("Grayscale 5", bundle: .module)
}

Я знаю, что названия цветов все еще могут путаться, но пока мы просто создадим подобие Podcast App Guidelines. Мы можем использовать его следующим образом:

Color.accent1

Типографика

Для типографики нам нужно импортировать наш пользовательский шрифт в менеджер пакетов или проект:

Шрифты

После того как мы импортировали шрифт в наш проект, нам нужно создать класс и метод для регистрации пользовательского шрифта в приложении, это очень просто:

import SwiftUI

public enum NotoSans: String, CaseIterable {
  case thin = "NotoSans-Thin"
  case bold = "NotoSans-Bold"
  case light = "NotoSans-Light"
  case black = "NotoSans-Black"
  case medium = "NotoSans-Medium"
  case regular = "NotoSans-Regular"
  case semiBold = "NotoSans-SemiBold"
  case extraBold = "NotoSans-ExtraBold"
  case extraLight = "NotoSans-ExtraLight"
}

public struct NotoSansFont {
  public static func registerFonts() {
    NotoSans.allCases.forEach {
      registerFont(bundle: .module, fontName: $0.rawValue, fontExtension: "ttf")
    }
  }

  fileprivate static func registerFont(bundle: Bundle, fontName: String, fontExtension: String) {
    guard let fontURL = bundle.url(forResource: fontName, withExtension: fontExtension),
          let fontDataProvider = CGDataProvider(url: fontURL as CFURL),
          let font = CGFont(fontDataProvider) else {
      fatalError("Couldn't ceate font from filename: \(fontName) with extension \(fontExtension)")
    }
    var error: Unmanaged<CFError>?
    CTFontManagerRegisterGraphicsFont(font, &error)
  }
}

Объявите здесь все типы вашего шрифта, после чего создайте расширение для шрифта следующим образом:

import SwiftUI

extension Font {
  public static var h1: Font = {
    return notoSans(.bold, size: 36)
  }()
  
  public static var h2: Font = {
    return notoSans(.semiBold, size: 24)
  }()
  
  public static var h3: Font = {
    return notoSans(.medium, size: 18)
  }()
  
  public static var h4: Font = {
    return notoSans(.medium, size: 14)
  }()
  
  public static var h5: Font = {
    return notoSans(.medium, size: 12)
  }()
  
  public static var h6: Font = {
    return notoSans(.regular, size: 12)
  }()

  public static func notoSans(_ font: NotoSans, size: CGFloat) -> Font {
    return .custom(font.rawValue, size: size)
  }
}

Чтобы использовать шрифт, мы можем просто сделать так:

Text("Listen to the best Podcast")
 .font(.h1)
 .foregroundColor(.grayScale1)

Но мы можем еще больше упростить задачу с помощью ViewModifier:

struct TitleStyle: ViewModifier {
  func body(content: Content) -> some View {
    content.font(.h1)
  }
}

extension View {
  public func titleStyle() -> some View {
    modifier(TitleStyle())
  }
}

Тогда мы можем применить шрифт следующим образом:

Text("Listen to the best Podcast")
  .foregroundColor(.grayScale1)
   .titleStyle()

Интервалы

Для интервалов мы просто добавим следующее:

public enum Spacing {
  public static let small: CGFloat = 8
  public static let medium: CGFloat = 16
  public static let large: CGFloat = 32
  public static let extrLarge: CGFloat = 64
}

Основание готово!

Далее мы перейдем к компонентам и просто создадим компонент кнопки.

Кнопка

В SwiftUI мы можем создать ButtonStyle, который может быть использован для всех кнопок.

Так как же его использовать?

На самом деле все очень просто, вы можете просто скопировать из моего кода:

import SwiftUI

public struct PrimaryButtonStyle: ButtonStyle {
  public init() {}
  
  public func makeBody(configuration: Configuration) -> some View {
    configuration.label
      .font(.h4)
      .padding(Spacing.medium)
      .background(Capsule().fill(Color.accent1))
      .foregroundColor(.white)
      .scaleEffect(configuration.isPressed ? 0.95 : 1.0)
  }
}

А как это использовать? Следующим образом:

Button {

} label: {
  Text("Create new account")
}
.buttonStyle(PrimaryButtonStyle())

Но мы можем сделать все более красивым, просто добавив такой код:

extension ButtonStyle where Self == PrimaryButtonStyle {
  public static var primary: Self { Self() }
}

Поэтому всякий раз, когда вы регистрируете ButtonStyle, вы просто вызываете его следующим образом:

Button {

} label: {
  Text("Create new account")
}
.buttonStyle(.primary)

Дайте мне 6 часов, чтобы срубить дерево, и первые 4 часа я потрачу на заточку топора.

Так и с дизайн-системой. Мы потратим 4 часа на заточку топора, это заняло больше половины времени, но это того стоило.

Источник

Exit mobile version