Skip to content

Commit

Permalink
Initial commit - created package & added files
Browse files Browse the repository at this point in the history
  • Loading branch information
franklynw committed Feb 4, 2021
0 parents commit f68038a
Show file tree
Hide file tree
Showing 15 changed files with 461 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
7 changes: 7 additions & 0 deletions .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
16 changes: 16 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"object": {
"pins": [
{
"package": "HalfASheet",
"repositoryURL": "https://github.com/franklynw/HalfASheet.git",
"state": {
"branch": null,
"revision": "2f1959c5e8a74cb078b96942d46388b7e4bebcdb",
"version": "1.0.0"
}
}
]
},
"version": 1
}
27 changes: 27 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "FontPicker",
platforms: [
.iOS(.v14)
],
products: [
.library(
name: "FontPicker",
targets: ["FontPicker"]),
],
dependencies: [
.package(name: "HalfASheet", url: "https://github.com/franklynw/HalfASheet.git", .upToNextMajor(from: "1.0.0"))
],
targets: [
.target(
name: "FontPicker",
dependencies: ["HalfASheet"]),
.testTarget(
name: "FontPickerTests",
dependencies: ["FontPicker"]),
]
)
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# FontPicker

A description of this package.
78 changes: 78 additions & 0 deletions Sources/FontPicker/FontPicker+Modifiers.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//
// File.swift
//
//
// Created by Franklyn Weber on 04/02/2021.
//

import SwiftUI


extension FontPicker {

/// The color to use for the background of the picker
/// - Parameter backgroundColor: a UIColor
public func backgroundColor(_ backgroundColor: UIColor) -> Self {
var copy = self
copy.backgroundColor = backgroundColor
return copy
}

/// Normally, selecting a font will dismiss the picker. Use this to disable that functionality (eg if you have a preview which shows how the font will look in context)
public var disableDismissOnSelection: Self {
var copy = self
copy._dismissOnSelection = false
return copy
}

/// Use this to display only the fonts you want
/// - Parameter fontNames: the names of the fonts you want to display
public func fontNames(_ fontNames: [String]) -> Self {
var copy = self
copy.userFontNames = fontNames
return copy
}

/// Add your own fonts to the default list of those provided by the system
/// - Parameter fontNames: the names of the fonts you want to add
public func additionalFontNames(_ fontNames: [String]) -> Self {
var copy = self
copy.additionalFontNames = fontNames
return copy
}

/// If there are any fonts you don't want to appear, specify them here. By default, "Bodoni Ornaments", "Damascus" & "Hiragino" are excluded as they render in an odd way, with bits chopped off.
/// To reinstate them, specify an empty array here
/// - Parameter fontNames: the names of the fonts you want to add
public func excludedFontNames(_ fontNames: [String]) -> Self {
var copy = self
copy.excludedFontNames = fontNames
return copy
}
}


extension View {

/// View extension in the style of .sheet - lacks a couple of customisation options. If more flexibility is required, use FontPicker(...) directly, and apply the required modifiers
/// - Parameters:
/// - isPresented: binding to a Bool which controls whether or not to show the picker
/// - selected: binding to a String for the selected font name
func fontPicker(isPresented: Binding<Bool>, selected: Binding<String>) -> some View {
modifier(FontPickerPresentationModifier(content: { FontPicker(isPresented: isPresented, selected: selected)}))
}
}


struct FontPickerPresentationModifier: ViewModifier {

var content: () -> FontPicker

init(@ViewBuilder content: @escaping () -> FontPicker) {
self.content = content
}

func body(content: Content) -> some View {
self.content()
}
}
93 changes: 93 additions & 0 deletions Sources/FontPicker/FontPicker.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//
// FontPicker.swift
// HandyList
//
// Created by Franklyn Weber on 28/01/2021.
//

import SwiftUI
import HalfASheet


struct FontPicker: View {

@Binding private var isPresented: Bool
@Binding private var selection: String

internal var userFontNames: [String]?
internal var additionalFontNames: [String] = []

// these fonts render in an odd way, with tops or bottoms being chopped off, or being nonsense for our purposes (such as symbols)
// However, if the user wants them, they just need to specify [] as the excludedFontNames
internal var excludedFontNames = [
"Bodoni Ornaments",
"Damascus",
"Hiragino"
]

internal var backgroundColor: UIColor = .systemBackground
internal var _dismissOnSelection = true


/// Initialiser
/// - Parameters:
/// - isPresented: binding to a Bool which controls whether or not to show the picker
/// - selected: binding to a String for the selected font name
init(isPresented: Binding<Bool>, selected: Binding<String>) {
_isPresented = isPresented
_selection = selected
}

var body: some View {

HalfASheet(isPresented: $isPresented, title: "Font") {

ZStack {

RoundedRectangle(cornerRadius: 10)
.foregroundColor(Color(UIColor.lightGray.withAlphaComponent(0.2)))
RoundedRectangle(cornerRadius: 10)
.foregroundColor(Color(backgroundColor))

List {
ForEach(fontNamesToDisplay(), id: \.self) { fontName in

HStack {
Text(fontName)
.customFont(name: fontName)
Spacer()
}
.contentShape(Rectangle())
.onTapGesture {
selection = fontName
if _dismissOnSelection {
isPresented = false
}
}
}
.listRowBackground(Color.clear)
}
}
.onAppear {
NotificationCenter.default.post(name: .fontPickerAppeared, object: self)
}
.onDisappear {
NotificationCenter.default.post(name: .fontPickerDisappeared, object: self)
}
}
.backgroundColor(backgroundColor)
.closeButtonColor(UIColor.gray.withAlphaComponent(0.4))
.disableDragToDismiss
}

private func fontNamesToDisplay() -> [String] {

if let userFontNames = userFontNames {
return userFontNames
}

let fontNames = UIFont.familyNames.sorted().filter { !excludedFontNames.contains(where: $0.contains) }

return (fontNames + additionalFontNames).sorted()
}
}
99 changes: 99 additions & 0 deletions Sources/FontPicker/Helpers/CustomFont.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//
// CustomFont.swift
//
// Created by Franklyn on 25/06/2019.
// Copyright © 2019 Franklyn. All rights reserved.
//

import SwiftUI


struct CustomFont: ViewModifier {

let name: String
let size: CGFloat?
let style: Font.TextStyle?
let weight: Font.Weight
let maxSize: CGFloat?

init(name: String, size: CGFloat? = nil, weight: Font.Weight = .regular, relativeTo style: Font.TextStyle? = nil) {
self.name = name
self.style = style
self.size = size
self.weight = weight
maxSize = nil
}

init(name: String, weight: Font.Weight = .regular, relativeTo style: Font.TextStyle, maxSize: CGFloat) {
self.name = name
self.weight = weight
self.maxSize = maxSize
self.style = style
size = nil
}


/// Will trigger the refresh of the view when the ContentSizeCategory changes.
@Environment(\.sizeCategory) var sizeCategory: ContentSizeCategory

func body(content: Content) -> some View {

if let maxSize = maxSize, let style = style {
let fontSize = min(Self.textSize(for: style), maxSize)
return content.font(Font.custom(self.name, size: Self.scaledSize(for: fontSize, style: style)).weight(weight))
}

if let size = self.size {
if let style = style {
return content.font(Font.custom(name, size: size, relativeTo: style).weight(weight))
} else {
return content.font(Font.custom(name, size: size).weight(weight))
}
}

guard let style = self.style else {
return content.font(Font.custom(name, size: Self.textSize(for: .body)).weight(weight))
}
guard let size = self.fontSizes[style] else {
return content.font(Font.custom(name, size: Self.textSize(for: .body)).weight(weight))
}

return content.font(Font.custom(self.name, size: size, relativeTo: .body).weight(weight))
}


private let fontSizes: [Font.TextStyle: CGFloat] = [
.largeTitle: 34,
.title: 28,
.title2: 22,
.title3: 20,
.headline: 17,
.body: 17,
.callout: 16,
.subheadline: 15,
.footnote: 13,
.caption: 12,
.caption2: 11
]

private static func scaledSize(for size: CGFloat, style: Font.TextStyle) -> CGFloat {
let scale = UIFontMetrics.default.scaledValue(for: size)
return size * size / scale
}

private static func textSize(for textStyle: Font.TextStyle) -> CGFloat {
return UIFont.preferredFont(style: textStyle.uiStyle).pointSize
}
}


extension Text {

func customFont(name: String, size: CGFloat? = nil, weight: Font.Weight = .regular, relativeTo style: Font.TextStyle? = nil) -> ModifiedContent<Self, CustomFont> {
return modifier(CustomFont(name: name, size: size, weight: weight, relativeTo: style))
}

func customFont(name: String, weight: Font.Weight = .regular, relativeTo style: Font.TextStyle, maxSize: CGFloat) -> ModifiedContent<Self, CustomFont> {
return modifier(CustomFont(name: name, weight: weight, relativeTo: style, maxSize: maxSize))
}
}
47 changes: 47 additions & 0 deletions Sources/FontPicker/Helpers/Font.TextStyle+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// Font.TextStyle+Extensions.swift
//
// Created by Franklyn Weber on 02/02/2021.
//

import SwiftUI


extension Font.TextStyle {

var uiStyle: UIFont.TextStyle {
switch self {
case .largeTitle: return .largeTitle
case .title: return .title1
case .title2: return .title2
case .title3: return .title3
case .headline: return .headline
case .body: return .body
case .callout: return .callout
case .subheadline: return .subheadline
case .footnote: return .footnote
case .caption: return .caption1
case .caption2: return .caption2
@unknown default:
return .body
}
}
}


extension Font.Weight {

var uiWeight: UIFont.Weight {
switch self {
case .thin: return .thin
case .light: return .light
case .regular: return .regular
case .medium: return .medium
case .semibold: return .semibold
case .bold: return .bold
case .heavy: return .heavy
case .black: return .black
default: return .regular
}
}
}
Loading

0 comments on commit f68038a

Please sign in to comment.