mirror of
https://github.com/dscyrescotti/Memola.git
synced 2026-05-11 02:10:05 +02:00
feat: add color picker
This commit is contained in:
@@ -81,6 +81,8 @@
|
||||
ECFA15242BEF223300455818 /* GraphicContextObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFA15232BEF223300455818 /* GraphicContextObject.swift */; };
|
||||
ECFA15262BEF224900455818 /* StrokeObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFA15252BEF224900455818 /* StrokeObject.swift */; };
|
||||
ECFA15282BEF225000455818 /* QuadObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFA15272BEF225000455818 /* QuadObject.swift */; };
|
||||
ECFC51272BF8885700D0D051 /* ColorPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFC51262BF8885700D0D051 /* ColorPicker.swift */; };
|
||||
ECFC512A2BF8BBD800D0D051 /* Triangle.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFC51292BF8BBD800D0D051 /* Triangle.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@@ -160,6 +162,8 @@
|
||||
ECFA15232BEF223300455818 /* GraphicContextObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphicContextObject.swift; sourceTree = "<group>"; };
|
||||
ECFA15252BEF224900455818 /* StrokeObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StrokeObject.swift; sourceTree = "<group>"; };
|
||||
ECFA15272BEF225000455818 /* QuadObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuadObject.swift; sourceTree = "<group>"; };
|
||||
ECFC51262BF8885700D0D051 /* ColorPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPicker.swift; sourceTree = "<group>"; };
|
||||
ECFC51292BF8BBD800D0D051 /* Triangle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Triangle.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -225,6 +229,7 @@
|
||||
EC50500A2BF6672000B4D86E /* Components */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ECFC51282BF8BBD000D0D051 /* Shapes */,
|
||||
EC50500B2BF6673300B4D86E /* ViewModifiers */,
|
||||
);
|
||||
path = Components;
|
||||
@@ -315,6 +320,7 @@
|
||||
ECA7387B2BE5EF3500A4542E /* Memo */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ECFC51252BF8885000D0D051 /* ColorPicker */,
|
||||
EC5050082BF65D0500B4D86E /* Memo */,
|
||||
EC5050052BF65CCD00B4D86E /* PenDock */,
|
||||
);
|
||||
@@ -599,6 +605,22 @@
|
||||
path = Objects;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
ECFC51252BF8885000D0D051 /* ColorPicker */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ECFC51262BF8885700D0D051 /* ColorPicker.swift */,
|
||||
);
|
||||
path = ColorPicker;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
ECFC51282BF8BBD000D0D051 /* Shapes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ECFC51292BF8BBD800D0D051 /* Triangle.swift */,
|
||||
);
|
||||
path = Shapes;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@@ -672,6 +694,7 @@
|
||||
ECA738B02BE60D0B00A4542E /* CanvasViewController.swift in Sources */,
|
||||
ECA738E42BE6110800A4542E /* Drawable.swift in Sources */,
|
||||
ECA738AD2BE60CC600A4542E /* DrawingView.swift in Sources */,
|
||||
ECFC512A2BF8BBD800D0D051 /* Triangle.swift in Sources */,
|
||||
ECA738E02BE610B900A4542E /* EraserRenderPass.swift in Sources */,
|
||||
EC35655A2BF060D900A4E0BF /* Quad.metal in Sources */,
|
||||
ECA738912BE600F500A4542E /* Cache.metal in Sources */,
|
||||
@@ -704,6 +727,7 @@
|
||||
ECA738CB2BE60F1900A4542E /* ViewPortContext.swift in Sources */,
|
||||
ECA738EE2BE6125D00A4542E /* simd_float4x4++.swift in Sources */,
|
||||
ECA7388C2BE6009600A4542E /* Textures.swift in Sources */,
|
||||
ECFC51272BF8885700D0D051 /* ColorPicker.swift in Sources */,
|
||||
ECA738B82BE60DDC00A4542E /* HistoryEvent.swift in Sources */,
|
||||
EC0D14262BF7A8C9009BFE5F /* PenObject.swift in Sources */,
|
||||
ECA738952BE6012D00A4542E /* ViewPort.metal in Sources */,
|
||||
|
||||
@@ -117,7 +117,7 @@ extension GraphicContext {
|
||||
func beginStroke(at point: CGPoint, pen: Pen) -> Stroke {
|
||||
let stroke = Stroke(
|
||||
bounds: [point.x - pen.thickness, point.y - pen.thickness, point.x + pen.thickness, point.y + pen.thickness],
|
||||
color: pen.color,
|
||||
color: pen.rgba,
|
||||
style: pen.strokeStyle.rawValue,
|
||||
createdAt: .now,
|
||||
thickness: pen.thickness
|
||||
|
||||
@@ -16,6 +16,7 @@ public class Tool: NSObject, ObservableObject {
|
||||
@Published var pens: [Pen] = []
|
||||
@Published var selectedPen: Pen?
|
||||
@Published var draggedPen: Pen?
|
||||
@Published var opensColorPicker: Bool = false
|
||||
|
||||
let scrollPublisher = PassthroughSubject<String, Never>()
|
||||
|
||||
@@ -32,6 +33,7 @@ public class Tool: NSObject, ObservableObject {
|
||||
}
|
||||
if let selectedPen = pens.first(where: { $0.isSelected }) {
|
||||
selectPen(selectedPen)
|
||||
scrollPublisher.send(selectedPen.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@ class Pen: NSObject, ObservableObject, Identifiable {
|
||||
object?.style = strokeStyle.rawValue
|
||||
}
|
||||
}
|
||||
@Published var color: [CGFloat] {
|
||||
@Published var rgba: [CGFloat] {
|
||||
didSet {
|
||||
object?.color = color
|
||||
object?.color = rgba
|
||||
}
|
||||
}
|
||||
@Published var thickness: CGFloat {
|
||||
@@ -33,12 +33,18 @@ class Pen: NSObject, ObservableObject, Identifiable {
|
||||
object?.isSelected = isSelected
|
||||
}
|
||||
}
|
||||
var color: Color {
|
||||
get { Color.rgba(from: rgba) }
|
||||
set {
|
||||
rgba = newValue.components
|
||||
}
|
||||
}
|
||||
|
||||
init(object: PenObject) {
|
||||
self.object = object
|
||||
self.id = object.objectID.uriRepresentation().absoluteString
|
||||
self.style = (Stroke.Style(rawValue: object.style) ?? .marker).anyPenStyle
|
||||
self.color = object.color
|
||||
self.rgba = object.color
|
||||
self.thickness = object.thickness
|
||||
self.isSelected = object.isSelected
|
||||
super.init()
|
||||
|
||||
20
Memola/Components/Shapes/Triangle.swift
Normal file
20
Memola/Components/Shapes/Triangle.swift
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// Triangle.swift
|
||||
// Memola
|
||||
//
|
||||
// Created by Dscyre Scotti on 5/18/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Foundation
|
||||
|
||||
struct Triangle: Shape {
|
||||
func path(in rect: CGRect) -> Path {
|
||||
var path = Path()
|
||||
path.move(to: CGPoint(x: rect.maxX, y: rect.minY))
|
||||
path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
|
||||
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
|
||||
path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
|
||||
return path
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,18 @@ extension Color {
|
||||
}
|
||||
}
|
||||
|
||||
extension Color {
|
||||
var hsba: (hue: Double, saturation: Double, brightness: Double, alpha: Double) {
|
||||
let uiColor = UIColor(self)
|
||||
var hue: CGFloat = 0
|
||||
var saturation: CGFloat = 0
|
||||
var brightness: CGFloat = 0
|
||||
var alpha: CGFloat = 0
|
||||
uiColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
|
||||
return (hue, saturation, brightness, alpha)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIColor {
|
||||
var components: [CGFloat] {
|
||||
let uiColor: UIColor = self
|
||||
|
||||
184
Memola/Features/Memo/ColorPicker/ColorPicker.swift
Normal file
184
Memola/Features/Memo/ColorPicker/ColorPicker.swift
Normal file
@@ -0,0 +1,184 @@
|
||||
//
|
||||
// ColorPicker.swift
|
||||
// Memola
|
||||
//
|
||||
// Created by Dscyre Scotti on 5/18/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Foundation
|
||||
|
||||
struct ColorPicker: View {
|
||||
@ObservedObject var pen: Pen
|
||||
|
||||
@State var hue: Double = 1
|
||||
@State var saturation: Double = 0
|
||||
@State var brightness: Double = 1
|
||||
@State var alpha: Double = 1
|
||||
|
||||
let size: CGFloat = 15
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 10) {
|
||||
colorPicker
|
||||
.frame(width: 180, height: 180)
|
||||
HStack(spacing: 10) {
|
||||
Color(hue: hue, saturation: saturation, brightness: brightness)
|
||||
.overlay {
|
||||
Image("transparent-grid-square")
|
||||
.resizable()
|
||||
.scaleEffect(1.8)
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.opacity(0.5)
|
||||
.overlay {
|
||||
pen.color
|
||||
}
|
||||
.clipShape(Triangle())
|
||||
}
|
||||
.frame(width: size * 2 + 10)
|
||||
.cornerRadius(5)
|
||||
VStack(spacing: 10) {
|
||||
hueSlider
|
||||
alphaSlider
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(10)
|
||||
.background {
|
||||
Rectangle()
|
||||
.fill(.regularMaterial)
|
||||
.ignoresSafeArea(.all)
|
||||
}
|
||||
.onAppear {
|
||||
let hsba = pen.color.hsba
|
||||
hue = hsba.hue
|
||||
saturation = hsba.saturation
|
||||
brightness = hsba.brightness
|
||||
alpha = hsba.alpha * 1.43 - 0.43
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var colorPicker: some View {
|
||||
GeometryReader { proxy in
|
||||
ZStack {
|
||||
Color(hue: hue, saturation: 1, brightness: 1)
|
||||
LinearGradient(
|
||||
colors: [.white, .clear],
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
LinearGradient(
|
||||
colors: [.black, .clear],
|
||||
startPoint: .bottom,
|
||||
endPoint: .top
|
||||
)
|
||||
}
|
||||
.cornerRadius(5)
|
||||
.drawingGroup()
|
||||
.overlay(alignment: .bottomLeading) {
|
||||
Color(hue: hue, saturation: saturation, brightness: brightness)
|
||||
.frame(width: size, height: size)
|
||||
.clipShape(Circle())
|
||||
.overlay {
|
||||
Circle()
|
||||
.strokeBorder(.white, lineWidth: 2)
|
||||
}
|
||||
.offset(x: -size + 5, y: size - 5)
|
||||
.offset(x: max(proxy.size.width * saturation, size - 10), y: min(proxy.size.height * -brightness, -size + 10))
|
||||
}
|
||||
.gesture(
|
||||
DragGesture(minimumDistance: 0)
|
||||
.onChanged { value in
|
||||
saturation = min(1, max(value.location.x / proxy.size.width, 0))
|
||||
brightness = 1 - min(1, max(value.location.y / proxy.size.height, 0))
|
||||
updateBaseColor()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var hueSlider: some View {
|
||||
GeometryReader { proxy in
|
||||
ZStack(alignment: .leading) {
|
||||
LinearGradient(
|
||||
colors: (0...10).map { Color(hue: Double($0) * 0.1, saturation: 1, brightness: 1) },
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
Color(hue: hue, saturation: 1, brightness: 1)
|
||||
.frame(width: size, height: size)
|
||||
.overlay {
|
||||
Circle()
|
||||
.strokeBorder(.white, lineWidth: 2)
|
||||
}
|
||||
.clipShape(Circle())
|
||||
.offset(x: -size)
|
||||
.offset(x: max(size, proxy.size.width * hue))
|
||||
}
|
||||
.frame(width: proxy.size.width, height: proxy.size.height)
|
||||
.clipShape(Capsule())
|
||||
.gesture(
|
||||
DragGesture(minimumDistance: 0)
|
||||
.onChanged { value in
|
||||
hue = min(1, max(value.location.x / proxy.size.width, 0))
|
||||
updateBaseColor()
|
||||
}
|
||||
.onEnded { value in
|
||||
hue = min(1, max(value.location.x / proxy.size.width, 0))
|
||||
updateBaseColor()
|
||||
}
|
||||
)
|
||||
}
|
||||
.frame(height: size)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var alphaSlider: some View {
|
||||
GeometryReader { proxy in
|
||||
let color = Color(hue: hue, saturation: saturation, brightness: brightness)
|
||||
ZStack(alignment: .leading) {
|
||||
LinearGradient(
|
||||
colors: (3...10).map { color.opacity(0.1 * CGFloat($0)) },
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
.background {
|
||||
Image("transparent-grid-rect")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.opacity(0.5)
|
||||
.background(.white)
|
||||
}
|
||||
color
|
||||
.frame(width: size, height: size)
|
||||
.overlay {
|
||||
Circle()
|
||||
.strokeBorder(.white, lineWidth: 2)
|
||||
}
|
||||
.clipShape(Circle())
|
||||
.offset(x: -size)
|
||||
.offset(x: max(size, proxy.size.width * alpha))
|
||||
}
|
||||
.frame(width: proxy.size.width, height: proxy.size.height)
|
||||
.clipShape(Capsule())
|
||||
.gesture(
|
||||
DragGesture(minimumDistance: 0)
|
||||
.onChanged { value in
|
||||
alpha = min(1, max(value.location.x / proxy.size.width, 0))
|
||||
updateBaseColor()
|
||||
}
|
||||
.onEnded { value in
|
||||
alpha = min(1, max(value.location.x / proxy.size.width, 0))
|
||||
updateBaseColor()
|
||||
}
|
||||
)
|
||||
}
|
||||
.frame(height: size)
|
||||
}
|
||||
|
||||
func updateBaseColor() {
|
||||
pen.color = Color(hue: hue, saturation: saturation, brightness: brightness).opacity(0.7 * alpha + 0.3)
|
||||
}
|
||||
}
|
||||
@@ -15,41 +15,85 @@ struct PenDockView: View {
|
||||
let factor: CGFloat = 0.95
|
||||
|
||||
var body: some View {
|
||||
ScrollViewReader { proxy in
|
||||
ScrollView(.vertical, showsIndicators: false) {
|
||||
LazyVStack(spacing: 0) {
|
||||
ForEach(tool.pens) { pen in
|
||||
penView(pen)
|
||||
.id(pen.id)
|
||||
.scrollTransition { content, phase in
|
||||
content
|
||||
.scaleEffect(phase.isIdentity ? 1 : 0.04, anchor: .trailing)
|
||||
VStack(alignment: .trailing) {
|
||||
if let pen = tool.selectedPen {
|
||||
VStack(spacing: 10) {
|
||||
Button {
|
||||
tool.opensColorPicker = true
|
||||
} label: {
|
||||
let hsba = pen.color.hsba
|
||||
Color(hue: hsba.hue, saturation: hsba.saturation, brightness: hsba.brightness)
|
||||
.overlay {
|
||||
Image("transparent-grid-square")
|
||||
.resizable()
|
||||
.scaleEffect(1.8)
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.opacity(0.5)
|
||||
.overlay {
|
||||
pen.color
|
||||
}
|
||||
.clipShape(Triangle())
|
||||
}
|
||||
.clipShape(Capsule())
|
||||
.frame(height: 20)
|
||||
.drawingGroup()
|
||||
}
|
||||
.hoverEffect(.lift)
|
||||
.popover(isPresented: $tool.opensColorPicker) {
|
||||
ColorPicker(pen: pen)
|
||||
.presentationCompactAdaptation(.popover)
|
||||
}
|
||||
Capsule()
|
||||
.frame(height: 20)
|
||||
}
|
||||
.padding(.vertical, 10)
|
||||
.padding(.leading, 40)
|
||||
.padding()
|
||||
.frame(width: width * factor - 18)
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 20)
|
||||
.fill(.regularMaterial)
|
||||
}
|
||||
.transition(.move(edge: .trailing).combined(with: .opacity))
|
||||
} else {
|
||||
Color.clear
|
||||
.frame(width: width * factor - 18, height: 50)
|
||||
}
|
||||
.onReceive(tool.scrollPublisher) { id in
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
withAnimation {
|
||||
proxy.scrollTo(id)
|
||||
ScrollViewReader { proxy in
|
||||
ScrollView(.vertical, showsIndicators: false) {
|
||||
LazyVStack(spacing: 0) {
|
||||
ForEach(tool.pens) { pen in
|
||||
penView(pen)
|
||||
.id(pen.id)
|
||||
.scrollTransition { content, phase in
|
||||
content
|
||||
.scaleEffect(phase.isIdentity ? 1 : 0.04, anchor: .trailing)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 10)
|
||||
.padding(.leading, 40)
|
||||
}
|
||||
.onReceive(tool.scrollPublisher) { id in
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
withAnimation {
|
||||
proxy.scrollTo(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxHeight:( (height * factor + 10) * 6) + 20)
|
||||
.fixedSize()
|
||||
.background(alignment: .trailing) {
|
||||
RoundedRectangle(cornerRadius: 20)
|
||||
.fill(.regularMaterial)
|
||||
.frame(width: width * factor - 18)
|
||||
}
|
||||
.clipShape(.rect(cornerRadii: .init(bottomTrailing: 20, topTrailing: 20)))
|
||||
.overlay(alignment: .bottomLeading) {
|
||||
newPenButton
|
||||
.offset(x: 60, y: 10)
|
||||
}
|
||||
}
|
||||
.frame(maxHeight:( (height * factor + 10) * 7) + 20)
|
||||
.fixedSize()
|
||||
.background(alignment: .trailing) {
|
||||
RoundedRectangle(cornerRadius: 20)
|
||||
.fill(.regularMaterial)
|
||||
.frame(width: width * factor - 15)
|
||||
}
|
||||
.clipShape(.rect(cornerRadii: .init(bottomTrailing: 20, topTrailing: 20)))
|
||||
.overlay(alignment: .bottomLeading) {
|
||||
newPenButton
|
||||
.offset(x: 60, y: 10)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
@@ -59,7 +103,7 @@ struct PenDockView: View {
|
||||
Image(tip)
|
||||
.resizable()
|
||||
.renderingMode(.template)
|
||||
.foregroundStyle(Color.rgba(from: pen.color))
|
||||
.foregroundStyle(Color.rgba(from: pen.rgba))
|
||||
}
|
||||
Image(pen.style.icon.base)
|
||||
.resizable()
|
||||
@@ -74,12 +118,13 @@ struct PenDockView: View {
|
||||
tool.selectPen(pen)
|
||||
}
|
||||
}
|
||||
.padding(.leading, 10)
|
||||
.contextMenu(if: pen.strokeStyle != .eraser) {
|
||||
ControlGroup {
|
||||
Button {
|
||||
let originalPen = pen
|
||||
let pen = PenObject.createObject(\.viewContext, penStyle: originalPen.style)
|
||||
pen.color = originalPen.color
|
||||
pen.color = originalPen.rgba
|
||||
pen.isSelected = true
|
||||
pen.tool = tool.object
|
||||
let _pen = Pen(object: pen)
|
||||
@@ -109,7 +154,6 @@ struct PenDockView: View {
|
||||
.contentShape(.dragPreview, .rect(cornerRadius: 10))
|
||||
}
|
||||
.onDrop(of: [.item], delegate: PenDropDelegate(id: pen.id, tool: tool))
|
||||
.padding(.leading, 10)
|
||||
.offset(x: tool.selectedPen === pen ? 0 : 25)
|
||||
}
|
||||
|
||||
@@ -142,7 +186,7 @@ struct PenDockView: View {
|
||||
Image(tip)
|
||||
.resizable()
|
||||
.renderingMode(.template)
|
||||
.foregroundStyle(Color.rgba(from: pen.color))
|
||||
.foregroundStyle(Color.rgba(from: pen.rgba))
|
||||
}
|
||||
Image(pen.style.icon.base)
|
||||
.resizable()
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
23
Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-rect.imageset/Contents.json
vendored
Normal file
23
Memola/Resources/Assets/Assets.xcassets/backgrounds/transparent-grid-rect.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "transparency_grid 1.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "transparency_grid 1@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "transparency_grid 1@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "transparency_grid 1.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "transparency_grid 1@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "transparency_grid 1@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.8 KiB |
Reference in New Issue
Block a user