├── .DS_Store
├── .gitignore
├── Draw_App_01_Final
├── .DS_Store
├── Draw App.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── Draw App
│ ├── .DS_Store
│ ├── AppDelegate.swift
│ ├── Extensions
│ ├── CGPoint + Helpers.swift
│ ├── CGRect + Helpers.swift
│ ├── CGSize + Helpers.swift
│ ├── Number+Helpers.swift
│ └── UIColor + Utils.swift
│ ├── Metal
│ ├── .DS_Store
│ ├── DrawingGestureRecognizer.swift
│ ├── DrawingView.swift
│ ├── Point.swift
│ ├── Renderer.swift
│ └── Shader.metal
│ ├── SceneDelegate.swift
│ ├── Supporting Files
│ ├── .DS_Store
│ ├── Assets.xcassets
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Base.lproj
│ │ └── LaunchScreen.storyboard
│ ├── DrawingApp-BridgingHeader.h
│ └── Info.plist
│ └── ViewController.swift
├── Draw_App_01_Starter
├── .DS_Store
├── Draw App.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── Draw App
│ ├── .DS_Store
│ ├── Drawing
│ ├── DrawingGestureRecognizer.swift
│ └── Shader.metal
│ ├── Extensions
│ ├── CGPoint + Helpers.swift
│ ├── CGRect + Helpers.swift
│ ├── CGSize + Helpers.swift
│ ├── Number+Helpers.swift
│ └── UIColor + Utils.swift
│ ├── Supporting Files
│ ├── .DS_Store
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Base.lproj
│ │ └── LaunchScreen.storyboard
│ ├── DrawingApp-BridgingHeader.h
│ ├── Info.plist
│ └── SceneDelegate.swift
│ └── ViewController.swift
├── Draw_App_02_Final
├── .DS_Store
├── Draw App.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── Draw App
│ ├── .DS_Store
│ ├── AppDelegate.swift
│ ├── BottomView
│ ├── Color Picker
│ │ ├── CollorPickerBottomSheetController.swift
│ │ ├── ColorPaletteCollectionViewCell.swift
│ │ ├── ColorPickerBottomSheetView.swift
│ │ ├── ColorPickerGridView.swift
│ │ ├── ColorPickerPresentationController.swift
│ │ ├── ColorPickerSlider.swift
│ │ ├── ColorPickerSliderInputView.swift
│ │ └── ColorPickerSpectrumView.swift
│ ├── EditorBottomSegementedControl.swift
│ ├── EditorBottomSizeEditor.swift
│ ├── EditorBottomView.swift
│ ├── EditorBottomViewModel.swift
│ └── SizeSlider.swift
│ ├── DrawingViewController.swift
│ ├── Extensions
│ ├── CGImage + PixelColor.swift
│ ├── CGPoint + Helpers.swift
│ ├── CGRect + Helpers.swift
│ ├── CGSize + Helpers.swift
│ ├── MTLTexture + Helpers.swift
│ ├── Number+Helpers.swift
│ ├── UIApplication + currentWindow.swift
│ ├── UIColor + Utils.swift
│ ├── UIControl + Combine.swift
│ ├── UIImage + Resources.swift
│ ├── UIView + Helpers.swift
│ └── UIView+Constraints.swift
│ ├── Metal
│ ├── .DS_Store
│ ├── DrawingGestureRecognizer.swift
│ ├── DrawingView.swift
│ ├── Renderer.swift
│ └── Shader.metal
│ ├── Models
│ ├── Brush.swift
│ ├── Color.swift
│ └── Point.swift
│ ├── SceneDelegate.swift
│ └── Supporting Files
│ ├── .DS_Store
│ ├── Assets.xcassets
│ ├── .DS_Store
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Contents.json
│ ├── add.imageset
│ │ ├── Contents.json
│ │ └── add.png
│ ├── arrowTip.imageset
│ │ ├── Contents.json
│ │ └── arrowTip.png
│ ├── back.imageset
│ │ ├── Contents.json
│ │ └── back.png
│ ├── blurEraser.imageset
│ │ ├── Contents.json
│ │ └── blurEraser.png
│ ├── blurTip.imageset
│ │ ├── Contents.json
│ │ └── blurTip.png
│ ├── brush.imageset
│ │ ├── Contents.json
│ │ └── brush.png
│ ├── brush_tip.imageset
│ │ ├── Contents.json
│ │ └── brush.png
│ ├── cancel.imageset
│ │ ├── Contents.json
│ │ └── cancel.png
│ ├── close.imageset
│ │ ├── Close.pdf
│ │ └── Contents.json
│ ├── color_picker.imageset
│ │ ├── .DS_Store
│ │ ├── Contents.json
│ │ └── color_picker_gradient.png
│ ├── download.imageset
│ │ ├── Contents.json
│ │ └── download.png
│ ├── eraser.imageset
│ │ ├── Contents.json
│ │ └── eraser.png
│ ├── eyedropper.imageset
│ │ ├── Contents.json
│ │ └── eyedropper.pdf
│ ├── neon.imageset
│ │ ├── Contents.json
│ │ └── neon.png
│ ├── neon_tip.imageset
│ │ ├── Contents.json
│ │ └── neon.png
│ ├── objectEraser.imageset
│ │ ├── Contents.json
│ │ └── objectEraser.png
│ ├── opacity_slider_background.imageset
│ │ ├── Contents.json
│ │ └── VStack.pdf
│ ├── opacity_slider_thumb.imageset
│ │ ├── Contents.json
│ │ └── Ellipse 26.pdf
│ ├── pen.imageset
│ │ ├── Contents.json
│ │ └── pen.png
│ ├── pen_tip.imageset
│ │ ├── Contents.json
│ │ └── pen.png
│ ├── pencil.imageset
│ │ ├── Contents.json
│ │ └── pencil.png
│ ├── pencil_tip.imageset
│ │ ├── Contents.json
│ │ └── pencil.png
│ ├── roundTip.imageset
│ │ ├── Contents.json
│ │ └── roundTip.png
│ ├── shapeArrow.imageset
│ │ ├── Contents.json
│ │ └── shapeArrow.png
│ ├── shapeBubble.imageset
│ │ ├── Contents.json
│ │ └── shapeBubble.png
│ ├── shapeEllipse.imageset
│ │ ├── Contents.json
│ │ └── shapeEllipse.png
│ ├── shapeRectangle.imageset
│ │ ├── Contents.json
│ │ └── shapeRectangle.png
│ ├── shapeStar.imageset
│ │ ├── Contents.json
│ │ └── shapeStar.png
│ ├── size_slslider.imageset
│ │ ├── Contents.json
│ │ └── Rectangle 53@2x.png
│ ├── undo.imageset
│ │ ├── Contents.json
│ │ └── undo.png
│ ├── xmarkTip.imageset
│ │ ├── Contents.json
│ │ └── xmarkTip.png
│ └── zoomOut.imageset
│ │ ├── Contents.json
│ │ └── zoomOut.png
│ ├── Base.lproj
│ └── LaunchScreen.storyboard
│ ├── DrawingApp-BridgingHeader.h
│ └── Info.plist
├── Draw_App_02_Starter
├── .DS_Store
├── Draw App.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── Draw App
│ ├── .DS_Store
│ ├── AppDelegate.swift
│ ├── BottomView
│ ├── Color Picker
│ │ ├── CollorPickerBottomSheetController.swift
│ │ ├── ColorPaletteCollectionViewCell.swift
│ │ ├── ColorPickerBottomSheetView.swift
│ │ ├── ColorPickerGridView.swift
│ │ ├── ColorPickerPresentationController.swift
│ │ ├── ColorPickerSlider.swift
│ │ ├── ColorPickerSliderInputView.swift
│ │ └── ColorPickerSpectrumView.swift
│ ├── EditorBottomSegementedControl.swift
│ ├── EditorBottomSizeEditor.swift
│ ├── EditorBottomView.swift
│ ├── EditorBottomViewModel.swift
│ └── SizeSlider.swift
│ ├── DrawingViewController.swift
│ ├── Extensions
│ ├── CGImage + PixelColor.swift
│ ├── CGPoint + Helpers.swift
│ ├── CGRect + Helpers.swift
│ ├── CGSize + Helpers.swift
│ ├── MTLTexture + Helpers.swift
│ ├── Number+Helpers.swift
│ ├── UIApplication + currentWindow.swift
│ ├── UIColor + Utils.swift
│ ├── UIControl + Combine.swift
│ ├── UIImage + Resources.swift
│ ├── UIView + Helpers.swift
│ └── UIView+Constraints.swift
│ ├── Metal
│ ├── .DS_Store
│ ├── DrawingGestureRecognizer.swift
│ ├── DrawingView.swift
│ ├── Renderer.swift
│ └── Shader.metal
│ ├── Models
│ ├── Brush.swift
│ ├── Color.swift
│ └── Point.swift
│ ├── SceneDelegate.swift
│ └── Supporting Files
│ ├── .DS_Store
│ ├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Contents.json
│ ├── blurEraser 1.imageset
│ │ ├── Contents.json
│ │ └── blurEraser.png
│ ├── blurEraser.imageset
│ │ ├── Contents.json
│ │ └── blurEraser.png
│ ├── blurTip 1.imageset
│ │ ├── Contents.json
│ │ └── blurTip.png
│ ├── blurTip.imageset
│ │ ├── Contents.json
│ │ └── blurTip.png
│ ├── brush 1.imageset
│ │ ├── Contents.json
│ │ └── brush.png
│ ├── brush.imageset
│ │ ├── Contents.json
│ │ └── brush.png
│ ├── brush_tip 1.imageset
│ │ ├── Contents.json
│ │ └── brush.png
│ ├── brush_tip.imageset
│ │ ├── Contents.json
│ │ └── brush.png
│ ├── close 1.imageset
│ │ ├── Close.pdf
│ │ └── Contents.json
│ ├── close.imageset
│ │ ├── Close.pdf
│ │ └── Contents.json
│ ├── color_picker.imageset
│ │ ├── Contents.json
│ │ └── color_picker_gradient.png
│ ├── eraser.imageset
│ │ ├── Contents.json
│ │ └── eraser.png
│ ├── neon.imageset
│ │ ├── Contents.json
│ │ └── neon.png
│ ├── neon_tip.imageset
│ │ ├── Contents.json
│ │ └── neon.png
│ ├── objectEraser.imageset
│ │ ├── Contents.json
│ │ └── objectEraser.png
│ ├── opacity_slider_background.imageset
│ │ ├── Contents.json
│ │ └── VStack.pdf
│ ├── opacity_slider_thumb.imageset
│ │ ├── Contents.json
│ │ └── Ellipse 26.pdf
│ ├── pen.imageset
│ │ ├── Contents.json
│ │ └── pen.png
│ ├── pen_tip.imageset
│ │ ├── Contents.json
│ │ └── pen.png
│ ├── pencil.imageset
│ │ ├── Contents.json
│ │ └── pencil.png
│ ├── pencil_tip.imageset
│ │ ├── Contents.json
│ │ └── pencil.png
│ ├── roundTip.imageset
│ │ ├── Contents.json
│ │ └── roundTip.png
│ └── size_slslider.imageset
│ │ ├── Contents.json
│ │ └── Rectangle 53@2x.png
│ ├── Base.lproj
│ └── LaunchScreen.storyboard
│ ├── DrawingApp-BridgingHeader.h
│ └── Info.plist
├── LICENSE
└── README.md
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## User settings
6 | xcuserdata/
7 |
8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
9 | *.xcscmblueprint
10 | *.xccheckout
11 |
12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
13 | build/
14 | DerivedData/
15 | *.moved-aside
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 |
28 | ## App packaging
29 | *.ipa
30 | *.dSYM.zip
31 | *.dSYM
32 |
33 | ## Playgrounds
34 | timeline.xctimeline
35 | playground.xcworkspace
36 |
37 | # Swift Package Manager
38 | #
39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40 | # Packages/
41 | # Package.pins
42 | # Package.resolved
43 | # *.xcodeproj
44 | #
45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
46 | # hence it is not needed unless you have added a package configuration file to your project
47 | # .swiftpm
48 |
49 | .build/
50 |
51 | # CocoaPods
52 | #
53 | # We recommend against adding the Pods directory to your .gitignore. However
54 | # you should judge for yourself, the pros and cons are mentioned at:
55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
56 | #
57 | # Pods/
58 | #
59 | # Add this line if you want to avoid checking in source code from the Xcode workspace
60 | # *.xcworkspace
61 |
62 | # Carthage
63 | #
64 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
65 | # Carthage/Checkouts
66 |
67 | Carthage/Build/
68 |
69 | # Accio dependency management
70 | Dependencies/
71 | .accio/
72 |
73 | # fastlane
74 | #
75 | # It is recommended to not store the screenshots in the git repo.
76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
77 | # For more information about the recommended setup visit:
78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
79 |
80 | fastlane/report.xml
81 | fastlane/Preview.html
82 | fastlane/screenshots/**/*.png
83 | fastlane/test_output
84 |
85 | # Code Injection
86 | #
87 | # After new code Injection tools there's a generated folder /iOSInjectionProject
88 | # https://github.com/johnno1962/injectionforxcode
89 |
90 | iOSInjectionProject/
91 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_01_Final/.DS_Store
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_01_Final/Draw App/.DS_Store
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 29/04/2023.
6 | //
7 |
8 | import UIKit
9 |
10 | @main
11 | class AppDelegate: UIResponder, UIApplicationDelegate {
12 |
13 |
14 |
15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
16 | // Override point for customization after application launch.
17 | return true
18 | }
19 |
20 | // MARK: UISceneSession Lifecycle
21 |
22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
23 | // Called when a new scene session is being created.
24 | // Use this method to select a configuration to create the new scene with.
25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
26 | }
27 |
28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
29 | // Called when the user discards a scene session.
30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
32 | }
33 |
34 |
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/Extensions/CGPoint + Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGPoint + Arithmetic.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 11/10/2022.
6 | //
7 |
8 | import Foundation
9 | import simd
10 |
11 | extension CGPoint {
12 | func interpolate(to: CGPoint, progress: CGFloat) -> CGPoint {
13 | guard progress >= 0.0 && progress <= 1.0 else { return self }
14 | let x = self.x.interpolate(to: to.x, progress: progress)
15 | let y = self.y.interpolate(to: to.y, progress: progress)
16 | return CGPoint(x: x, y: y)
17 | }
18 |
19 | init(vector: vector_uint2) {
20 | self.init(x: CGFloat(vector.x), y: CGFloat(vector.y))
21 | }
22 |
23 | init(vector: vector_float2) {
24 | self.init(x: vector.x.cgFloat, y: vector.y.cgFloat)
25 | }
26 |
27 | var vectorFloat: vector_float2 {
28 | return vector_float2(x: x.float, y: y.float)
29 | }
30 |
31 | var vectorUint: vector_uint2 {
32 | return vector_uint2(x: x.int32, y: y.int32)
33 | }
34 |
35 | // Arithmetic
36 |
37 | static func + (lhs: CGPoint, rhs: CGPoint) -> CGPoint {
38 | return CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
39 | }
40 |
41 | static func -(lhs: CGPoint, rhs: CGPoint) -> CGVector {
42 | return CGVector(dx: lhs.x - rhs.x, dy: lhs.y - rhs.y)
43 | }
44 |
45 | static func -(lhs: CGPoint, rhs: CGVector) -> CGPoint {
46 | return CGPoint(x: lhs.x - rhs.dx, y: lhs.y - rhs.dy)
47 | }
48 |
49 | static func *(lhs: CGPoint, rhs: CGFloat) -> CGPoint {
50 | return CGPoint(x: lhs.x * rhs, y: lhs.y * rhs)
51 | }
52 |
53 | static func *(lhs: CGFloat, rhs: CGPoint) -> CGPoint {
54 | return CGPoint(x: rhs.x * lhs, y: rhs.y * lhs)
55 | }
56 |
57 | func distanceToPoint(otherPoint: CGPoint) -> CGFloat {
58 | return sqrt(pow((otherPoint.x - x), 2) + pow((otherPoint.y - y), 2))
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/Extensions/CGRect + Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGRect + Helpers.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 11/10/2022.
6 | //
7 |
8 | import Foundation
9 |
10 | extension CGRect {
11 |
12 | public init(size: CGSize) {
13 | self.init(origin: .zero, size: size)
14 | }
15 |
16 | func scale(_ factor: CGFloat) -> CGRect {
17 | let transform = CGAffineTransform(scaleX: factor, y: factor)
18 | return self.applying(transform)
19 | }
20 |
21 | func interpolate(to: CGRect, progress: CGFloat) -> CGRect {
22 | guard progress >= 0.0, progress <= 1.0 else { return self }
23 | let origin = origin.interpolate(to: to.origin, progress: progress)
24 | let size = size.interpolate(to: to.size, progress: progress)
25 | return CGRect(origin: origin, size: size)
26 | }
27 |
28 | var center: CGPoint {
29 | return CGPoint(x: midX, y: midY)
30 | }
31 |
32 | init(center: CGPoint, size: CGSize) {
33 | self.init(origin: CGPoint(x: center.x - size.width / 2, y: center.y - size.height / 2), size: size)
34 | }
35 |
36 | func withOrigin(_ newOrigin: CGPoint) -> CGRect {
37 | var newRect = self
38 | newRect.origin = newOrigin
39 | return newRect
40 | }
41 |
42 | func withX(_ newX: CGFloat) -> CGRect {
43 | return withOrigin(CGPoint(x: newX, y: minY))
44 | }
45 |
46 | func withY(_ newY: CGFloat) -> CGRect {
47 | return withOrigin(CGPoint(x: minX, y: newY))
48 | }
49 |
50 | func withSize(_ newValue: CGSize) -> CGRect {
51 | var newRect = self
52 | newRect.size = newValue
53 | return newRect
54 | }
55 |
56 | func withWidth(_ newWidth: CGFloat) -> CGRect {
57 | return withSize(CGSize(width: newWidth, height: height))
58 | }
59 |
60 |
61 | func withHeight(_ newHeight: CGFloat) -> CGRect {
62 | return withSize(CGSize(width: width, height: newHeight))
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/Extensions/CGSize + Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGSize + Helpers.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 11/10/2022.
6 | //
7 |
8 | import Foundation
9 |
10 | extension CGSize {
11 |
12 | init(size: CGFloat) {
13 | self.init(width: size, height: size)
14 | }
15 |
16 | func interpolate(to: CGSize, progress: CGFloat) -> CGSize {
17 | guard progress >= 0.0 && progress <= 1.0 else { return self }
18 |
19 | let width = self.width.interpolate(to: to.width, progress: progress)
20 | let height = self.height.interpolate(to: to.height, progress: progress)
21 | return CGSize(width: width, height: height)
22 | }
23 |
24 | func scale(_ factor: CGFloat) -> CGSize {
25 | let transform = CGAffineTransform(scaleX: factor, y: factor)
26 | return self.applying(transform)
27 | }
28 |
29 | static func *(lhs: CGSize, rhs: CGSize) -> CGSize {
30 | return CGSize(width: lhs.width * rhs.width, height: lhs.height * rhs.height)
31 | }
32 |
33 | static func *(lhs: CGSize, rhs: CGFloat) -> CGSize {
34 | return CGSize(width: lhs.width * rhs, height: lhs.height * rhs)
35 | }
36 |
37 | static func *(lhs: CGFloat, rhs: CGSize) -> CGSize {
38 | return CGSize(width: rhs.width * lhs, height: rhs.height * lhs)
39 | }
40 |
41 | static func /(lhs: CGSize, rhs: CGSize) -> CGSize {
42 | return CGSize(width: lhs.width / rhs.width, height: lhs.height / rhs.height)
43 | }
44 |
45 | static func /(lhs: CGSize, rhs: CGFloat) -> CGSize {
46 | return CGSize(width: lhs.width / rhs, height: lhs.height / rhs)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/Extensions/Number+Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Number+Helpers.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 11/10/2022.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Int {
11 | func interpolate(to: Int, progress: CGFloat) -> Int {
12 | guard progress >= 0.0 && progress <= 1.0 else { return self }
13 | return self + Int(CGFloat(to - self) * progress)
14 | }
15 |
16 | var int32: UInt32 {
17 | return UInt32(self)
18 | }
19 |
20 | var float: Float {
21 | return Float(self)
22 | }
23 |
24 | var double: Double {
25 | return Double(self)
26 | }
27 |
28 | var cgFloat: CGFloat {
29 | return CGFloat(self)
30 | }
31 |
32 | var string: String {
33 | return String(describing: self)
34 | }
35 |
36 | static func extract(from string: String) -> Int? {
37 | return Int(string.components(separatedBy: CharacterSet.decimalDigits.inverted).joined())
38 | }
39 | }
40 |
41 | extension Float {
42 |
43 | var int: Int {
44 | return Int(self)
45 | }
46 |
47 | var int32: UInt32 {
48 | return UInt32(self)
49 | }
50 |
51 | var double: Double {
52 | return Double(self)
53 | }
54 |
55 | var cgFloat: CGFloat {
56 | return CGFloat(self)
57 | }
58 |
59 | var string: String {
60 | return String(describing: self)
61 | }
62 | }
63 |
64 | extension Double {
65 | var float: Float {
66 | return Float(self)
67 | }
68 |
69 | var int: Int {
70 | return Int(self)
71 | }
72 |
73 | var int32: UInt32 {
74 | return UInt32(self)
75 | }
76 |
77 | var cgFloat: CGFloat {
78 | return CGFloat(self)
79 | }
80 |
81 | var string: String {
82 | return String(describing: self)
83 | }
84 | }
85 |
86 | let π = CGFloat.pi
87 |
88 | extension CGFloat {
89 | func interpolate(to: CGFloat, progress: CGFloat) -> CGFloat {
90 | guard progress >= 0.0 && progress <= 1.0 else { return self }
91 | return self + (to - self) * progress
92 | }
93 |
94 | var int: Int {
95 | return Int(self)
96 | }
97 |
98 | var int32: UInt32 {
99 | return UInt32(self)
100 | }
101 |
102 | var double: Double {
103 | return Double(self)
104 | }
105 |
106 | var float: Float {
107 | return Float(self)
108 | }
109 |
110 | var string: String {
111 | return String(describing: self)
112 | }
113 | // Converts an angle in degrees to radians.
114 | func degreesToRadians() -> CGFloat {
115 |
116 | return π * self / 180.0
117 | }
118 |
119 | // Converts an angle in radians to degrees.
120 | func radiansToDegrees() -> CGFloat {
121 | return self * 180.0 / π
122 | }
123 |
124 | // Ensures that the float value stays between the given values, inclusive.
125 | func clamped(_ v1: CGFloat, _ v2: CGFloat) -> CGFloat {
126 | let min = v1 < v2 ? v1 : v2
127 | let max = v1 > v2 ? v1 : v2
128 | return self < min ? min : (self > max ? max : self)
129 | }
130 |
131 | // Ensures that the float value stays between the given values, inclusive.
132 | mutating func clamp(_ v1: CGFloat, _ v2: CGFloat) -> CGFloat {
133 | self = clamped(v1, v2)
134 | return self
135 | }
136 |
137 | // Returns 1.0 if a floating point value is positive; -1.0 if it is negative.
138 | func sign() -> CGFloat {
139 | return (self >= 0.0) ? 1.0 : -1.0
140 | }
141 |
142 | func rounded(symbolsAfterComma count: Int) -> CGFloat {
143 | let multiplier = pow(10, count.double).cgFloat
144 | let result = (self * multiplier).rounded() / multiplier
145 | return result
146 | }
147 | }
148 |
149 | extension UInt32 {
150 | var float: Float {
151 | return Float(self)
152 | }
153 |
154 | var int: Int {
155 | return Int(self)
156 | }
157 |
158 | var cgFloat: CGFloat {
159 | return CGFloat(self)
160 | }
161 |
162 | var string: String {
163 | return String(describing: self)
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/Extensions/UIColor + Utils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor + Hex.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 10/10/2022.
6 | //
7 |
8 | import UIKit
9 | import simd
10 |
11 | extension UIColor {
12 |
13 | public convenience init?(hex: String) {
14 | var hexInt: UInt64 = 0
15 | let scanner = Scanner(string: hex)
16 | scanner.charactersToBeSkipped = CharacterSet(charactersIn: "#")
17 | scanner.scanHexInt64(&hexInt)
18 |
19 | let red = CGFloat((hexInt & 0xFF0000) >> 16) / 255.0
20 | let green = CGFloat((hexInt & 0xFF00) >> 8) / 255.0
21 | let blue = CGFloat((hexInt & 0xFF) >> 0) / 255.0
22 |
23 | self.init(red: red, green: green, blue: blue, alpha: 1.0)
24 | }
25 |
26 | static var random: UIColor {
27 | return UIColor(red: .random(in: 0...1),
28 | green: .random(in: 0...1),
29 | blue: .random(in: 0...1),
30 | alpha: 1.0)
31 | }
32 |
33 | var hexString: String {
34 | let (r, g, b, _) = rgbaCGFloat
35 |
36 | let rgb:Int = (Int)(r*255)<<16 | (Int)(g*255)<<8 | (Int)(b*255)<<0
37 |
38 | return String(format:"#%06x", rgb)
39 | }
40 |
41 | typealias RGBA = (r: T, g: T, b: T, a: T)
42 |
43 | var rgbaCGFloat: RGBA {
44 | var r: CGFloat = .zero
45 | var g: CGFloat = .zero
46 | var b: CGFloat = .zero
47 | var a: CGFloat = .zero
48 | getRed(&r, green: &g, blue: &b, alpha: &a)
49 | return (r, g, b, a)
50 | }
51 |
52 | var rgbaDouble: RGBA {
53 | let (r,g,b,a) = rgbaCGFloat
54 | return (r.double, g.double, b.double, a.double)
55 | }
56 |
57 | var vectorFloat4: vector_float4 {
58 | let (r,g,b,a) = rgbaDouble
59 | return vector_float4(r.float, g.float, b.float, a.float)
60 | }
61 |
62 | var hsba: (h: CGFloat, s: CGFloat, b: CGFloat, a: CGFloat) {
63 | var h: CGFloat = .zero
64 | var s: CGFloat = .zero
65 | var b: CGFloat = .zero
66 | var a: CGFloat = .zero
67 | getHue(&h, saturation: &s, brightness: &b, alpha: &a)
68 | return (h, s, b, a)
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/Metal/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_01_Final/Draw App/Metal/.DS_Store
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/Metal/DrawingGestureRecognizer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DrawingGestureRecognizer.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 25/10/2022.
6 | //
7 |
8 | import UIKit
9 |
10 | class DrawingGestureRecognizer: UIPanGestureRecognizer {
11 |
12 | typealias touchesResponse = (touches: Set, event: UIEvent, velocity: CGPoint)
13 |
14 | var touchesBeganHandler: ((touchesResponse) -> Void)?
15 | var touchesMovedHandler: ((touchesResponse) -> Void)?
16 | var touchesEndedHandler: ((touchesResponse) -> Void)?
17 | var touchesCancelledHandler: ((touchesResponse) -> Void)?
18 |
19 | override func touchesBegan(_ touches: Set, with event: UIEvent) {
20 | let response: touchesResponse = (touches, event, velocity(in: view))
21 | touchesBeganHandler?(response)
22 | super.touchesBegan(touches, with: event)
23 | }
24 |
25 | override func touchesMoved(_ touches: Set, with event: UIEvent) {
26 | let response: touchesResponse = (touches, event, velocity(in: view))
27 | touchesMovedHandler?(response)
28 | super.touchesMoved(touches, with: event)
29 | }
30 |
31 | override func touchesEnded(_ touches: Set, with event: UIEvent) {
32 | let response: touchesResponse = (touches, event, velocity(in: view))
33 | touchesEndedHandler?(response)
34 | super.touchesEnded(touches, with: event)
35 | }
36 |
37 | override func touchesCancelled(_ touches: Set, with event: UIEvent) {
38 | let response: touchesResponse = (touches, event, velocity(in: view))
39 | touchesCancelledHandler?(response)
40 | super.touchesCancelled(touches, with: event)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/Metal/DrawingView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DrawingView.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 29/04/2023.
6 | //
7 |
8 | import MetalKit
9 |
10 | class DrawingView: MTKView {
11 | private var previousPreviousLocation: CGPoint?
12 | private var previousLocation: CGPoint?
13 |
14 | var brushSize = CGFloat(20)
15 |
16 | var points: [Point] = []
17 |
18 | override var frame: CGRect {
19 | didSet {
20 | delegate?.mtkView(self, drawableSizeWillChange: frame.size)
21 | }
22 | }
23 |
24 | override init(frame frameRect: CGRect, device: MTLDevice?) {
25 | super.init(frame: frameRect, device: device)
26 | setupGestureRecognizer()
27 | }
28 |
29 | @available(*, unavailable) required init(coder: NSCoder) {
30 | fatalError("init(coder:) has not been implemented")
31 | }
32 |
33 | private func setupGestureRecognizer() {
34 | let panGesture = DrawingGestureRecognizer()
35 | panGesture.maximumNumberOfTouches = 1
36 | panGesture.touchesBeganHandler = { [weak self] response in
37 | guard let self else { return }
38 | for touch in response.touches {
39 | let pos = touch.location(in: self) * self.contentScaleFactor
40 | self.draw(at: pos)
41 | let predicted = response.event.coalescedTouches(for: touch)
42 | for p in (predicted ?? []) {
43 | let pos = p.location(in: self) * self.contentScaleFactor
44 | self.draw(at: pos)
45 | }
46 | }
47 | }
48 |
49 | panGesture.touchesMovedHandler = { [weak self] response in
50 | guard let self else { return }
51 | for touch in response.touches {
52 | let pos = touch.location(in: self) * self.contentScaleFactor
53 | self.draw(at: pos)
54 | }
55 | }
56 |
57 | let gestureFinishedHandler: ((DrawingGestureRecognizer.touchesResponse) -> Void)? = { [weak self] _ in
58 | guard let self else { return }
59 | self.previousLocation = nil
60 | self.previousPreviousLocation = nil
61 | }
62 |
63 | panGesture.touchesEndedHandler = gestureFinishedHandler
64 | panGesture.touchesCancelledHandler = gestureFinishedHandler
65 |
66 | addGestureRecognizer(panGesture)
67 | }
68 |
69 | private func draw(at point: CGPoint) {
70 | let previousPreviousLocation = self.previousPreviousLocation ?? point
71 | let previousLocation = self.previousLocation ?? point
72 | self.previousPreviousLocation = self.previousLocation
73 | self.previousLocation = point
74 |
75 | let mid1 = (previousLocation + previousPreviousLocation) * 0.5
76 | let mid2 = (point + previousLocation) * 0.5
77 |
78 | let pl = SIMD2(Float(previousLocation.x), Float(previousLocation.y))
79 | let cl = SIMD2(Float(point.x), Float(point.y))
80 | let d = distance(pl, cl)
81 |
82 | for i in 0 ... Int(d) {
83 | let p = d <= 0 ? point : quadBezierPoint(t: CGFloat(i) / CGFloat(d), start: mid1, c1: previousLocation, end: mid2)
84 | let point = Point(location: p, parentSize: self.bounds.size * contentScaleFactor, color: .blue, size: brushSize * contentScaleFactor)
85 | points.append(point)
86 | }
87 | }
88 |
89 | private func quadBezierPoint(t: CGFloat, start: CGPoint, c1: CGPoint, end: CGPoint) -> CGPoint {
90 | let x = quadBezier(t: t, start: start.x, c1: c1.x, end: end.x)
91 | let y = quadBezier(t: t, start: start.y, c1: c1.y, end: end.y)
92 | return CGPoint(x: x, y: y)
93 | }
94 |
95 | private func quadBezier(t: CGFloat, start: CGFloat, c1: CGFloat, end: CGFloat) -> CGFloat {
96 | let t_ = (1.0 - t)
97 | let tt_ = t_ * t_
98 | let tt = t * t
99 | return start * tt_ + 2.0 * c1 * t_ * t + end * tt
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/Metal/Point.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Point.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 07/05/2023.
6 | //
7 |
8 | import Foundation
9 | import simd
10 | import UIKit
11 |
12 | struct Point {
13 | var position: vector_float4
14 | var color: vector_float4
15 | var size: Float
16 |
17 | init(position: vector_float4, color: vector_float4, size: Float) {
18 | self.position = position
19 | self.color = color
20 | self.size = size
21 | }
22 |
23 | init(x: CGFloat, y: CGFloat, color: UIColor, size: CGFloat) {
24 | self.init(position: vector_float4(Float(x), Float(y), 0, 1), color: color.vectorFloat4, size: Float(size))
25 | }
26 |
27 | init(location: CGPoint, parentSize: CGSize, color: UIColor, size: CGFloat) {
28 | let roundedLocation = CGPoint(x: location.x.int.cgFloat, y: location.y.int.cgFloat)
29 | let xK = (1.0 / (parentSize.width / roundedLocation.x)).rounded(symbolsAfterComma: 5)
30 | let yK = (1.0 / (parentSize.height / roundedLocation.y)).rounded(symbolsAfterComma: 5)
31 | let x = xK == 0.5 ? 0.0 : (xK < 0.5 ? -(1 - xK / 0.5) : xK / 0.5 - 1)
32 | let y = yK == 0.5 ? 0.0 : (yK > 0.5 ? (1 - yK / 0.5) : 1 - (yK / 0.5))
33 | self.init(x: x, y: y, color: color, size: size)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/Metal/Shader.metal:
--------------------------------------------------------------------------------
1 | //
2 | // Shader.metal
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 06/05/2023.
6 | //
7 |
8 | #include
9 | using namespace metal;
10 |
11 | struct PointVertex {
12 | float4 position [[position]];
13 | float4 color;
14 | float size [[point_size]];
15 | };
16 |
17 | vertex PointVertex pointShaderVertex(constant PointVertex *points [[ buffer(0) ]],
18 | uint vid [[ vertex_id ]])
19 | {
20 | PointVertex out = points[vid];
21 |
22 | float2 pos = float2(out.position.x, out.position.y);
23 | out.position = float4(pos, 0, 1);
24 | return out;
25 | };
26 |
27 | fragment half4 pointShaderFragment(PointVertex point_data [[ stage_in ]],
28 | float2 pointCoord [[ point_coord ]])
29 | {
30 | float dist = length(pointCoord - float2(0.5));
31 | float4 out_color = point_data.color;
32 | out_color.a = 1.0 - smoothstep(0.4, 0.5, dist);
33 | return half4(out_color);
34 | };
35 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 29/04/2023.
6 | //
7 |
8 | import UIKit
9 |
10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
11 |
12 | var window: UIWindow?
13 |
14 |
15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
16 | guard let windowScene = (scene as? UIWindowScene) else { return }
17 |
18 | let window = UIWindow(windowScene: windowScene)
19 | window.rootViewController = ViewController()
20 | self.window = window
21 | window.makeKeyAndVisible()
22 | }
23 |
24 | func sceneDidDisconnect(_ scene: UIScene) {
25 | // Called as the scene is being released by the system.
26 | // This occurs shortly after the scene enters the background, or when its session is discarded.
27 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
28 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
29 | }
30 |
31 | func sceneDidBecomeActive(_ scene: UIScene) {
32 | // Called when the scene has moved from an inactive state to an active state.
33 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
34 | }
35 |
36 | func sceneWillResignActive(_ scene: UIScene) {
37 | // Called when the scene will move from an active state to an inactive state.
38 | // This may occur due to temporary interruptions (ex. an incoming phone call).
39 | }
40 |
41 | func sceneWillEnterForeground(_ scene: UIScene) {
42 | // Called as the scene transitions from the background to the foreground.
43 | // Use this method to undo the changes made on entering the background.
44 | }
45 |
46 | func sceneDidEnterBackground(_ scene: UIScene) {
47 | // Called as the scene transitions from the foreground to the background.
48 | // Use this method to save data, release shared resources, and store enough scene-specific state information
49 | // to restore the scene back to its current state.
50 | }
51 |
52 |
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/Supporting Files/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_01_Final/Draw App/Supporting Files/.DS_Store
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/Supporting Files/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/Supporting Files/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/Supporting Files/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/Supporting Files/DrawingApp-BridgingHeader.h:
--------------------------------------------------------------------------------
1 | //
2 | // DrawingApp-BridgingHeader.h
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 29/04/2023.
6 | //
7 |
8 | #ifndef DrawingApp_BridgingHeader_h
9 | #define DrawingApp_BridgingHeader_h
10 |
11 | #endif /* DrawingApp_BridgingHeader_h */
12 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/Supporting Files/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIApplicationSceneManifest
6 |
7 | UIApplicationSupportsMultipleScenes
8 |
9 | UISceneConfigurations
10 |
11 | UIWindowSceneSessionRoleApplication
12 |
13 |
14 | UISceneConfigurationName
15 | Default Configuration
16 | UISceneDelegateClassName
17 | $(PRODUCT_MODULE_NAME).SceneDelegate
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Draw_App_01_Final/Draw App/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 29/04/2023.
6 | //
7 |
8 | import MetalKit
9 | import UIKit
10 |
11 | class ViewController: UIViewController {
12 |
13 | private var drawingView: DrawingView? {
14 | return self.view as? DrawingView
15 | }
16 |
17 | private var renderer: Renderer?
18 |
19 | override func loadView() {
20 | super.loadView()
21 | self.view = DrawingView(frame: UIScreen.main.bounds)
22 | }
23 |
24 | override func viewDidLoad() {
25 | super.viewDidLoad()
26 | guard let drawingView else { return }
27 | renderer = Renderer(metalKitView: drawingView)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Draw_App_01_Starter/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_01_Starter/.DS_Store
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_01_Starter/Draw App/.DS_Store
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App/Drawing/DrawingGestureRecognizer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DrawingGestureRecognizer.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 25/10/2022.
6 | //
7 |
8 | import UIKit
9 |
10 | class DrawingGestureRecognizer: UIPanGestureRecognizer {
11 |
12 | typealias touchesResponse = (touches: Set, event: UIEvent, velocity: CGPoint)
13 |
14 | var touchesBeganHandler: ((touchesResponse) -> Void)?
15 | var touchesMovedHandler: ((touchesResponse) -> Void)?
16 | var touchesEndedHandler: ((touchesResponse) -> Void)?
17 | var touchesCancelledHandler: ((touchesResponse) -> Void)?
18 |
19 | override func touchesBegan(_ touches: Set, with event: UIEvent) {
20 | let response: touchesResponse = (touches, event, velocity(in: view))
21 | touchesBeganHandler?(response)
22 | super.touchesBegan(touches, with: event)
23 | }
24 |
25 | override func touchesMoved(_ touches: Set, with event: UIEvent) {
26 | let response: touchesResponse = (touches, event, velocity(in: view))
27 | touchesMovedHandler?(response)
28 | super.touchesMoved(touches, with: event)
29 | }
30 |
31 | override func touchesEnded(_ touches: Set, with event: UIEvent) {
32 | let response: touchesResponse = (touches, event, velocity(in: view))
33 | touchesEndedHandler?(response)
34 | super.touchesEnded(touches, with: event)
35 | }
36 |
37 | override func touchesCancelled(_ touches: Set, with event: UIEvent) {
38 | let response: touchesResponse = (touches, event, velocity(in: view))
39 | touchesCancelledHandler?(response)
40 | super.touchesCancelled(touches, with: event)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App/Drawing/Shader.metal:
--------------------------------------------------------------------------------
1 | //
2 | // Shader.metal
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 06/05/2023.
6 | //
7 |
8 | #include
9 | using namespace metal;
10 |
11 | struct PointVertex {
12 | float4 position [[position]];
13 | float4 color;
14 | float size [[point_size]];
15 | };
16 |
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App/Extensions/CGPoint + Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGPoint + Arithmetic.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 11/10/2022.
6 | //
7 |
8 | import Foundation
9 | import simd
10 |
11 | extension CGPoint {
12 | func interpolate(to: CGPoint, progress: CGFloat) -> CGPoint {
13 | guard progress >= 0.0 && progress <= 1.0 else { return self }
14 | let x = self.x.interpolate(to: to.x, progress: progress)
15 | let y = self.y.interpolate(to: to.y, progress: progress)
16 | return CGPoint(x: x, y: y)
17 | }
18 |
19 | init(vector: vector_uint2) {
20 | self.init(x: CGFloat(vector.x), y: CGFloat(vector.y))
21 | }
22 |
23 | init(vector: vector_float2) {
24 | self.init(x: vector.x.cgFloat, y: vector.y.cgFloat)
25 | }
26 |
27 | var vectorFloat: vector_float2 {
28 | return vector_float2(x: x.float, y: y.float)
29 | }
30 |
31 | var vectorUint: vector_uint2 {
32 | return vector_uint2(x: x.int32, y: y.int32)
33 | }
34 |
35 | // Arithmetic
36 |
37 | static func + (lhs: CGPoint, rhs: CGPoint) -> CGPoint {
38 | return CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
39 | }
40 |
41 | static func -(lhs: CGPoint, rhs: CGPoint) -> CGVector {
42 | return CGVector(dx: lhs.x - rhs.x, dy: lhs.y - rhs.y)
43 | }
44 |
45 | static func -(lhs: CGPoint, rhs: CGVector) -> CGPoint {
46 | return CGPoint(x: lhs.x - rhs.dx, y: lhs.y - rhs.dy)
47 | }
48 |
49 | static func *(lhs: CGPoint, rhs: CGFloat) -> CGPoint {
50 | return CGPoint(x: lhs.x * rhs, y: lhs.y * rhs)
51 | }
52 |
53 | static func *(lhs: CGFloat, rhs: CGPoint) -> CGPoint {
54 | return CGPoint(x: rhs.x * lhs, y: rhs.y * lhs)
55 | }
56 |
57 | func distanceToPoint(otherPoint: CGPoint) -> CGFloat {
58 | return sqrt(pow((otherPoint.x - x), 2) + pow((otherPoint.y - y), 2))
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App/Extensions/CGRect + Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGRect + Helpers.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 11/10/2022.
6 | //
7 |
8 | import Foundation
9 |
10 | extension CGRect {
11 |
12 | public init(size: CGSize) {
13 | self.init(origin: .zero, size: size)
14 | }
15 |
16 | func scale(_ factor: CGFloat) -> CGRect {
17 | let transform = CGAffineTransform(scaleX: factor, y: factor)
18 | return self.applying(transform)
19 | }
20 |
21 | func interpolate(to: CGRect, progress: CGFloat) -> CGRect {
22 | guard progress >= 0.0, progress <= 1.0 else { return self }
23 | let origin = origin.interpolate(to: to.origin, progress: progress)
24 | let size = size.interpolate(to: to.size, progress: progress)
25 | return CGRect(origin: origin, size: size)
26 | }
27 |
28 | var center: CGPoint {
29 | return CGPoint(x: midX, y: midY)
30 | }
31 |
32 | init(center: CGPoint, size: CGSize) {
33 | self.init(origin: CGPoint(x: center.x - size.width / 2, y: center.y - size.height / 2), size: size)
34 | }
35 |
36 | func withOrigin(_ newOrigin: CGPoint) -> CGRect {
37 | var newRect = self
38 | newRect.origin = newOrigin
39 | return newRect
40 | }
41 |
42 | func withX(_ newX: CGFloat) -> CGRect {
43 | return withOrigin(CGPoint(x: newX, y: minY))
44 | }
45 |
46 | func withY(_ newY: CGFloat) -> CGRect {
47 | return withOrigin(CGPoint(x: minX, y: newY))
48 | }
49 |
50 | func withSize(_ newValue: CGSize) -> CGRect {
51 | var newRect = self
52 | newRect.size = newValue
53 | return newRect
54 | }
55 |
56 | func withWidth(_ newWidth: CGFloat) -> CGRect {
57 | return withSize(CGSize(width: newWidth, height: height))
58 | }
59 |
60 |
61 | func withHeight(_ newHeight: CGFloat) -> CGRect {
62 | return withSize(CGSize(width: width, height: newHeight))
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App/Extensions/CGSize + Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGSize + Helpers.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 11/10/2022.
6 | //
7 |
8 | import Foundation
9 |
10 | extension CGSize {
11 |
12 | init(size: CGFloat) {
13 | self.init(width: size, height: size)
14 | }
15 |
16 | func interpolate(to: CGSize, progress: CGFloat) -> CGSize {
17 | guard progress >= 0.0 && progress <= 1.0 else { return self }
18 |
19 | let width = self.width.interpolate(to: to.width, progress: progress)
20 | let height = self.height.interpolate(to: to.height, progress: progress)
21 | return CGSize(width: width, height: height)
22 | }
23 |
24 | func scale(_ factor: CGFloat) -> CGSize {
25 | let transform = CGAffineTransform(scaleX: factor, y: factor)
26 | return self.applying(transform)
27 | }
28 |
29 | static func *(lhs: CGSize, rhs: CGSize) -> CGSize {
30 | return CGSize(width: lhs.width * rhs.width, height: lhs.height * rhs.height)
31 | }
32 |
33 | static func *(lhs: CGSize, rhs: CGFloat) -> CGSize {
34 | return CGSize(width: lhs.width * rhs, height: lhs.height * rhs)
35 | }
36 |
37 | static func *(lhs: CGFloat, rhs: CGSize) -> CGSize {
38 | return CGSize(width: rhs.width * lhs, height: rhs.height * lhs)
39 | }
40 |
41 | static func /(lhs: CGSize, rhs: CGSize) -> CGSize {
42 | return CGSize(width: lhs.width / rhs.width, height: lhs.height / rhs.height)
43 | }
44 |
45 | static func /(lhs: CGSize, rhs: CGFloat) -> CGSize {
46 | return CGSize(width: lhs.width / rhs, height: lhs.height / rhs)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App/Extensions/Number+Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Number+Helpers.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 11/10/2022.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Int {
11 | func interpolate(to: Int, progress: CGFloat) -> Int {
12 | guard progress >= 0.0 && progress <= 1.0 else { return self }
13 | return self + Int(CGFloat(to - self) * progress)
14 | }
15 |
16 | var int32: UInt32 {
17 | return UInt32(self)
18 | }
19 |
20 | var float: Float {
21 | return Float(self)
22 | }
23 |
24 | var double: Double {
25 | return Double(self)
26 | }
27 |
28 | var cgFloat: CGFloat {
29 | return CGFloat(self)
30 | }
31 |
32 | var string: String {
33 | return String(describing: self)
34 | }
35 |
36 | static func extract(from string: String) -> Int? {
37 | return Int(string.components(separatedBy: CharacterSet.decimalDigits.inverted).joined())
38 | }
39 | }
40 |
41 | extension Float {
42 |
43 | var int: Int {
44 | return Int(self)
45 | }
46 |
47 | var int32: UInt32 {
48 | return UInt32(self)
49 | }
50 |
51 | var double: Double {
52 | return Double(self)
53 | }
54 |
55 | var cgFloat: CGFloat {
56 | return CGFloat(self)
57 | }
58 |
59 | var string: String {
60 | return String(describing: self)
61 | }
62 | }
63 |
64 | extension Double {
65 | var float: Float {
66 | return Float(self)
67 | }
68 |
69 | var int: Int {
70 | return Int(self)
71 | }
72 |
73 | var int32: UInt32 {
74 | return UInt32(self)
75 | }
76 |
77 | var cgFloat: CGFloat {
78 | return CGFloat(self)
79 | }
80 |
81 | var string: String {
82 | return String(describing: self)
83 | }
84 | }
85 |
86 | let π = CGFloat.pi
87 |
88 | extension CGFloat {
89 | func interpolate(to: CGFloat, progress: CGFloat) -> CGFloat {
90 | guard progress >= 0.0 && progress <= 1.0 else { return self }
91 | return self + (to - self) * progress
92 | }
93 |
94 | var int: Int {
95 | return Int(self)
96 | }
97 |
98 | var int32: UInt32 {
99 | return UInt32(self)
100 | }
101 |
102 | var double: Double {
103 | return Double(self)
104 | }
105 |
106 | var float: Float {
107 | return Float(self)
108 | }
109 |
110 | var string: String {
111 | return String(describing: self)
112 | }
113 | // Converts an angle in degrees to radians.
114 | func degreesToRadians() -> CGFloat {
115 |
116 | return π * self / 180.0
117 | }
118 |
119 | // Converts an angle in radians to degrees.
120 | func radiansToDegrees() -> CGFloat {
121 | return self * 180.0 / π
122 | }
123 |
124 | // Ensures that the float value stays between the given values, inclusive.
125 | func clamped(_ v1: CGFloat, _ v2: CGFloat) -> CGFloat {
126 | let min = v1 < v2 ? v1 : v2
127 | let max = v1 > v2 ? v1 : v2
128 | return self < min ? min : (self > max ? max : self)
129 | }
130 |
131 | // Ensures that the float value stays between the given values, inclusive.
132 | mutating func clamp(_ v1: CGFloat, _ v2: CGFloat) -> CGFloat {
133 | self = clamped(v1, v2)
134 | return self
135 | }
136 |
137 | // Returns 1.0 if a floating point value is positive; -1.0 if it is negative.
138 | func sign() -> CGFloat {
139 | return (self >= 0.0) ? 1.0 : -1.0
140 | }
141 |
142 | func rounded(symbolsAfterComma count: Int) -> CGFloat {
143 | let multiplier = pow(10, count.double).cgFloat
144 | let result = (self * multiplier).rounded() / multiplier
145 | return result
146 | }
147 | }
148 |
149 | extension UInt32 {
150 | var float: Float {
151 | return Float(self)
152 | }
153 |
154 | var int: Int {
155 | return Int(self)
156 | }
157 |
158 | var cgFloat: CGFloat {
159 | return CGFloat(self)
160 | }
161 |
162 | var string: String {
163 | return String(describing: self)
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App/Extensions/UIColor + Utils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor + Hex.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 10/10/2022.
6 | //
7 |
8 | import UIKit
9 | import simd
10 |
11 | extension UIColor {
12 |
13 | public convenience init?(hex: String) {
14 | var hexInt: UInt64 = 0
15 | let scanner = Scanner(string: hex)
16 | scanner.charactersToBeSkipped = CharacterSet(charactersIn: "#")
17 | scanner.scanHexInt64(&hexInt)
18 |
19 | let red = CGFloat((hexInt & 0xFF0000) >> 16) / 255.0
20 | let green = CGFloat((hexInt & 0xFF00) >> 8) / 255.0
21 | let blue = CGFloat((hexInt & 0xFF) >> 0) / 255.0
22 |
23 | self.init(red: red, green: green, blue: blue, alpha: 1.0)
24 | }
25 |
26 | static var random: UIColor {
27 | return UIColor(red: .random(in: 0...1),
28 | green: .random(in: 0...1),
29 | blue: .random(in: 0...1),
30 | alpha: 1.0)
31 | }
32 |
33 | var hexString: String {
34 | let (r, g, b, _) = rgbaCGFloat
35 |
36 | let rgb:Int = (Int)(r*255)<<16 | (Int)(g*255)<<8 | (Int)(b*255)<<0
37 |
38 | return String(format:"#%06x", rgb)
39 | }
40 |
41 | typealias RGBA = (r: T, g: T, b: T, a: T)
42 |
43 | var rgbaCGFloat: RGBA {
44 | var r: CGFloat = .zero
45 | var g: CGFloat = .zero
46 | var b: CGFloat = .zero
47 | var a: CGFloat = .zero
48 | getRed(&r, green: &g, blue: &b, alpha: &a)
49 | return (r, g, b, a)
50 | }
51 |
52 | var rgbaDouble: RGBA {
53 | let (r,g,b,a) = rgbaCGFloat
54 | return (r.double, g.double, b.double, a.double)
55 | }
56 |
57 | var vectorFloat4: vector_float4 {
58 | let (r,g,b,a) = rgbaDouble
59 | return vector_float4(r.float, g.float, b.float, a.float)
60 | }
61 |
62 | var hsba: (h: CGFloat, s: CGFloat, b: CGFloat, a: CGFloat) {
63 | var h: CGFloat = .zero
64 | var s: CGFloat = .zero
65 | var b: CGFloat = .zero
66 | var a: CGFloat = .zero
67 | getHue(&h, saturation: &s, brightness: &b, alpha: &a)
68 | return (h, s, b, a)
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App/Supporting Files/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_01_Starter/Draw App/Supporting Files/.DS_Store
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App/Supporting Files/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 29/04/2023.
6 | //
7 |
8 | import UIKit
9 |
10 | @main
11 | class AppDelegate: UIResponder, UIApplicationDelegate {
12 |
13 |
14 |
15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
16 | // Override point for customization after application launch.
17 | return true
18 | }
19 |
20 | // MARK: UISceneSession Lifecycle
21 |
22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
23 | // Called when a new scene session is being created.
24 | // Use this method to select a configuration to create the new scene with.
25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
26 | }
27 |
28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
29 | // Called when the user discards a scene session.
30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
32 | }
33 |
34 |
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App/Supporting Files/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App/Supporting Files/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App/Supporting Files/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App/Supporting Files/DrawingApp-BridgingHeader.h:
--------------------------------------------------------------------------------
1 | //
2 | // DrawingApp-BridgingHeader.h
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 29/04/2023.
6 | //
7 |
8 | #ifndef DrawingApp_BridgingHeader_h
9 | #define DrawingApp_BridgingHeader_h
10 |
11 | #endif /* DrawingApp_BridgingHeader_h */
12 |
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App/Supporting Files/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIApplicationSceneManifest
6 |
7 | UIApplicationSupportsMultipleScenes
8 |
9 | UISceneConfigurations
10 |
11 | UIWindowSceneSessionRoleApplication
12 |
13 |
14 | UISceneConfigurationName
15 | Default Configuration
16 | UISceneDelegateClassName
17 | $(PRODUCT_MODULE_NAME).SceneDelegate
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App/Supporting Files/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 29/04/2023.
6 | //
7 |
8 | import UIKit
9 |
10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
11 |
12 | var window: UIWindow?
13 |
14 |
15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
16 | guard let windowScene = (scene as? UIWindowScene) else { return }
17 |
18 | let window = UIWindow(windowScene: windowScene)
19 | window.rootViewController = ViewController()
20 | self.window = window
21 | window.makeKeyAndVisible()
22 | }
23 |
24 | func sceneDidDisconnect(_ scene: UIScene) {
25 | // Called as the scene is being released by the system.
26 | // This occurs shortly after the scene enters the background, or when its session is discarded.
27 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
28 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
29 | }
30 |
31 | func sceneDidBecomeActive(_ scene: UIScene) {
32 | // Called when the scene has moved from an inactive state to an active state.
33 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
34 | }
35 |
36 | func sceneWillResignActive(_ scene: UIScene) {
37 | // Called when the scene will move from an active state to an inactive state.
38 | // This may occur due to temporary interruptions (ex. an incoming phone call).
39 | }
40 |
41 | func sceneWillEnterForeground(_ scene: UIScene) {
42 | // Called as the scene transitions from the background to the foreground.
43 | // Use this method to undo the changes made on entering the background.
44 | }
45 |
46 | func sceneDidEnterBackground(_ scene: UIScene) {
47 | // Called as the scene transitions from the foreground to the background.
48 | // Use this method to save data, release shared resources, and store enough scene-specific state information
49 | // to restore the scene back to its current state.
50 | }
51 |
52 |
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/Draw_App_01_Starter/Draw App/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 29/04/2023.
6 | //
7 |
8 | import MetalKit
9 | import UIKit
10 |
11 | class ViewController: UIViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/.DS_Store
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/.DS_Store
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 29/04/2023.
6 | //
7 |
8 | import UIKit
9 |
10 | @main
11 | class AppDelegate: UIResponder, UIApplicationDelegate {
12 |
13 |
14 |
15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
16 | // Override point for customization after application launch.
17 | return true
18 | }
19 |
20 | // MARK: UISceneSession Lifecycle
21 |
22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
23 | // Called when a new scene session is being created.
24 | // Use this method to select a configuration to create the new scene with.
25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
26 | }
27 |
28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
29 | // Called when the user discards a scene session.
30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
32 | }
33 |
34 |
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/BottomView/Color Picker/ColorPaletteCollectionViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ColorPaletteCollectionViewCell.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 20/10/2022.
6 | //
7 |
8 | import UIKit
9 |
10 | class ColorPaletteCollectionViewCell: UICollectionViewCell {
11 |
12 | var item: CollorPickerBottomSheetController.Item?
13 | var imageView: UIImageView?
14 |
15 | override init(frame: CGRect) {
16 | super.init(frame: frame)
17 | setupViews()
18 | }
19 |
20 | required init?(coder: NSCoder) {
21 | super.init(coder: coder)
22 | setupViews()
23 | }
24 |
25 | func setup(with item: CollorPickerBottomSheetController.Item) {
26 | self.item = item
27 | switch item {
28 | case .add:
29 | imageView?.isHidden = false
30 | contentView.backgroundColor = .clear
31 | case .paletteItem(let item):
32 | contentView.backgroundColor = item.color.uiColor
33 | imageView?.isHidden = true
34 | }
35 |
36 | if isSelected {
37 | contentView.layer.borderWidth = 3
38 | contentView.layer.borderColor = UIColor.white.cgColor
39 | } else {
40 | contentView.layer.borderColor = UIColor.clear.cgColor
41 | contentView.layer.borderWidth = 0
42 | }
43 | }
44 |
45 | private func setupViews() {
46 | contentView.layer.cornerRadius = 15
47 | contentView.layer.masksToBounds = true
48 | imageView = UIImageView(frame: bounds)
49 | imageView?.image = UIImage.generateAddIconColorPicker()
50 | contentView.addSubviewPinnedToEdges(imageView!)
51 | }
52 |
53 | override func prepareForReuse() {
54 | super.prepareForReuse()
55 | self.imageView?.isHidden = true
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/BottomView/Color Picker/ColorPickerSlider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ColorPickerSlider.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 16/10/2022.
6 | //
7 |
8 | import UIKit
9 |
10 | class ColorPickerSlider: UISlider {
11 |
12 | private var currentColor: Color?
13 |
14 | override init(frame: CGRect) {
15 | super.init(frame: frame)
16 | initialSetup()
17 | }
18 |
19 | required init?(coder: NSCoder) {
20 | super.init(coder: coder)
21 | initialSetup()
22 | }
23 |
24 | private func initialSetup() {
25 | if let trackImage = UIImage.generateOpacitySliderTrackImage(color: .black) {
26 | self.setMaximumTrackImage(trackImage, for: .normal)
27 | self.setMinimumTrackImage(trackImage, for: .normal)
28 | }
29 | setThumbImage(UIImage(named: "opacity_slider_thumb"), for: .normal)
30 | }
31 |
32 | override func trackRect(forBounds bounds: CGRect) -> CGRect {
33 | var result = super.trackRect(forBounds: bounds)
34 | result.size.height = 36
35 | result.origin = .zero
36 | return result
37 | }
38 |
39 | func updateSelectedColor(color: Color) {
40 | if value.cgFloat != color.alpha {
41 | value = color.alpha.float
42 | }
43 | if let currentColor, currentColor.hex == color.hex {
44 | return
45 | }
46 | currentColor = color
47 | if let trackImage = UIImage.generateOpacitySliderTrackImage(color: color.uiColor) {
48 | self.setMaximumTrackImage(trackImage, for: .normal)
49 | self.setMinimumTrackImage(trackImage, for: .normal)
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/BottomView/EditorBottomSegementedControl.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EditorBottomSegementedControl.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 16/10/2022.
6 | //
7 |
8 | import UIKit
9 |
10 | class EditorBottomSegementedControl: UISegmentedControl {
11 |
12 | lazy var backgroundBlur: UIVisualEffectView = {
13 | let effect = UIBlurEffect(style: .dark)
14 | let black = UIVisualEffectView(effect: effect)
15 | return black
16 | }()
17 |
18 | override init(items: [Any]?) {
19 | super.init(items: items)
20 | commonInit()
21 | }
22 |
23 | override init(frame: CGRect) {
24 | super.init(frame: frame)
25 | commonInit()
26 | }
27 |
28 | required init?(coder: NSCoder) {
29 | super.init(coder: coder)
30 | commonInit()
31 | }
32 |
33 | private func commonInit() {
34 | layer.masksToBounds = true
35 | clipsToBounds = true
36 | selectedSegmentTintColor = UIColor(hex: "5D5D5D")
37 | tintColor = UIColor(hex: "5D5D5D")
38 | setTitleTextAttributes([.foregroundColor: UIColor.white], for: .normal)
39 | insertSubview(backgroundBlur, at: .zero)
40 | }
41 |
42 | override func layoutSubviews() {
43 | super.layoutSubviews()
44 | layer.masksToBounds = true
45 | layer.cornerRadius = bounds.height / 2
46 | backgroundBlur.frame = bounds
47 | sendSubviewToBack(backgroundBlur)
48 | }
49 |
50 | override func addSubview(_ view: UIView) {
51 | let newBounds = view.layer.bounds.inset(by: UIEdgeInsets(top: 6, left: 7, bottom: 0, right: 0))
52 |
53 | let path = UIBezierPath(roundedRect: newBounds, cornerRadius: newBounds.height / 2)
54 | let mask = CAShapeLayer()
55 | mask.path = path.cgPath
56 | view.layer.mask = mask
57 |
58 | super.addSubview(view)
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/BottomView/EditorBottomSizeEditor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EditorBottomSizeEditor.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 29/10/2022.
6 | //
7 |
8 | import UIKit
9 |
10 | class EditorBottomSizeEditor: UIView {
11 | var brushImageView: UIImageView?
12 | var sizeSlider: SizeSlider?
13 | var brushTypeButton: UIButton?
14 | var backButton: UIButton?
15 |
16 | var selectedBrush: Brush
17 | let viewModel: EditorBottomViewModel
18 |
19 | init(selectedBrush: Brush, viewModel: EditorBottomViewModel, frame: CGRect) {
20 | self.selectedBrush = selectedBrush
21 | self.viewModel = viewModel
22 | super.init(frame: frame)
23 | initialSetup()
24 | }
25 |
26 | required init?(coder: NSCoder) {
27 | fatalError("init(coder:) has not been implemented")
28 | }
29 |
30 | private func initialSetup() {
31 | brushImageView = UIImageView(frame: CGRect(origin: CGPoint(x: (bounds.width - 40) / 2 , y: .zero), size: CGSize(width: 40, height: 120)))
32 | brushImageView?.contentMode = .scaleAspectFit
33 | brushImageView?.image = selectedBrush.icon()
34 | addSubview(brushImageView!)
35 |
36 | sizeSlider = SizeSlider(frame: CGRect(origin: CGPoint(x: 46.5 , y: brushImageView!.frame.maxY), size: CGSize(width: bounds.width-134, height: 28)))
37 | sizeSlider?.value = selectedBrush.width
38 | sizeSlider?.addTarget(self, action: #selector(sliderValueChanged), for: .valueChanged)
39 | addSubview(sizeSlider!)
40 |
41 | }
42 |
43 | @objc private func sliderValueChanged(_ slider: UISlider) {
44 | print("slider value changed: \(slider.value)")
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/BottomView/SizeSlider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SizeSlider.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 26/10/2022.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | class SizeSlider: UISlider {
12 |
13 | override init(frame: CGRect) {
14 | super.init(frame: frame)
15 | initialSetup()
16 | }
17 |
18 | required init?(coder: NSCoder) {
19 | super.init(coder: coder)
20 | initialSetup()
21 | }
22 |
23 | private func initialSetup() {
24 | if let trackImage = UIImage.generateSizeSlider(isRotated: true) {
25 | self.setMaximumTrackImage(trackImage, for: .normal)
26 | self.setMinimumTrackImage(trackImage, for: .normal)
27 | }
28 |
29 | if let thumbImage = UIImage.generateSizeSliderThumb(size: CGSize(width: 32, height: 32)) {
30 | self.setThumbImage(thumbImage, for: .normal)
31 | }
32 | value = 0.5
33 | }
34 |
35 | override func trackRect(forBounds bounds: CGRect) -> CGRect {
36 | var result = super.trackRect(forBounds: bounds)
37 | result.size.height = 24
38 | result.origin = .zero
39 | return result
40 | }
41 |
42 | func runTransitionAnimation(from segmentedControl: EditorBottomSegementedControl) {
43 | layer.sublayers?.forEach({ $0.isHidden = true })
44 | }
45 |
46 | override func touchesBegan(_ touches: Set, with event: UIEvent?) {
47 | super.touchesBegan(touches, with: event)
48 |
49 | var sliderTransforms = self.transform
50 | sliderTransforms = sliderTransforms.concatenating(CGAffineTransform(translationX: 20, y: .zero))
51 | UIView.animate(withDuration: 0.2, delay: .zero) {
52 | self.transform = sliderTransforms
53 | }
54 | }
55 |
56 | override func touchesEnded(_ touches: Set, with event: UIEvent?) {
57 | super.touchesEnded(touches, with: event)
58 | var sliderTransforms = self.transform
59 | sliderTransforms = sliderTransforms.concatenating(CGAffineTransform(translationX: -20, y: .zero))
60 | UIView.animate(withDuration: 0.2, delay: .zero) {
61 | self.transform = sliderTransforms
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/DrawingViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 29/04/2023.
6 | //
7 |
8 | import MetalKit
9 | import Combine
10 | import UIKit
11 |
12 | class DrawingViewController: UIViewController {
13 |
14 | private var drawingView: DrawingView? {
15 | return self.view as? DrawingView
16 | }
17 |
18 | private var editorViewModel = EditorBottomViewModel()
19 |
20 | private lazy var bottomView: EditorBottomView = {
21 | return EditorBottomView(viewModel: editorViewModel)
22 | }()
23 |
24 | private var renderer: Renderer?
25 | private var bindings = Set()
26 |
27 | override func loadView() {
28 | super.loadView()
29 | self.view = DrawingView(frame: UIScreen.main.bounds, brush: editorViewModel.selectedBrush, device: MTLCreateSystemDefaultDevice())
30 | }
31 |
32 | override func viewDidLoad() {
33 | super.viewDidLoad()
34 | guard let drawingView else { return }
35 | renderer = Renderer(metalKitView: drawingView)
36 | view.addSubview(bottomView)
37 |
38 | bottomView.pinToSuperviewEdges(exclude: .top, respectingSafeArea: false)
39 | NSLayoutConstraint.activate([
40 | bottomView.heightAnchor.constraint(equalToConstant: 88 + 33 + 25),
41 | ])
42 |
43 | editorViewModel.$state.receive(on: DispatchQueue.main)
44 | .sink { [weak self] state in
45 | guard let self = self else { return }
46 | switch state {
47 | case .draw(let brush, let presentationState):
48 | switch presentationState {
49 | case .updated:
50 | self.drawingView?.selectedBrush = brush
51 | case .show:
52 | self.drawingView?.selectedBrush = brush
53 | case .hide:
54 | print("brush: \(brush) hide")
55 | }
56 | case .brushConfiguration(let brush, let presentationState):
57 | switch presentationState {
58 | case .updated:
59 | print("brushConfiguration updated, brush: \(brush)")
60 | case .show:
61 | print("brushConfiguration show, brush: \(brush)")
62 | case .hide:
63 | print("brushConfiguration hide, brush: \(brush)")
64 | }
65 | self.drawingView?.selectedBrush = brush
66 | case let .colorPicker(color, _, presentationState):
67 | switch presentationState {
68 | case .show:
69 | print("color picker show")
70 | CollorPickerBottomSheetController.buildAndPresent(
71 | viewController: self,
72 | viewModel: self.editorViewModel
73 | )
74 | case .hide:
75 | print("color picker hide")
76 | case .updated:
77 | print("color picker updated")
78 | }
79 | self.drawingView?.selectedBrush.color = color
80 | case .clear(_):
81 | self.drawingView?.clear?()
82 | default:
83 | break
84 | }
85 | }.store(in: &bindings)
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Extensions/CGImage + PixelColor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGImage + PixelColor.swift
3 | // telegram-contest
4 | //
5 | // Created by Bogdan Redkin on 29/10/2022.
6 | //
7 |
8 | import CoreGraphics
9 | import UIKit
10 |
11 | extension CGImage {
12 |
13 | func pixel(x: Int, y: Int) -> (r: Int, g: Int, b: Int)? {
14 | guard let pixelData = dataProvider?.data,
15 | let data = CFDataGetBytePtr(pixelData) else { return nil }
16 |
17 | let pixelInfo = ((width * y) + x ) * 4
18 |
19 | let red = Int(data[pixelInfo])
20 | let green = Int(data[(pixelInfo + 1)])
21 | let blue = Int(data[pixelInfo + 2])
22 |
23 | return (red, green, blue)
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Extensions/CGPoint + Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGPoint + Arithmetic.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 11/10/2022.
6 | //
7 |
8 | import Foundation
9 | import simd
10 |
11 | extension CGPoint {
12 | func interpolate(to: CGPoint, progress: CGFloat) -> CGPoint {
13 | guard progress >= 0.0 && progress <= 1.0 else { return self }
14 | let x = self.x.interpolate(to: to.x, progress: progress)
15 | let y = self.y.interpolate(to: to.y, progress: progress)
16 | return CGPoint(x: x, y: y)
17 | }
18 |
19 | init(vector: vector_uint2) {
20 | self.init(x: CGFloat(vector.x), y: CGFloat(vector.y))
21 | }
22 |
23 | init(vector: vector_float2) {
24 | self.init(x: vector.x.cgFloat, y: vector.y.cgFloat)
25 | }
26 |
27 | var vectorFloat: vector_float2 {
28 | return vector_float2(x: x.float, y: y.float)
29 | }
30 |
31 | var vectorUint: vector_uint2 {
32 | return vector_uint2(x: x.int32, y: y.int32)
33 | }
34 |
35 | // Arithmetic
36 |
37 | static func + (lhs: CGPoint, rhs: CGPoint) -> CGPoint {
38 | return CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
39 | }
40 |
41 | static func -(lhs: CGPoint, rhs: CGPoint) -> CGVector {
42 | return CGVector(dx: lhs.x - rhs.x, dy: lhs.y - rhs.y)
43 | }
44 |
45 | static func -(lhs: CGPoint, rhs: CGVector) -> CGPoint {
46 | return CGPoint(x: lhs.x - rhs.dx, y: lhs.y - rhs.dy)
47 | }
48 |
49 | static func *(lhs: CGPoint, rhs: CGFloat) -> CGPoint {
50 | return CGPoint(x: lhs.x * rhs, y: lhs.y * rhs)
51 | }
52 |
53 | static func *(lhs: CGFloat, rhs: CGPoint) -> CGPoint {
54 | return CGPoint(x: rhs.x * lhs, y: rhs.y * lhs)
55 | }
56 |
57 | func distanceToPoint(otherPoint: CGPoint) -> CGFloat {
58 | return sqrt(pow((otherPoint.x - x), 2) + pow((otherPoint.y - y), 2))
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Extensions/CGRect + Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGRect + Helpers.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 11/10/2022.
6 | //
7 |
8 | import Foundation
9 |
10 | extension CGRect {
11 |
12 | public init(size: CGSize) {
13 | self.init(origin: .zero, size: size)
14 | }
15 |
16 | func scale(_ factor: CGFloat) -> CGRect {
17 | let transform = CGAffineTransform(scaleX: factor, y: factor)
18 | return self.applying(transform)
19 | }
20 |
21 | func interpolate(to: CGRect, progress: CGFloat) -> CGRect {
22 | guard progress >= 0.0, progress <= 1.0 else { return self }
23 | let origin = origin.interpolate(to: to.origin, progress: progress)
24 | let size = size.interpolate(to: to.size, progress: progress)
25 | return CGRect(origin: origin, size: size)
26 | }
27 |
28 | var center: CGPoint {
29 | return CGPoint(x: midX, y: midY)
30 | }
31 |
32 | init(center: CGPoint, size: CGSize) {
33 | self.init(origin: CGPoint(x: center.x - size.width / 2, y: center.y - size.height / 2), size: size)
34 | }
35 |
36 | func withOrigin(_ newOrigin: CGPoint) -> CGRect {
37 | var newRect = self
38 | newRect.origin = newOrigin
39 | return newRect
40 | }
41 |
42 | func withX(_ newX: CGFloat) -> CGRect {
43 | return withOrigin(CGPoint(x: newX, y: minY))
44 | }
45 |
46 | func withY(_ newY: CGFloat) -> CGRect {
47 | return withOrigin(CGPoint(x: minX, y: newY))
48 | }
49 |
50 | func withSize(_ newValue: CGSize) -> CGRect {
51 | var newRect = self
52 | newRect.size = newValue
53 | return newRect
54 | }
55 |
56 | func withWidth(_ newWidth: CGFloat) -> CGRect {
57 | return withSize(CGSize(width: newWidth, height: height))
58 | }
59 |
60 |
61 | func withHeight(_ newHeight: CGFloat) -> CGRect {
62 | return withSize(CGSize(width: width, height: newHeight))
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Extensions/CGSize + Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGSize + Helpers.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 11/10/2022.
6 | //
7 |
8 | import Foundation
9 |
10 | extension CGSize {
11 |
12 | init(size: CGFloat) {
13 | self.init(width: size, height: size)
14 | }
15 |
16 | func interpolate(to: CGSize, progress: CGFloat) -> CGSize {
17 | guard progress >= 0.0 && progress <= 1.0 else { return self }
18 |
19 | let width = self.width.interpolate(to: to.width, progress: progress)
20 | let height = self.height.interpolate(to: to.height, progress: progress)
21 | return CGSize(width: width, height: height)
22 | }
23 |
24 | func scale(_ factor: CGFloat) -> CGSize {
25 | let transform = CGAffineTransform(scaleX: factor, y: factor)
26 | return self.applying(transform)
27 | }
28 |
29 | static func *(lhs: CGSize, rhs: CGSize) -> CGSize {
30 | return CGSize(width: lhs.width * rhs.width, height: lhs.height * rhs.height)
31 | }
32 |
33 | static func *(lhs: CGSize, rhs: CGFloat) -> CGSize {
34 | return CGSize(width: lhs.width * rhs, height: lhs.height * rhs)
35 | }
36 |
37 | static func *(lhs: CGFloat, rhs: CGSize) -> CGSize {
38 | return CGSize(width: rhs.width * lhs, height: rhs.height * lhs)
39 | }
40 |
41 | static func /(lhs: CGSize, rhs: CGSize) -> CGSize {
42 | return CGSize(width: lhs.width / rhs.width, height: lhs.height / rhs.height)
43 | }
44 |
45 | static func /(lhs: CGSize, rhs: CGFloat) -> CGSize {
46 | return CGSize(width: lhs.width / rhs, height: lhs.height / rhs)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Extensions/MTLTexture + Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MTLTexture + Helpers.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 20/06/2023.
6 | //
7 |
8 | import Metal
9 | import UIKit
10 | import simd
11 |
12 | extension MTLTexture {
13 | // func clear() {
14 | // let region = MTLRegion(
15 | // origin: MTLOrigin(x: 0, y: 0, z: 0),
16 | // size: MTLSize(width: width, height: height, depth: 1)
17 | // )
18 | // let bytesPerRow = 4 * width
19 | // let data = Data(capacity: Int(bytesPerRow * height))
20 | // if let bytes = data.withUnsafeBytes({ $0.baseAddress }) {
21 | // replace(region: region, mipmapLevel: 0, withBytes: bytes, bytesPerRow: bytesPerRow)
22 | // }
23 | // }
24 | }
25 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Extensions/Number+Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Number+Helpers.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 11/10/2022.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Int {
11 | func interpolate(to: Int, progress: CGFloat) -> Int {
12 | guard progress >= 0.0 && progress <= 1.0 else { return self }
13 | return self + Int(CGFloat(to - self) * progress)
14 | }
15 |
16 | var int32: UInt32 {
17 | return UInt32(self)
18 | }
19 |
20 | var float: Float {
21 | return Float(self)
22 | }
23 |
24 | var double: Double {
25 | return Double(self)
26 | }
27 |
28 | var cgFloat: CGFloat {
29 | return CGFloat(self)
30 | }
31 |
32 | var string: String {
33 | return String(describing: self)
34 | }
35 |
36 | static func extract(from string: String) -> Int? {
37 | return Int(string.components(separatedBy: CharacterSet.decimalDigits.inverted).joined())
38 | }
39 | }
40 |
41 | extension Float {
42 |
43 | var int: Int {
44 | return Int(self)
45 | }
46 |
47 | var int32: UInt32 {
48 | return UInt32(self)
49 | }
50 |
51 | var double: Double {
52 | return Double(self)
53 | }
54 |
55 | var cgFloat: CGFloat {
56 | return CGFloat(self)
57 | }
58 |
59 | var string: String {
60 | return String(describing: self)
61 | }
62 | }
63 |
64 | extension Double {
65 | var float: Float {
66 | return Float(self)
67 | }
68 |
69 | var int: Int {
70 | return Int(self)
71 | }
72 |
73 | var int32: UInt32 {
74 | return UInt32(self)
75 | }
76 |
77 | var cgFloat: CGFloat {
78 | return CGFloat(self)
79 | }
80 |
81 | var string: String {
82 | return String(describing: self)
83 | }
84 | }
85 |
86 | let π = CGFloat.pi
87 |
88 | extension CGFloat {
89 | func interpolate(to: CGFloat, progress: CGFloat) -> CGFloat {
90 | guard progress >= 0.0 && progress <= 1.0 else { return self }
91 | return self + (to - self) * progress
92 | }
93 |
94 | var int: Int {
95 | return Int(self)
96 | }
97 |
98 | var int32: UInt32 {
99 | return UInt32(self)
100 | }
101 |
102 | var double: Double {
103 | return Double(self)
104 | }
105 |
106 | var float: Float {
107 | return Float(self)
108 | }
109 |
110 | var string: String {
111 | return String(describing: self)
112 | }
113 | // Converts an angle in degrees to radians.
114 | func degreesToRadians() -> CGFloat {
115 |
116 | return π * self / 180.0
117 | }
118 |
119 | // Converts an angle in radians to degrees.
120 | func radiansToDegrees() -> CGFloat {
121 | return self * 180.0 / π
122 | }
123 |
124 | // Ensures that the float value stays between the given values, inclusive.
125 | func clamped(_ v1: CGFloat, _ v2: CGFloat) -> CGFloat {
126 | let min = v1 < v2 ? v1 : v2
127 | let max = v1 > v2 ? v1 : v2
128 | return self < min ? min : (self > max ? max : self)
129 | }
130 |
131 | // Ensures that the float value stays between the given values, inclusive.
132 | mutating func clamp(_ v1: CGFloat, _ v2: CGFloat) -> CGFloat {
133 | self = clamped(v1, v2)
134 | return self
135 | }
136 |
137 | // Returns 1.0 if a floating point value is positive; -1.0 if it is negative.
138 | func sign() -> CGFloat {
139 | return (self >= 0.0) ? 1.0 : -1.0
140 | }
141 |
142 | func rounded(symbolsAfterComma count: Int) -> CGFloat {
143 | let multiplier = pow(10, count.double).cgFloat
144 | let result = (self * multiplier).rounded() / multiplier
145 | return result
146 | }
147 | }
148 |
149 | extension UInt32 {
150 | var float: Float {
151 | return Float(self)
152 | }
153 |
154 | var int: Int {
155 | return Int(self)
156 | }
157 |
158 | var cgFloat: CGFloat {
159 | return CGFloat(self)
160 | }
161 |
162 | var string: String {
163 | return String(describing: self)
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Extensions/UIApplication + currentWindow.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIApplication + currentWindow.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 06/06/2023.
6 | //
7 |
8 | import UIKit
9 |
10 | extension UIApplication {
11 |
12 | var window: UIWindow? {
13 | // Get connected scenes
14 | let scene = UIApplication.shared.connectedScenes
15 | // Keep only active scenes, onscreen and visible to the user
16 | .filter { $0.activationState == .foregroundActive }
17 | // Keep only the first `UIWindowScene`
18 | .first(where: { $0 is UIWindowScene })
19 | // Get its associated windows
20 | .flatMap { $0 as? UIWindowScene }
21 |
22 | return scene?.keyWindow
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Extensions/UIColor + Utils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor + Hex.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 10/10/2022.
6 | //
7 |
8 | import UIKit
9 | import simd
10 |
11 | extension UIColor {
12 |
13 | public convenience init?(hex: String) {
14 | var hexInt: UInt64 = 0
15 | let scanner = Scanner(string: hex)
16 | scanner.charactersToBeSkipped = CharacterSet(charactersIn: "#")
17 | scanner.scanHexInt64(&hexInt)
18 |
19 | let red = CGFloat((hexInt & 0xFF0000) >> 16) / 255.0
20 | let green = CGFloat((hexInt & 0xFF00) >> 8) / 255.0
21 | let blue = CGFloat((hexInt & 0xFF) >> 0) / 255.0
22 |
23 | self.init(red: red, green: green, blue: blue, alpha: 1.0)
24 | }
25 |
26 | static var random: UIColor {
27 | return UIColor(red: .random(in: 0...1),
28 | green: .random(in: 0...1),
29 | blue: .random(in: 0...1),
30 | alpha: 1.0)
31 | }
32 |
33 | var hexString: String {
34 | let (r, g, b, _) = rgbaCGFloat
35 |
36 | let rgb:Int = (Int)(r*255)<<16 | (Int)(g*255)<<8 | (Int)(b*255)<<0
37 |
38 | return String(format:"#%06x", rgb)
39 | }
40 |
41 | typealias RGBA = (r: T, g: T, b: T, a: T)
42 |
43 | var rgbaCGFloat: RGBA {
44 | var r: CGFloat = .zero
45 | var g: CGFloat = .zero
46 | var b: CGFloat = .zero
47 | var a: CGFloat = .zero
48 | getRed(&r, green: &g, blue: &b, alpha: &a)
49 | return (r, g, b, a)
50 | }
51 |
52 | var rgbaDouble: RGBA {
53 | let (r,g,b,a) = rgbaCGFloat
54 | return (r.double, g.double, b.double, a.double)
55 | }
56 |
57 | var vectorFloat4: vector_float4 {
58 | let (r,g,b,a) = rgbaDouble
59 | return vector_float4(r.float, g.float, b.float, a.float)
60 | }
61 |
62 | var hsba: (h: CGFloat, s: CGFloat, b: CGFloat, a: CGFloat) {
63 | var h: CGFloat = .zero
64 | var s: CGFloat = .zero
65 | var b: CGFloat = .zero
66 | var a: CGFloat = .zero
67 | getHue(&h, saturation: &s, brightness: &b, alpha: &a)
68 | return (h, s, b, a)
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Extensions/UIControl + Combine.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIControl + Combine.swift
3 | // telegram-contest
4 | //
5 | // Created by Bogdan Redkin on 11/10/2022.
6 | //
7 |
8 | import Foundation
9 | import Combine
10 | import UIKit
11 |
12 | final class UIControlSubscription: Subscription where SubscriberType.Input == Control {
13 | private var subscriber: SubscriberType?
14 | private let control: Control
15 |
16 | init(subscriber: SubscriberType, control: Control, event: UIControl.Event) {
17 | self.subscriber = subscriber
18 | self.control = control
19 | control.addTarget(self, action: #selector(self.eventHandler), for: event)
20 | }
21 |
22 | func request(_ demand: Subscribers.Demand) { }
23 |
24 | func cancel() {
25 | self.subscriber = nil
26 | }
27 |
28 | @objc private func eventHandler() {
29 | _ = self.subscriber?.receive(self.control)
30 | }
31 | }
32 |
33 | struct UIControlPublisher: Publisher {
34 | typealias Output = Control
35 | typealias Failure = Never
36 |
37 | let control: Control
38 | let controlEvents: UIControl.Event
39 |
40 | init(control: Control, events: UIControl.Event) {
41 | self.control = control
42 | self.controlEvents = events
43 | }
44 |
45 | func receive(subscriber: S) where S: Subscriber, S.Failure == UIControlPublisher.Failure, S.Input == UIControlPublisher.Output {
46 | let subscription = UIControlSubscription(subscriber: subscriber, control: control, event: controlEvents)
47 | subscriber.receive(subscription: subscription)
48 | }
49 | }
50 |
51 | protocol CombineCompatible {}
52 | extension UIControl: CombineCompatible {}
53 | extension CombineCompatible where Self: UIControl {
54 | func publisher(for events: UIControl.Event) -> UIControlPublisher {
55 | return UIControlPublisher(control: self, events: events)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Extensions/UIView + Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIView + Helpers.swift
3 | // telegram-contest
4 | //
5 | // Created by Bogdan Redkin on 16/10/2022.
6 | //
7 |
8 | import UIKit
9 |
10 | extension UIView {
11 |
12 | func setAnchorPoint(_ point: CGPoint) {
13 | var newPoint = CGPoint(x: bounds.size.width * point.x, y: bounds.size.height * point.y)
14 | var oldPoint = CGPoint(x: bounds.size.width * layer.anchorPoint.x, y: bounds.size.height * layer.anchorPoint.y);
15 |
16 | newPoint = newPoint.applying(transform)
17 | oldPoint = oldPoint.applying(transform)
18 |
19 | var position = layer.position
20 |
21 | position.x -= oldPoint.x
22 | position.x += newPoint.x
23 |
24 | position.y -= oldPoint.y
25 | position.y += newPoint.y
26 |
27 | layer.position = position
28 | layer.anchorPoint = point
29 | }
30 |
31 | var rootSuperView: UIView? {
32 | if superview?.superview == nil {
33 | return superview
34 | } else {
35 | return superview?.rootSuperView
36 | }
37 | }
38 |
39 | var recursiveAllSubviews: [UIView] {
40 | subviews + subviews.flatMap { $0.recursiveAllSubviews }
41 | }
42 |
43 | func firstSubview(of type: T.Type) -> T? {
44 | recursiveAllSubviews.first { $0 is T } as? T
45 | }
46 |
47 | func findFirstResponder() -> UIView? {
48 | for subview in subviews {
49 | if subview.isFirstResponder {
50 | return subview
51 | }
52 |
53 | if let recursiveSubView = subview.findFirstResponder() {
54 | return recursiveSubView
55 | }
56 | }
57 |
58 | return nil
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Metal/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Metal/.DS_Store
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Metal/DrawingGestureRecognizer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DrawingGestureRecognizer.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 25/10/2022.
6 | //
7 |
8 | import UIKit
9 |
10 | class DrawingGestureRecognizer: UIPanGestureRecognizer {
11 |
12 | typealias touchesResponse = (touches: Set, event: UIEvent, velocity: CGPoint)
13 |
14 | var touchesBeganHandler: ((touchesResponse) -> Void)?
15 | var touchesMovedHandler: ((touchesResponse) -> Void)?
16 | var touchesEndedHandler: ((touchesResponse) -> Void)?
17 | var touchesCancelledHandler: ((touchesResponse) -> Void)?
18 |
19 | override func touchesBegan(_ touches: Set, with event: UIEvent) {
20 | let response: touchesResponse = (touches, event, velocity(in: view))
21 | touchesBeganHandler?(response)
22 | super.touchesBegan(touches, with: event)
23 | }
24 |
25 | override func touchesMoved(_ touches: Set, with event: UIEvent) {
26 | let response: touchesResponse = (touches, event, velocity(in: view))
27 | touchesMovedHandler?(response)
28 | // print("touches moved: \(touches)")
29 | super.touchesMoved(touches, with: event)
30 | }
31 |
32 | override func touchesEnded(_ touches: Set, with event: UIEvent) {
33 | // guard !touches.filter({ $0.view == self.view }).isEmpty else { return }
34 | let response: touchesResponse = (touches, event, velocity(in: view))
35 | touchesEndedHandler?(response)
36 | super.touchesEnded(touches, with: event)
37 | }
38 |
39 | override func touchesCancelled(_ touches: Set, with event: UIEvent) {
40 | // guard !touches.filter({ $0.view == self.view }).isEmpty else { return }
41 | let response: touchesResponse = (touches, event, velocity(in: view))
42 | touchesCancelledHandler?(response)
43 | super.touchesCancelled(touches, with: event)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Metal/Shader.metal:
--------------------------------------------------------------------------------
1 | //
2 | // Shader.metal
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 06/05/2023.
6 | //
7 |
8 | #include
9 | using namespace metal;
10 |
11 | struct PointVertex {
12 | float4 position [[position]];
13 | float4 color;
14 | float size [[point_size]];
15 | };
16 |
17 | vertex PointVertex pointShaderVertex(constant PointVertex *points [[ buffer(0) ]],
18 | uint vid [[ vertex_id ]])
19 | {
20 | PointVertex out = points[vid];
21 |
22 | float2 pos = float2(out.position.x, out.position.y);
23 | out.position = float4(pos, 0, 1);
24 | return out;
25 | };
26 |
27 | fragment float4 pointShaderFragment(PointVertex point_data [[ stage_in ]],
28 | float2 pointCoord [[ point_coord ]])
29 | {
30 | float dist = length(pointCoord - float2(0.5));
31 | if (dist >= 0.5) {
32 | return float4(0);
33 | }
34 | return point_data.color;
35 | };
36 |
37 | struct TexturePipelineRasterizerData
38 | {
39 | float4 position [[position]];
40 | float2 texcoord;
41 | };
42 |
43 | struct TextureVertex
44 | {
45 | float2 position;
46 | float2 texcoord;
47 | };
48 |
49 |
50 | // Vertex shader which adjusts positions by an aspect ratio and passes texture
51 | // coordinates through to the rasterizer.
52 | vertex TexturePipelineRasterizerData textureVertexShader(const uint vertexID [[ vertex_id ]],
53 | const device TextureVertex *vertices [[ buffer(0) ]])
54 | {
55 | TexturePipelineRasterizerData out;
56 |
57 | out.position = vector_float4(vertices[vertexID].position.x, vertices[vertexID].position.y, 0.0, 1.0);
58 |
59 | out.texcoord = vertices[vertexID].texcoord;
60 | return out;
61 | }
62 |
63 | fragment float4 textureFragmentShader(TexturePipelineRasterizerData in [[stage_in]], texture2d texture [[texture(0)]])
64 | {
65 | constexpr sampler textureSampler(mag_filter::linear, min_filter::linear);
66 | float4 colorSample = float4(texture.sample(textureSampler, in.texcoord));
67 | return colorSample;
68 | }
69 |
70 | kernel void alphaBlend(texture2d outputTexture [[texture(0)]],
71 | texture2d inputTexture [[texture(1)]],
72 | texture2d inputTexture2 [[texture(2)]],
73 | constant float *mixturePercent [[buffer(0)]],
74 | uint2 grid [[thread_position_in_grid]]) {
75 |
76 | const half4 inColor = inputTexture.read(grid);
77 | constexpr sampler quadSampler(mag_filter::linear, min_filter::linear);
78 |
79 | const half4 inColor2 = inputTexture2.sample(quadSampler, float2(float(grid.x) / outputTexture.get_width(), float(grid.y) / outputTexture.get_height()));
80 | const half4 outColor(mix(inColor.rgb, inColor2.rgb, inColor2.a * half(*mixturePercent)), inColor.a);
81 |
82 | outputTexture.write(outColor, grid);
83 | }
84 |
85 | kernel void maskingBlend(texture2d outputTexture [[texture(0)]],
86 | texture2d inputTexture1 [[texture(1)]],
87 | texture2d inputTexture2 [[texture(2)]],
88 | uint2 gid [[thread_position_in_grid]])
89 | {
90 | if (gid.x < inputTexture1.get_width() && gid.y < inputTexture1.get_height()) {
91 | float4 pixel1 = inputTexture1.read(gid);
92 | float4 pixel2 = inputTexture2.read(gid);
93 |
94 | if (pixel2.a > 0.0) {
95 | pixel1 = float4(pixel1.rgb, 0.0);
96 | }
97 |
98 | outputTexture.write(pixel1, gid);
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Models/Brush.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Brush.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 17/10/2022.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | struct Brush {
12 |
13 | enum Style: String {
14 | case pen
15 | case brush
16 | case neon
17 | case pencil
18 | case eraser
19 | }
20 |
21 | let style: Style
22 | var width: Float
23 | var color: Color
24 |
25 | init(style: Style, width: Float? = nil, color: Color = Color(uiColor: .tintColor)) {
26 | self.style = style
27 | self.width = width ?? (style.maxWidth - style.minWidth)/2
28 | self.color = color
29 | }
30 |
31 | }
32 |
33 | extension Brush {
34 |
35 | var isAvailable: Bool {
36 | switch style {
37 | case .eraser, .pen: return true
38 | default: return false
39 | }
40 | }
41 |
42 | static func defaultBrushes() -> [Brush] {
43 | return [
44 | Brush(style: .pen),
45 | Brush(style: .brush),
46 | Brush(style: .neon),
47 | Brush(style: .pencil),
48 | Brush(style: .eraser)
49 | ]
50 | }
51 |
52 | func icon() -> UIImage? {
53 | return UIImage.generateToolIcon(name: style.rawValue, color: color.uiColor, height: width.cgFloat)
54 | }
55 |
56 |
57 | }
58 |
59 | extension Brush.Style {
60 | var contentScale: Float {
61 | return (UIApplication.shared.window?.rootViewController?.view.contentScaleFactor ?? 1.0).float
62 | }
63 |
64 | var maxWidth: Float {
65 | switch self {
66 | case .eraser:
67 | return 50 * contentScale
68 | default:
69 | return 40 * contentScale
70 | }
71 | }
72 |
73 | var minWidth: Float {
74 | switch self {
75 | case .eraser:
76 | return 25 * contentScale
77 | default:
78 | return 13 * contentScale
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Models/Color.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Color.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 20/10/2022.
6 | //
7 |
8 | import UIKit
9 |
10 | struct Color: Codable {
11 |
12 | var red: CGFloat
13 | var green: CGFloat
14 | var blue: CGFloat
15 | var alpha: CGFloat
16 |
17 | var hex: String {
18 | uiColor.hexString
19 | }
20 |
21 | var uiColor: UIColor {
22 | return UIColor(red: red, green: green, blue: blue, alpha: alpha)
23 | }
24 |
25 | var hue: CGFloat {
26 | get { uiColor.hsba.h }
27 | set {
28 | var (h,s,b,a) = uiColor.hsba
29 | h = newValue
30 | let (r, g, blue, _) = UIColor(hue: h, saturation: s, brightness: b, alpha: a).rgbaCGFloat
31 | self.red = r
32 | self.green = g
33 | self.blue = blue
34 | }
35 | }
36 |
37 | var saturation: CGFloat {
38 | get { uiColor.hsba.s }
39 | set {
40 | var (h,s,b,a) = uiColor.hsba
41 | s = newValue
42 | let (r, g, blue, _) = UIColor(hue: h, saturation: s, brightness: b, alpha: a).rgbaCGFloat
43 | self.red = r
44 | self.green = g
45 | self.blue = blue
46 | }
47 | }
48 |
49 | var brightness: CGFloat {
50 | get { uiColor.hsba.b }
51 | set {
52 | var (h, s, b, a) = uiColor.hsba
53 | b = newValue
54 | let (r, g, blue, _) = UIColor(hue: h, saturation: s, brightness: b, alpha: a).rgbaCGFloat
55 | self.red = r
56 | self.green = g
57 | self.blue = blue
58 | }
59 | }
60 |
61 | init(uiColor: UIColor) {
62 | let (r, g, b, a) = uiColor.rgbaCGFloat
63 | red = r
64 | green = g
65 | blue = b
66 | alpha = a
67 | }
68 |
69 | init(hue: CGFloat, saturation: CGFloat, brightness: CGFloat, alpha: CGFloat = 1) {
70 | let h = max(0, min(1, hue))
71 | let s = max(0, min(1, saturation))
72 | let b = max(0, min(1, brightness))
73 | let a = max(0, min(1, alpha))
74 | let uiColor = UIColor(hue: h, saturation: s, brightness: b, alpha: a)
75 | self.init(uiColor: uiColor)
76 | }
77 |
78 | init(hue: Int, saturation: Int, brightness: Int, alpha: CGFloat = 1) {
79 | self.init(hue: hue.cgFloat/359, saturation: saturation.cgFloat/100, brightness: brightness.cgFloat/100, alpha: alpha)
80 | }
81 |
82 | func save() {
83 | var array = Color.fetchSavedColors()
84 | array.append(self)
85 | if let encodedArray = try? JSONEncoder().encode(array),
86 | let string = String(data: encodedArray, encoding: .utf8) {
87 | UserDefaults.standard.set(string, forKey: "saved_colors")
88 | }
89 | }
90 |
91 | static func fetchSavedColors() -> [Color] {
92 | if let savedArray = UserDefaults.standard.string(forKey: "saved_colors"),
93 | let data = savedArray.data(using: .utf8),
94 | let decodedArray = try? JSONDecoder().decode([Color].self, from: data)
95 | {
96 | return decodedArray
97 | } else {
98 | return []
99 | }
100 | }
101 |
102 | static func defaultPalette() -> [Color] {
103 | return [
104 | UIColor.black,
105 | UIColor.systemBlue,
106 | UIColor.systemRed,
107 | UIColor.systemOrange
108 | ].map { Color(uiColor: $0) }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Models/Point.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Point.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 07/05/2023.
6 | //
7 |
8 | import Foundation
9 | import simd
10 | import UIKit
11 |
12 | struct TextureVertex {
13 | var position: vector_float2
14 | var texcoord: vector_float2
15 |
16 | init(position: vector_float2, texcoord: vector_float2) {
17 | self.position = position
18 | self.texcoord = texcoord
19 | }
20 |
21 | init?(corner: UIRectCorner) {
22 | switch corner {
23 | case .topLeft:
24 | self.init(position: .init(x: -1.0, y: 1.0), texcoord: .init(0.0, 0.0))
25 | case .topRight:
26 | self.init(position: .init(x: 1.0, y: 1.0), texcoord: .init(1.0, 0.0))
27 | case .bottomLeft:
28 | self.init(position: .init(x: -1, y: -1), texcoord: .init(0.0, 1.0))
29 | case .bottomRight:
30 | self.init(position: .init(x: 1, y: -1), texcoord: .init(1.0, 1.0))
31 | default:
32 | return nil
33 | }
34 | }
35 | }
36 |
37 | struct Point {
38 | var position: vector_float4
39 | var color: vector_float4
40 | var size: Float
41 |
42 | init(position: vector_float4, color: vector_float4, size: Float) {
43 | self.position = position
44 | self.color = color
45 | self.size = size
46 | }
47 |
48 | init(x: CGFloat, y: CGFloat, color: UIColor, size: CGFloat) {
49 | self.init(position: vector_float4(Float(x), Float(y), 0, 1), color: color.vectorFloat4, size: Float(size))
50 | }
51 |
52 | init(location: CGPoint, parentSize: CGSize, color: UIColor, size: CGFloat) {
53 | let roundedLocation = CGPoint(x: location.x.int.cgFloat, y: location.y.int.cgFloat)
54 | let xK = (1.0 / (parentSize.width / roundedLocation.x)).rounded(symbolsAfterComma: 5)
55 | let yK = (1.0 / (parentSize.height / roundedLocation.y)).rounded(symbolsAfterComma: 5)
56 | let x = xK == 0.5 ? 0.0 : (xK < 0.5 ? -(1 - xK / 0.5) : xK / 0.5 - 1)
57 | let y = yK == 0.5 ? 0.0 : (yK > 0.5 ? (1 - yK / 0.5) : 1 - (yK / 0.5))
58 | self.init(x: x, y: y, color: color, size: size)
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 29/04/2023.
6 | //
7 |
8 | import UIKit
9 |
10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
11 |
12 | var window: UIWindow?
13 |
14 |
15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
16 | guard let windowScene = (scene as? UIWindowScene) else { return }
17 |
18 | let window = UIWindow(windowScene: windowScene)
19 | window.overrideUserInterfaceStyle = .light
20 | window.rootViewController = DrawingViewController()
21 | self.window = window
22 | window.makeKeyAndVisible()
23 | }
24 |
25 | func sceneDidDisconnect(_ scene: UIScene) {
26 | // Called as the scene is being released by the system.
27 | // This occurs shortly after the scene enters the background, or when its session is discarded.
28 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
29 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
30 | }
31 |
32 | func sceneDidBecomeActive(_ scene: UIScene) {
33 |
34 | }
35 |
36 | func sceneWillResignActive(_ scene: UIScene) {
37 | // Called when the scene will move from an active state to an inactive state.
38 | // This may occur due to temporary interruptions (ex. an incoming phone call).
39 | }
40 |
41 | func sceneWillEnterForeground(_ scene: UIScene) {
42 | // Called as the scene transitions from the background to the foreground.
43 | // Use this method to undo the changes made on entering the background.
44 | }
45 |
46 | func sceneDidEnterBackground(_ scene: UIScene) {
47 | // Called as the scene transitions from the foreground to the background.
48 | // Use this method to save data, release shared resources, and store enough scene-specific state information
49 | // to restore the scene back to its current state.
50 | }
51 |
52 |
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/.DS_Store
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/.DS_Store
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/add.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "add.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/add.imageset/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/add.imageset/add.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/arrowTip.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "arrowTip.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/arrowTip.imageset/arrowTip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/arrowTip.imageset/arrowTip.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/back.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "back.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/back.imageset/back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/back.imageset/back.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/blurEraser.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "blurEraser.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/blurEraser.imageset/blurEraser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/blurEraser.imageset/blurEraser.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/blurTip.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "blurTip.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/blurTip.imageset/blurTip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/blurTip.imageset/blurTip.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/brush.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "brush.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/brush.imageset/brush.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/brush.imageset/brush.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/brush_tip.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "brush.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/brush_tip.imageset/brush.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/brush_tip.imageset/brush.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/cancel.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "cancel.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/cancel.imageset/cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/cancel.imageset/cancel.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/close.imageset/Close.pdf:
--------------------------------------------------------------------------------
1 | %PDF-1.7
2 |
3 | 1 0 obj
4 | << /ExtGState << /E2 << /ca 0.600000 >>
5 | /E1 << /ca 0.240000 >>
6 | >> >>
7 | endobj
8 |
9 | 2 0 obj
10 | << /Length 3 0 R >>
11 | stream
12 | /DeviceRGB CS
13 | /DeviceRGB cs
14 | q
15 | /E1 gs
16 | 1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
17 | 0.462745 0.462745 0.501961 scn
18 | 30.000000 15.000000 m
19 | 30.000000 6.715729 23.284271 0.000000 15.000000 0.000000 c
20 | 6.715729 0.000000 0.000000 6.715729 0.000000 15.000000 c
21 | 0.000000 23.284271 6.715729 30.000000 15.000000 30.000000 c
22 | 23.284271 30.000000 30.000000 23.284271 30.000000 15.000000 c
23 | h
24 | f
25 | n
26 | Q
27 | q
28 | /E2 gs
29 | 1.000000 0.000000 -0.000000 1.000000 9.500000 9.304749 cm
30 | 0.921569 0.921569 0.960784 scn
31 | 0.292893 9.488144 m
32 | -0.097631 9.878669 -0.097631 10.511834 0.292893 10.902358 c
33 | 0.683417 11.292882 1.316583 11.292882 1.707107 10.902358 c
34 | 5.500000 7.109465 l
35 | 9.292893 10.902358 l
36 | 9.683417 11.292882 10.316583 11.292882 10.707107 10.902358 c
37 | 11.097631 10.511834 11.097631 9.878669 10.707107 9.488144 c
38 | 6.914213 5.695251 l
39 | 10.707107 1.902358 l
40 | 11.097631 1.511834 11.097631 0.878669 10.707107 0.488145 c
41 | 10.316583 0.097620 9.683417 0.097620 9.292893 0.488145 c
42 | 5.500000 4.281038 l
43 | 1.707107 0.488145 l
44 | 1.316583 0.097620 0.683418 0.097620 0.292893 0.488145 c
45 | -0.097631 0.878669 -0.097631 1.511834 0.292893 1.902358 c
46 | 4.085787 5.695251 l
47 | 0.292893 9.488144 l
48 | h
49 | f*
50 | n
51 | Q
52 |
53 | endstream
54 | endobj
55 |
56 | 3 0 obj
57 | 1153
58 | endobj
59 |
60 | 4 0 obj
61 | << /Annots []
62 | /Type /Page
63 | /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
64 | /Resources 1 0 R
65 | /Contents 2 0 R
66 | /Parent 5 0 R
67 | >>
68 | endobj
69 |
70 | 5 0 obj
71 | << /Kids [ 4 0 R ]
72 | /Count 1
73 | /Type /Pages
74 | >>
75 | endobj
76 |
77 | 6 0 obj
78 | << /Pages 5 0 R
79 | /Type /Catalog
80 | >>
81 | endobj
82 |
83 | xref
84 | 0 7
85 | 0000000000 65535 f
86 | 0000000010 00000 n
87 | 0000000132 00000 n
88 | 0000001341 00000 n
89 | 0000001364 00000 n
90 | 0000001537 00000 n
91 | 0000001611 00000 n
92 | trailer
93 | << /ID [ (some) (id) ]
94 | /Root 6 0 R
95 | /Size 7
96 | >>
97 | startxref
98 | 1670
99 | %%EOF
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/close.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Close.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/color_picker.imageset/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/color_picker.imageset/.DS_Store
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/color_picker.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "color_picker_gradient.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/color_picker.imageset/color_picker_gradient.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/color_picker.imageset/color_picker_gradient.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/download.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "download.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/download.imageset/download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/download.imageset/download.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/eraser.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "eraser.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/eraser.imageset/eraser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/eraser.imageset/eraser.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/eyedropper.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "eyedropper.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/eyedropper.imageset/eyedropper.pdf:
--------------------------------------------------------------------------------
1 | %PDF-1.7
2 |
3 | 1 0 obj
4 | << /Length 2 0 R >>
5 | stream
6 | 1.243652 0 0.112305 -0.165527 1.158203 1.150879 d1
7 |
8 | endstream
9 | endobj
10 |
11 | 2 0 obj
12 | 51
13 | endobj
14 |
15 | 3 0 obj
16 | [ 1.243652 ]
17 | endobj
18 |
19 | 4 0 obj
20 | << /Length 5 0 R >>
21 | stream
22 | /CIDInit /ProcSet findresource begin
23 | 12 dict begin
24 | begincmap
25 | /CIDSystemInfo
26 | << /Registry (FigmaPDF)
27 | /Ordering (FigmaPDF)
28 | /Supplement 0
29 | >> def
30 | /CMapName /A-B-C def
31 | /CMapType 2 def
32 | 1 begincodespacerange
33 | <00>
34 | endcodespacerange
35 | 1 beginbfchar
36 | <00>
37 | endbfchar
38 | endcmap
39 | CMapName currentdict /CMap defineresource pop
40 | end
41 | end
42 | endstream
43 | endobj
44 |
45 | 5 0 obj
46 | 336
47 | endobj
48 |
49 | 6 0 obj
50 | << /Subtype /Type3
51 | /CharProcs << /C0 1 0 R >>
52 | /Encoding << /Type /Encoding
53 | /Differences [ 0 /C0 ]
54 | >>
55 | /Widths 3 0 R
56 | /FontBBox [ 0.000000 0.000000 0.000000 0.000000 ]
57 | /FontMatrix [ 1.000000 0.000000 0.000000 1.000000 0.000000 0.000000 ]
58 | /Type /Font
59 | /ToUnicode 4 0 R
60 | /FirstChar 0
61 | /LastChar 0
62 | /Resources << >>
63 | >>
64 | endobj
65 |
66 | 7 0 obj
67 | << /Font << /F1 6 0 R >> >>
68 | endobj
69 |
70 | 8 0 obj
71 | << /Length 9 0 R >>
72 | stream
73 | /DeviceRGB CS
74 | /DeviceRGB cs
75 | q
76 | 1.000000 0.000000 -0.000000 1.000000 -5.824219 5.285156 cm
77 | 1.000000 1.000000 1.000000 scn
78 | 3.802734 -2.305664 m
79 | h
80 | 19.605469 3.723633 m
81 | 19.851562 3.996094 l
82 | 20.282227 4.444336 20.308594 4.971680 19.842773 5.428711 c
83 | 19.579102 5.683594 l
84 | 20.923828 6.887695 22.417969 7.054688 23.340820 7.995117 c
85 | 24.650391 9.313477 24.228516 11.159180 23.305664 12.082031 c
86 | 22.391602 13.013672 20.563477 13.409180 19.218750 12.117188 c
87 | 18.269531 11.203125 18.111328 9.700195 16.907227 8.355469 c
88 | 16.652344 8.619141 l
89 | 16.204102 9.076172 15.667969 9.058594 15.219727 8.627930 c
90 | 14.956055 8.381836 l
91 | 14.411133 7.863281 14.516602 7.406250 14.973633 6.940430 c
92 | 15.351562 6.571289 l
93 | 8.707031 -0.073242 l
94 | 5.991211 -2.789062 7.300781 -2.367188 5.824219 -4.450195 c
95 | 6.615234 -5.285156 l
96 | 8.627930 -3.817383 8.355469 -5.258789 11.115234 -2.507812 c
97 | 17.786133 4.127930 l
98 | 18.164062 3.750000 l
99 | 18.629883 3.284180 19.095703 3.187500 19.605469 3.723633 c
100 | h
101 | 8.531250 -2.736328 m
102 | 8.206055 -2.384766 8.276367 -2.050781 8.619141 -1.699219 c
103 | 16.107422 5.815430 l
104 | 17.056641 4.857422 l
105 | 9.541992 -2.648438 l
106 | 9.234375 -2.964844 8.830078 -3.061523 8.531250 -2.736328 c
107 | h
108 | f
109 | n
110 | Q
111 | q
112 | 1.000000 0.000000 -0.000000 1.000000 -5.824219 5.285156 cm
113 | BT
114 | 18.000000 0.000000 0.000000 18.000000 3.802734 -2.305664 Tm
115 | /F1 1.000000 Tf
116 | [ (\000) ] TJ
117 | ET
118 | Q
119 |
120 | endstream
121 | endobj
122 |
123 | 9 0 obj
124 | 1321
125 | endobj
126 |
127 | 10 0 obj
128 | << /Annots []
129 | /Type /Page
130 | /MediaBox [ 0.000000 0.000000 18.826172 18.694336 ]
131 | /Resources 7 0 R
132 | /Contents 8 0 R
133 | /Parent 11 0 R
134 | >>
135 | endobj
136 |
137 | 11 0 obj
138 | << /Kids [ 10 0 R ]
139 | /Count 1
140 | /Type /Pages
141 | >>
142 | endobj
143 |
144 | 12 0 obj
145 | << /Pages 11 0 R
146 | /Type /Catalog
147 | >>
148 | endobj
149 |
150 | xref
151 | 0 13
152 | 0000000000 65535 f
153 | 0000000010 00000 n
154 | 0000000117 00000 n
155 | 0000000138 00000 n
156 | 0000000169 00000 n
157 | 0000000561 00000 n
158 | 0000000583 00000 n
159 | 0000000995 00000 n
160 | 0000001041 00000 n
161 | 0000002418 00000 n
162 | 0000002441 00000 n
163 | 0000002616 00000 n
164 | 0000002692 00000 n
165 | trailer
166 | << /ID [ (some) (id) ]
167 | /Root 12 0 R
168 | /Size 13
169 | >>
170 | startxref
171 | 2753
172 | %%EOF
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/neon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "neon.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/neon.imageset/neon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/neon.imageset/neon.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/neon_tip.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "neon.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/neon_tip.imageset/neon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/neon_tip.imageset/neon.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/objectEraser.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "objectEraser.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/objectEraser.imageset/objectEraser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/objectEraser.imageset/objectEraser.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/opacity_slider_background.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "VStack.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/opacity_slider_thumb.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Ellipse 26.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/opacity_slider_thumb.imageset/Ellipse 26.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/opacity_slider_thumb.imageset/Ellipse 26.pdf
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/pen.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "pen.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/pen.imageset/pen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/pen.imageset/pen.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/pen_tip.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "pen.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/pen_tip.imageset/pen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/pen_tip.imageset/pen.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/pencil.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "pencil.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/pencil.imageset/pencil.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/pencil.imageset/pencil.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/pencil_tip.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "pencil.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/pencil_tip.imageset/pencil.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/pencil_tip.imageset/pencil.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/roundTip.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "roundTip.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/roundTip.imageset/roundTip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/roundTip.imageset/roundTip.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/shapeArrow.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "shapeArrow.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/shapeArrow.imageset/shapeArrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/shapeArrow.imageset/shapeArrow.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/shapeBubble.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "shapeBubble.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/shapeBubble.imageset/shapeBubble.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/shapeBubble.imageset/shapeBubble.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/shapeEllipse.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "shapeEllipse.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/shapeEllipse.imageset/shapeEllipse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/shapeEllipse.imageset/shapeEllipse.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/shapeRectangle.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "shapeRectangle.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/shapeRectangle.imageset/shapeRectangle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/shapeRectangle.imageset/shapeRectangle.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/shapeStar.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "shapeStar.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/shapeStar.imageset/shapeStar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/shapeStar.imageset/shapeStar.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/size_slslider.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Rectangle 53@2x.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/size_slslider.imageset/Rectangle 53@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/size_slslider.imageset/Rectangle 53@2x.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/undo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "undo.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/undo.imageset/undo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/undo.imageset/undo.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/xmarkTip.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "xmarkTip.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/xmarkTip.imageset/xmarkTip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/xmarkTip.imageset/xmarkTip.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/zoomOut.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "zoomOut.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/zoomOut.imageset/zoomOut.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Final/Draw App/Supporting Files/Assets.xcassets/zoomOut.imageset/zoomOut.png
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/DrawingApp-BridgingHeader.h:
--------------------------------------------------------------------------------
1 | //
2 | // DrawingApp-BridgingHeader.h
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 29/04/2023.
6 | //
7 |
8 | #ifndef DrawingApp_BridgingHeader_h
9 | #define DrawingApp_BridgingHeader_h
10 |
11 | #endif /* DrawingApp_BridgingHeader_h */
12 |
--------------------------------------------------------------------------------
/Draw_App_02_Final/Draw App/Supporting Files/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIApplicationSceneManifest
6 |
7 | UIApplicationSupportsMultipleScenes
8 |
9 | UISceneConfigurations
10 |
11 | UIWindowSceneSessionRoleApplication
12 |
13 |
14 | UISceneConfigurationName
15 | Default Configuration
16 | UISceneDelegateClassName
17 | $(PRODUCT_MODULE_NAME).SceneDelegate
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/.DS_Store
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/.DS_Store
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 29/04/2023.
6 | //
7 |
8 | import UIKit
9 |
10 | @main
11 | class AppDelegate: UIResponder, UIApplicationDelegate {
12 |
13 |
14 |
15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
16 | // Override point for customization after application launch.
17 | return true
18 | }
19 |
20 | // MARK: UISceneSession Lifecycle
21 |
22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
23 | // Called when a new scene session is being created.
24 | // Use this method to select a configuration to create the new scene with.
25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
26 | }
27 |
28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
29 | // Called when the user discards a scene session.
30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
32 | }
33 |
34 |
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/BottomView/Color Picker/ColorPaletteCollectionViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ColorPaletteCollectionViewCell.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 20/10/2022.
6 | //
7 |
8 | import UIKit
9 |
10 | class ColorPaletteCollectionViewCell: UICollectionViewCell {
11 |
12 | var item: CollorPickerBottomSheetController.Item?
13 | var imageView: UIImageView?
14 |
15 | override init(frame: CGRect) {
16 | super.init(frame: frame)
17 | setupViews()
18 | }
19 |
20 | required init?(coder: NSCoder) {
21 | super.init(coder: coder)
22 | setupViews()
23 | }
24 |
25 | func setup(with item: CollorPickerBottomSheetController.Item) {
26 | self.item = item
27 | switch item {
28 | case .add:
29 | imageView?.isHidden = false
30 | contentView.backgroundColor = .clear
31 | case .paletteItem(let item):
32 | contentView.backgroundColor = item.color.uiColor
33 | imageView?.isHidden = true
34 | }
35 |
36 | if isSelected {
37 | contentView.layer.borderWidth = 3
38 | contentView.layer.borderColor = UIColor.white.cgColor
39 | } else {
40 | contentView.layer.borderColor = UIColor.clear.cgColor
41 | contentView.layer.borderWidth = 0
42 | }
43 | }
44 |
45 | private func setupViews() {
46 | contentView.layer.cornerRadius = 15
47 | contentView.layer.masksToBounds = true
48 | imageView = UIImageView(frame: bounds)
49 | imageView?.image = UIImage.generateAddIconColorPicker()
50 | contentView.addSubviewPinnedToEdges(imageView!)
51 | }
52 |
53 | override func prepareForReuse() {
54 | super.prepareForReuse()
55 | self.imageView?.isHidden = true
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/BottomView/Color Picker/ColorPickerSlider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ColorPickerSlider.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 16/10/2022.
6 | //
7 |
8 | import UIKit
9 |
10 | class ColorPickerSlider: UISlider {
11 |
12 | private var currentColor: Color?
13 |
14 | override init(frame: CGRect) {
15 | super.init(frame: frame)
16 | initialSetup()
17 | }
18 |
19 | required init?(coder: NSCoder) {
20 | super.init(coder: coder)
21 | initialSetup()
22 | }
23 |
24 | private func initialSetup() {
25 | if let trackImage = UIImage.generateOpacitySliderTrackImage(color: .black) {
26 | self.setMaximumTrackImage(trackImage, for: .normal)
27 | self.setMinimumTrackImage(trackImage, for: .normal)
28 | }
29 | setThumbImage(UIImage(named: "opacity_slider_thumb"), for: .normal)
30 | }
31 |
32 | override func trackRect(forBounds bounds: CGRect) -> CGRect {
33 | var result = super.trackRect(forBounds: bounds)
34 | result.size.height = 36
35 | result.origin = .zero
36 | return result
37 | }
38 |
39 | func updateSelectedColor(color: Color) {
40 | if value.cgFloat != color.alpha {
41 | value = color.alpha.float
42 | }
43 | if let currentColor, currentColor.hex == color.hex {
44 | return
45 | }
46 | currentColor = color
47 | if let trackImage = UIImage.generateOpacitySliderTrackImage(color: color.uiColor) {
48 | self.setMaximumTrackImage(trackImage, for: .normal)
49 | self.setMinimumTrackImage(trackImage, for: .normal)
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/BottomView/EditorBottomSegementedControl.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EditorBottomSegementedControl.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 16/10/2022.
6 | //
7 |
8 | import UIKit
9 |
10 | class EditorBottomSegementedControl: UISegmentedControl {
11 |
12 | lazy var backgroundBlur: UIVisualEffectView = {
13 | let effect = UIBlurEffect(style: .dark)
14 | let black = UIVisualEffectView(effect: effect)
15 | return black
16 | }()
17 |
18 | override init(items: [Any]?) {
19 | super.init(items: items)
20 | commonInit()
21 | }
22 |
23 | override init(frame: CGRect) {
24 | super.init(frame: frame)
25 | commonInit()
26 | }
27 |
28 | required init?(coder: NSCoder) {
29 | super.init(coder: coder)
30 | commonInit()
31 | }
32 |
33 | private func commonInit() {
34 | layer.masksToBounds = true
35 | clipsToBounds = true
36 | selectedSegmentTintColor = UIColor(hex: "5D5D5D")
37 | tintColor = UIColor(hex: "5D5D5D")
38 | setTitleTextAttributes([.foregroundColor: UIColor.white], for: .normal)
39 | insertSubview(backgroundBlur, at: .zero)
40 | }
41 |
42 | override func layoutSubviews() {
43 | super.layoutSubviews()
44 | layer.masksToBounds = true
45 | layer.cornerRadius = bounds.height / 2
46 | backgroundBlur.frame = bounds
47 | sendSubviewToBack(backgroundBlur)
48 | }
49 |
50 | override func addSubview(_ view: UIView) {
51 | let newBounds = view.layer.bounds.inset(by: UIEdgeInsets(top: 6, left: 7, bottom: 0, right: 0))
52 |
53 | let path = UIBezierPath(roundedRect: newBounds, cornerRadius: newBounds.height / 2)
54 | let mask = CAShapeLayer()
55 | mask.path = path.cgPath
56 | view.layer.mask = mask
57 |
58 | super.addSubview(view)
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/BottomView/EditorBottomSizeEditor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EditorBottomSizeEditor.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 29/10/2022.
6 | //
7 |
8 | import UIKit
9 |
10 | class EditorBottomSizeEditor: UIView {
11 | var brushImageView: UIImageView?
12 | var sizeSlider: SizeSlider?
13 | var brushTypeButton: UIButton?
14 | var backButton: UIButton?
15 |
16 | var selectedBrush: Brush
17 | let viewModel: EditorBottomViewModel
18 |
19 | init(selectedBrush: Brush, viewModel: EditorBottomViewModel, frame: CGRect) {
20 | self.selectedBrush = selectedBrush
21 | self.viewModel = viewModel
22 | super.init(frame: frame)
23 | initialSetup()
24 | }
25 |
26 | required init?(coder: NSCoder) {
27 | fatalError("init(coder:) has not been implemented")
28 | }
29 |
30 | private func initialSetup() {
31 | brushImageView = UIImageView(frame: CGRect(origin: CGPoint(x: (bounds.width - 40) / 2 , y: .zero), size: CGSize(width: 40, height: 120)))
32 | brushImageView?.contentMode = .scaleAspectFit
33 | brushImageView?.image = selectedBrush.icon()
34 | addSubview(brushImageView!)
35 |
36 | sizeSlider = SizeSlider(frame: CGRect(origin: CGPoint(x: 46.5 , y: brushImageView!.frame.maxY), size: CGSize(width: bounds.width-134, height: 28)))
37 | sizeSlider?.value = selectedBrush.width
38 | sizeSlider?.addTarget(self, action: #selector(sliderValueChanged), for: .valueChanged)
39 | addSubview(sizeSlider!)
40 |
41 | }
42 |
43 | @objc private func sliderValueChanged(_ slider: UISlider) {
44 | print("slider value changed: \(slider.value)")
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/BottomView/SizeSlider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SizeSlider.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 26/10/2022.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | class SizeSlider: UISlider {
12 |
13 | override init(frame: CGRect) {
14 | super.init(frame: frame)
15 | initialSetup()
16 | }
17 |
18 | required init?(coder: NSCoder) {
19 | super.init(coder: coder)
20 | initialSetup()
21 | }
22 |
23 | private func initialSetup() {
24 | if let trackImage = UIImage.generateSizeSlider(isRotated: true) {
25 | self.setMaximumTrackImage(trackImage, for: .normal)
26 | self.setMinimumTrackImage(trackImage, for: .normal)
27 | }
28 |
29 | if let thumbImage = UIImage.generateSizeSliderThumb(size: CGSize(width: 32, height: 32)) {
30 | self.setThumbImage(thumbImage, for: .normal)
31 | }
32 | value = 0.5
33 | }
34 |
35 | override func trackRect(forBounds bounds: CGRect) -> CGRect {
36 | var result = super.trackRect(forBounds: bounds)
37 | result.size.height = 24
38 | result.origin = .zero
39 | return result
40 | }
41 |
42 | func runTransitionAnimation(from segmentedControl: EditorBottomSegementedControl) {
43 | layer.sublayers?.forEach({ $0.isHidden = true })
44 | }
45 |
46 | override func touchesBegan(_ touches: Set, with event: UIEvent?) {
47 | super.touchesBegan(touches, with: event)
48 |
49 | var sliderTransforms = self.transform
50 | sliderTransforms = sliderTransforms.concatenating(CGAffineTransform(translationX: 20, y: .zero))
51 | UIView.animate(withDuration: 0.2, delay: .zero) {
52 | self.transform = sliderTransforms
53 | }
54 | }
55 |
56 | override func touchesEnded(_ touches: Set, with event: UIEvent?) {
57 | super.touchesEnded(touches, with: event)
58 | var sliderTransforms = self.transform
59 | sliderTransforms = sliderTransforms.concatenating(CGAffineTransform(translationX: -20, y: .zero))
60 | UIView.animate(withDuration: 0.2, delay: .zero) {
61 | self.transform = sliderTransforms
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/DrawingViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DrawingViewController.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 29/04/2023.
6 | //
7 |
8 | import MetalKit
9 | import UIKit
10 |
11 | class DrawingViewController: UIViewController {
12 |
13 | private var drawingView: DrawingView? {
14 | return self.view as? DrawingView
15 | }
16 |
17 | private var editorViewModel = EditorBottomViewModel()
18 |
19 | private lazy var bottomView: EditorBottomView = {
20 | return EditorBottomView(viewModel: editorViewModel)
21 | }()
22 |
23 | private var renderer: Renderer?
24 |
25 | override func loadView() {
26 | super.loadView()
27 | self.view = DrawingView(frame: UIScreen.main.bounds, brush: editorViewModel.selectedBrush, device: MTLCreateSystemDefaultDevice())
28 | }
29 |
30 | override func viewDidLoad() {
31 | super.viewDidLoad()
32 | guard let drawingView else { return }
33 | renderer = Renderer(metalKitView: drawingView)
34 | view.addSubview(bottomView)
35 |
36 | editorViewModel.brushUpdated = { [weak self] brush in
37 | self?.drawingView?.selectedBrush = brush
38 | }
39 | bottomView.pinToSuperviewEdges(exclude: .top, respectingSafeArea: false)
40 | NSLayoutConstraint.activate([
41 | bottomView.heightAnchor.constraint(equalToConstant: 88 + 33 + 25),
42 | ])
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Extensions/CGImage + PixelColor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGImage + PixelColor.swift
3 | // telegram-contest
4 | //
5 | // Created by Bogdan Redkin on 29/10/2022.
6 | //
7 |
8 | import CoreGraphics
9 | import UIKit
10 |
11 | extension CGImage {
12 |
13 | func pixel(x: Int, y: Int) -> (r: Int, g: Int, b: Int)? {
14 | guard let pixelData = dataProvider?.data,
15 | let data = CFDataGetBytePtr(pixelData) else { return nil }
16 |
17 | let pixelInfo = ((width * y) + x ) * 4
18 |
19 | let red = Int(data[pixelInfo])
20 | let green = Int(data[(pixelInfo + 1)])
21 | let blue = Int(data[pixelInfo + 2])
22 |
23 | return (red, green, blue)
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Extensions/CGPoint + Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGPoint + Arithmetic.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 11/10/2022.
6 | //
7 |
8 | import Foundation
9 | import simd
10 |
11 | extension CGPoint {
12 | func interpolate(to: CGPoint, progress: CGFloat) -> CGPoint {
13 | guard progress >= 0.0 && progress <= 1.0 else { return self }
14 | let x = self.x.interpolate(to: to.x, progress: progress)
15 | let y = self.y.interpolate(to: to.y, progress: progress)
16 | return CGPoint(x: x, y: y)
17 | }
18 |
19 | init(vector: vector_uint2) {
20 | self.init(x: CGFloat(vector.x), y: CGFloat(vector.y))
21 | }
22 |
23 | init(vector: vector_float2) {
24 | self.init(x: vector.x.cgFloat, y: vector.y.cgFloat)
25 | }
26 |
27 | var vectorFloat: vector_float2 {
28 | return vector_float2(x: x.float, y: y.float)
29 | }
30 |
31 | var vectorUint: vector_uint2 {
32 | return vector_uint2(x: x.int32, y: y.int32)
33 | }
34 |
35 | // Arithmetic
36 |
37 | static func + (lhs: CGPoint, rhs: CGPoint) -> CGPoint {
38 | return CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
39 | }
40 |
41 | static func -(lhs: CGPoint, rhs: CGPoint) -> CGVector {
42 | return CGVector(dx: lhs.x - rhs.x, dy: lhs.y - rhs.y)
43 | }
44 |
45 | static func -(lhs: CGPoint, rhs: CGVector) -> CGPoint {
46 | return CGPoint(x: lhs.x - rhs.dx, y: lhs.y - rhs.dy)
47 | }
48 |
49 | static func *(lhs: CGPoint, rhs: CGFloat) -> CGPoint {
50 | return CGPoint(x: lhs.x * rhs, y: lhs.y * rhs)
51 | }
52 |
53 | static func *(lhs: CGFloat, rhs: CGPoint) -> CGPoint {
54 | return CGPoint(x: rhs.x * lhs, y: rhs.y * lhs)
55 | }
56 |
57 | func distanceToPoint(otherPoint: CGPoint) -> CGFloat {
58 | return sqrt(pow((otherPoint.x - x), 2) + pow((otherPoint.y - y), 2))
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Extensions/CGRect + Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGRect + Helpers.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 11/10/2022.
6 | //
7 |
8 | import Foundation
9 |
10 | extension CGRect {
11 |
12 | public init(size: CGSize) {
13 | self.init(origin: .zero, size: size)
14 | }
15 |
16 | func scale(_ factor: CGFloat) -> CGRect {
17 | let transform = CGAffineTransform(scaleX: factor, y: factor)
18 | return self.applying(transform)
19 | }
20 |
21 | func interpolate(to: CGRect, progress: CGFloat) -> CGRect {
22 | guard progress >= 0.0, progress <= 1.0 else { return self }
23 | let origin = origin.interpolate(to: to.origin, progress: progress)
24 | let size = size.interpolate(to: to.size, progress: progress)
25 | return CGRect(origin: origin, size: size)
26 | }
27 |
28 | var center: CGPoint {
29 | return CGPoint(x: midX, y: midY)
30 | }
31 |
32 | init(center: CGPoint, size: CGSize) {
33 | self.init(origin: CGPoint(x: center.x - size.width / 2, y: center.y - size.height / 2), size: size)
34 | }
35 |
36 | func withOrigin(_ newOrigin: CGPoint) -> CGRect {
37 | var newRect = self
38 | newRect.origin = newOrigin
39 | return newRect
40 | }
41 |
42 | func withX(_ newX: CGFloat) -> CGRect {
43 | return withOrigin(CGPoint(x: newX, y: minY))
44 | }
45 |
46 | func withY(_ newY: CGFloat) -> CGRect {
47 | return withOrigin(CGPoint(x: minX, y: newY))
48 | }
49 |
50 | func withSize(_ newValue: CGSize) -> CGRect {
51 | var newRect = self
52 | newRect.size = newValue
53 | return newRect
54 | }
55 |
56 | func withWidth(_ newWidth: CGFloat) -> CGRect {
57 | return withSize(CGSize(width: newWidth, height: height))
58 | }
59 |
60 |
61 | func withHeight(_ newHeight: CGFloat) -> CGRect {
62 | return withSize(CGSize(width: width, height: newHeight))
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Extensions/CGSize + Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGSize + Helpers.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 11/10/2022.
6 | //
7 |
8 | import Foundation
9 |
10 | extension CGSize {
11 |
12 | init(size: CGFloat) {
13 | self.init(width: size, height: size)
14 | }
15 |
16 | func interpolate(to: CGSize, progress: CGFloat) -> CGSize {
17 | guard progress >= 0.0 && progress <= 1.0 else { return self }
18 |
19 | let width = self.width.interpolate(to: to.width, progress: progress)
20 | let height = self.height.interpolate(to: to.height, progress: progress)
21 | return CGSize(width: width, height: height)
22 | }
23 |
24 | func scale(_ factor: CGFloat) -> CGSize {
25 | let transform = CGAffineTransform(scaleX: factor, y: factor)
26 | return self.applying(transform)
27 | }
28 |
29 | static func *(lhs: CGSize, rhs: CGSize) -> CGSize {
30 | return CGSize(width: lhs.width * rhs.width, height: lhs.height * rhs.height)
31 | }
32 |
33 | static func *(lhs: CGSize, rhs: CGFloat) -> CGSize {
34 | return CGSize(width: lhs.width * rhs, height: lhs.height * rhs)
35 | }
36 |
37 | static func *(lhs: CGFloat, rhs: CGSize) -> CGSize {
38 | return CGSize(width: rhs.width * lhs, height: rhs.height * lhs)
39 | }
40 |
41 | static func /(lhs: CGSize, rhs: CGSize) -> CGSize {
42 | return CGSize(width: lhs.width / rhs.width, height: lhs.height / rhs.height)
43 | }
44 |
45 | static func /(lhs: CGSize, rhs: CGFloat) -> CGSize {
46 | return CGSize(width: lhs.width / rhs, height: lhs.height / rhs)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Extensions/MTLTexture + Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MTLTexture + Helpers.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 20/06/2023.
6 | //
7 |
8 | import Metal
9 | import UIKit
10 | import simd
11 |
12 | extension MTLTexture {
13 | // func clear() {
14 | // let region = MTLRegion(
15 | // origin: MTLOrigin(x: 0, y: 0, z: 0),
16 | // size: MTLSize(width: width, height: height, depth: 1)
17 | // )
18 | // let bytesPerRow = 4 * width
19 | // let data = Data(capacity: Int(bytesPerRow * height))
20 | // if let bytes = data.withUnsafeBytes({ $0.baseAddress }) {
21 | // replace(region: region, mipmapLevel: 0, withBytes: bytes, bytesPerRow: bytesPerRow)
22 | // }
23 | // }
24 | }
25 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Extensions/Number+Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Number+Helpers.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 11/10/2022.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Int {
11 | func interpolate(to: Int, progress: CGFloat) -> Int {
12 | guard progress >= 0.0 && progress <= 1.0 else { return self }
13 | return self + Int(CGFloat(to - self) * progress)
14 | }
15 |
16 | var int32: UInt32 {
17 | return UInt32(self)
18 | }
19 |
20 | var float: Float {
21 | return Float(self)
22 | }
23 |
24 | var double: Double {
25 | return Double(self)
26 | }
27 |
28 | var cgFloat: CGFloat {
29 | return CGFloat(self)
30 | }
31 |
32 | var string: String {
33 | return String(describing: self)
34 | }
35 |
36 | static func extract(from string: String) -> Int? {
37 | return Int(string.components(separatedBy: CharacterSet.decimalDigits.inverted).joined())
38 | }
39 | }
40 |
41 | extension Float {
42 |
43 | var int: Int {
44 | return Int(self)
45 | }
46 |
47 | var int32: UInt32 {
48 | return UInt32(self)
49 | }
50 |
51 | var double: Double {
52 | return Double(self)
53 | }
54 |
55 | var cgFloat: CGFloat {
56 | return CGFloat(self)
57 | }
58 |
59 | var string: String {
60 | return String(describing: self)
61 | }
62 | }
63 |
64 | extension Double {
65 | var float: Float {
66 | return Float(self)
67 | }
68 |
69 | var int: Int {
70 | return Int(self)
71 | }
72 |
73 | var int32: UInt32 {
74 | return UInt32(self)
75 | }
76 |
77 | var cgFloat: CGFloat {
78 | return CGFloat(self)
79 | }
80 |
81 | var string: String {
82 | return String(describing: self)
83 | }
84 | }
85 |
86 | let π = CGFloat.pi
87 |
88 | extension CGFloat {
89 | func interpolate(to: CGFloat, progress: CGFloat) -> CGFloat {
90 | guard progress >= 0.0 && progress <= 1.0 else { return self }
91 | return self + (to - self) * progress
92 | }
93 |
94 | var int: Int {
95 | return Int(self)
96 | }
97 |
98 | var int32: UInt32 {
99 | return UInt32(self)
100 | }
101 |
102 | var double: Double {
103 | return Double(self)
104 | }
105 |
106 | var float: Float {
107 | return Float(self)
108 | }
109 |
110 | var string: String {
111 | return String(describing: self)
112 | }
113 | // Converts an angle in degrees to radians.
114 | func degreesToRadians() -> CGFloat {
115 |
116 | return π * self / 180.0
117 | }
118 |
119 | // Converts an angle in radians to degrees.
120 | func radiansToDegrees() -> CGFloat {
121 | return self * 180.0 / π
122 | }
123 |
124 | // Ensures that the float value stays between the given values, inclusive.
125 | func clamped(_ v1: CGFloat, _ v2: CGFloat) -> CGFloat {
126 | let min = v1 < v2 ? v1 : v2
127 | let max = v1 > v2 ? v1 : v2
128 | return self < min ? min : (self > max ? max : self)
129 | }
130 |
131 | // Ensures that the float value stays between the given values, inclusive.
132 | mutating func clamp(_ v1: CGFloat, _ v2: CGFloat) -> CGFloat {
133 | self = clamped(v1, v2)
134 | return self
135 | }
136 |
137 | // Returns 1.0 if a floating point value is positive; -1.0 if it is negative.
138 | func sign() -> CGFloat {
139 | return (self >= 0.0) ? 1.0 : -1.0
140 | }
141 |
142 | func rounded(symbolsAfterComma count: Int) -> CGFloat {
143 | let multiplier = pow(10, count.double).cgFloat
144 | let result = (self * multiplier).rounded() / multiplier
145 | return result
146 | }
147 | }
148 |
149 | extension UInt32 {
150 | var float: Float {
151 | return Float(self)
152 | }
153 |
154 | var int: Int {
155 | return Int(self)
156 | }
157 |
158 | var cgFloat: CGFloat {
159 | return CGFloat(self)
160 | }
161 |
162 | var string: String {
163 | return String(describing: self)
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Extensions/UIApplication + currentWindow.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIApplication + currentWindow.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 06/06/2023.
6 | //
7 |
8 | import UIKit
9 |
10 | extension UIApplication {
11 |
12 | var window: UIWindow? {
13 | // Get connected scenes
14 | let scene = UIApplication.shared.connectedScenes
15 | // Keep only active scenes, onscreen and visible to the user
16 | .filter { $0.activationState == .foregroundActive }
17 | // Keep only the first `UIWindowScene`
18 | .first(where: { $0 is UIWindowScene })
19 | // Get its associated windows
20 | .flatMap { $0 as? UIWindowScene }
21 |
22 | return scene?.keyWindow
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Extensions/UIColor + Utils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor + Hex.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 10/10/2022.
6 | //
7 |
8 | import UIKit
9 | import simd
10 |
11 | extension UIColor {
12 |
13 | public convenience init?(hex: String) {
14 | var hexInt: UInt64 = 0
15 | let scanner = Scanner(string: hex)
16 | scanner.charactersToBeSkipped = CharacterSet(charactersIn: "#")
17 | scanner.scanHexInt64(&hexInt)
18 |
19 | let red = CGFloat((hexInt & 0xFF0000) >> 16) / 255.0
20 | let green = CGFloat((hexInt & 0xFF00) >> 8) / 255.0
21 | let blue = CGFloat((hexInt & 0xFF) >> 0) / 255.0
22 |
23 | self.init(red: red, green: green, blue: blue, alpha: 1.0)
24 | }
25 |
26 | static var random: UIColor {
27 | return UIColor(red: .random(in: 0...1),
28 | green: .random(in: 0...1),
29 | blue: .random(in: 0...1),
30 | alpha: 1.0)
31 | }
32 |
33 | var hexString: String {
34 | let (r, g, b, _) = rgbaCGFloat
35 |
36 | let rgb:Int = (Int)(r*255)<<16 | (Int)(g*255)<<8 | (Int)(b*255)<<0
37 |
38 | return String(format:"#%06x", rgb)
39 | }
40 |
41 | typealias RGBA = (r: T, g: T, b: T, a: T)
42 |
43 | var rgbaCGFloat: RGBA {
44 | var r: CGFloat = .zero
45 | var g: CGFloat = .zero
46 | var b: CGFloat = .zero
47 | var a: CGFloat = .zero
48 | getRed(&r, green: &g, blue: &b, alpha: &a)
49 | return (r, g, b, a)
50 | }
51 |
52 | var rgbaDouble: RGBA {
53 | let (r,g,b,a) = rgbaCGFloat
54 | return (r.double, g.double, b.double, a.double)
55 | }
56 |
57 | var vectorFloat4: vector_float4 {
58 | let (r,g,b,a) = rgbaDouble
59 | return vector_float4(r.float, g.float, b.float, a.float)
60 | }
61 |
62 | var hsba: (h: CGFloat, s: CGFloat, b: CGFloat, a: CGFloat) {
63 | var h: CGFloat = .zero
64 | var s: CGFloat = .zero
65 | var b: CGFloat = .zero
66 | var a: CGFloat = .zero
67 | getHue(&h, saturation: &s, brightness: &b, alpha: &a)
68 | return (h, s, b, a)
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Extensions/UIControl + Combine.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIControl + Combine.swift
3 | // telegram-contest
4 | //
5 | // Created by Bogdan Redkin on 11/10/2022.
6 | //
7 |
8 | import Foundation
9 | import Combine
10 | import UIKit
11 |
12 | final class UIControlSubscription: Subscription where SubscriberType.Input == Control {
13 | private var subscriber: SubscriberType?
14 | private let control: Control
15 |
16 | init(subscriber: SubscriberType, control: Control, event: UIControl.Event) {
17 | self.subscriber = subscriber
18 | self.control = control
19 | control.addTarget(self, action: #selector(self.eventHandler), for: event)
20 | }
21 |
22 | func request(_ demand: Subscribers.Demand) { }
23 |
24 | func cancel() {
25 | self.subscriber = nil
26 | }
27 |
28 | @objc private func eventHandler() {
29 | _ = self.subscriber?.receive(self.control)
30 | }
31 | }
32 |
33 | struct UIControlPublisher: Publisher {
34 | typealias Output = Control
35 | typealias Failure = Never
36 |
37 | let control: Control
38 | let controlEvents: UIControl.Event
39 |
40 | init(control: Control, events: UIControl.Event) {
41 | self.control = control
42 | self.controlEvents = events
43 | }
44 |
45 | func receive(subscriber: S) where S: Subscriber, S.Failure == UIControlPublisher.Failure, S.Input == UIControlPublisher.Output {
46 | let subscription = UIControlSubscription(subscriber: subscriber, control: control, event: controlEvents)
47 | subscriber.receive(subscription: subscription)
48 | }
49 | }
50 |
51 | protocol CombineCompatible {}
52 | extension UIControl: CombineCompatible {}
53 | extension CombineCompatible where Self: UIControl {
54 | func publisher(for events: UIControl.Event) -> UIControlPublisher {
55 | return UIControlPublisher(control: self, events: events)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Extensions/UIView + Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIView + Helpers.swift
3 | // telegram-contest
4 | //
5 | // Created by Bogdan Redkin on 16/10/2022.
6 | //
7 |
8 | import UIKit
9 |
10 | extension UIView {
11 |
12 | func setAnchorPoint(_ point: CGPoint) {
13 | var newPoint = CGPoint(x: bounds.size.width * point.x, y: bounds.size.height * point.y)
14 | var oldPoint = CGPoint(x: bounds.size.width * layer.anchorPoint.x, y: bounds.size.height * layer.anchorPoint.y);
15 |
16 | newPoint = newPoint.applying(transform)
17 | oldPoint = oldPoint.applying(transform)
18 |
19 | var position = layer.position
20 |
21 | position.x -= oldPoint.x
22 | position.x += newPoint.x
23 |
24 | position.y -= oldPoint.y
25 | position.y += newPoint.y
26 |
27 | layer.position = position
28 | layer.anchorPoint = point
29 | }
30 |
31 | var rootSuperView: UIView? {
32 | if superview?.superview == nil {
33 | return superview
34 | } else {
35 | return superview?.rootSuperView
36 | }
37 | }
38 |
39 | var recursiveAllSubviews: [UIView] {
40 | subviews + subviews.flatMap { $0.recursiveAllSubviews }
41 | }
42 |
43 | func firstSubview(of type: T.Type) -> T? {
44 | recursiveAllSubviews.first { $0 is T } as? T
45 | }
46 |
47 | func findFirstResponder() -> UIView? {
48 | for subview in subviews {
49 | if subview.isFirstResponder {
50 | return subview
51 | }
52 |
53 | if let recursiveSubView = subview.findFirstResponder() {
54 | return recursiveSubView
55 | }
56 | }
57 |
58 | return nil
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Metal/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Metal/.DS_Store
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Metal/DrawingGestureRecognizer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DrawingGestureRecognizer.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 25/10/2022.
6 | //
7 |
8 | import UIKit
9 |
10 | class DrawingGestureRecognizer: UIPanGestureRecognizer {
11 |
12 | typealias touchesResponse = (touches: Set, event: UIEvent, velocity: CGPoint)
13 |
14 | var touchesBeganHandler: ((touchesResponse) -> Void)?
15 | var touchesMovedHandler: ((touchesResponse) -> Void)?
16 | var touchesEndedHandler: ((touchesResponse) -> Void)?
17 | var touchesCancelledHandler: ((touchesResponse) -> Void)?
18 |
19 | override func touchesBegan(_ touches: Set, with event: UIEvent) {
20 | let response: touchesResponse = (touches, event, velocity(in: view))
21 | touchesBeganHandler?(response)
22 | super.touchesBegan(touches, with: event)
23 | }
24 |
25 | override func touchesMoved(_ touches: Set, with event: UIEvent) {
26 | let response: touchesResponse = (touches, event, velocity(in: view))
27 | touchesMovedHandler?(response)
28 | super.touchesMoved(touches, with: event)
29 | }
30 |
31 | override func touchesEnded(_ touches: Set, with event: UIEvent) {
32 | let response: touchesResponse = (touches, event, velocity(in: view))
33 | touchesEndedHandler?(response)
34 | super.touchesEnded(touches, with: event)
35 | }
36 |
37 | override func touchesCancelled(_ touches: Set, with event: UIEvent) {
38 | let response: touchesResponse = (touches, event, velocity(in: view))
39 | touchesCancelledHandler?(response)
40 | super.touchesCancelled(touches, with: event)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Metal/Shader.metal:
--------------------------------------------------------------------------------
1 | //
2 | // Shader.metal
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 06/05/2023.
6 | //
7 |
8 | #include
9 | using namespace metal;
10 |
11 | struct PointVertex {
12 | float4 position [[position]];
13 | float4 color;
14 | float size [[point_size]];
15 | };
16 |
17 | vertex PointVertex pointShaderVertex(constant PointVertex *points [[ buffer(0) ]],
18 | uint vid [[ vertex_id ]])
19 | {
20 | PointVertex out = points[vid];
21 |
22 | float2 pos = float2(out.position.x, out.position.y);
23 | out.position = float4(pos, 0, 1);
24 | return out;
25 | };
26 |
27 | fragment half4 pointShaderFragment(PointVertex point_data [[ stage_in ]],
28 | float2 pointCoord [[ point_coord ]])
29 | {
30 | float dist = length(pointCoord - float2(0.5));
31 | float4 out_color = point_data.color;
32 | out_color.a = 1.0 - smoothstep(0.4, 0.5, dist);
33 | return half4(out_color);
34 | };
35 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Models/Brush.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Brush.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 17/10/2022.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | struct Brush {
12 |
13 | enum Style: String {
14 | case pen
15 | case brush
16 | case neon
17 | case pencil
18 | case eraser
19 | }
20 |
21 | let style: Style
22 | var width: Float
23 | var color: Color
24 |
25 | init(style: Style, width: Float? = nil, color: Color = Color(uiColor: .tintColor)) {
26 | self.style = style
27 | self.width = width ?? (style.maxWidth - style.minWidth)/2
28 | self.color = color
29 | }
30 |
31 | }
32 |
33 | extension Brush {
34 |
35 | var isAvailable: Bool {
36 | switch style {
37 | case .eraser, .pen: return true
38 | default: return false
39 | }
40 | }
41 |
42 | static func defaultBrushes() -> [Brush] {
43 | return [
44 | Brush(style: .pen),
45 | Brush(style: .brush),
46 | Brush(style: .neon),
47 | Brush(style: .pencil),
48 | Brush(style: .eraser)
49 | ]
50 | }
51 |
52 | func icon() -> UIImage? {
53 | return UIImage.generateToolIcon(name: style.rawValue, color: color.uiColor, height: width.cgFloat)
54 | }
55 |
56 |
57 | }
58 |
59 | extension Brush.Style {
60 | var contentScale: Float {
61 | return (UIApplication.shared.window?.rootViewController?.view.contentScaleFactor ?? 1.0).float
62 | }
63 |
64 | var maxWidth: Float {
65 | switch self {
66 | case .eraser:
67 | return 50 * contentScale
68 | default:
69 | return 40 * contentScale
70 | }
71 | }
72 |
73 | var minWidth: Float {
74 | switch self {
75 | case .eraser:
76 | return 25 * contentScale
77 | default:
78 | return 13 * contentScale
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Models/Color.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Color.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 20/10/2022.
6 | //
7 |
8 | import UIKit
9 |
10 | struct Color: Codable {
11 |
12 | var red: CGFloat
13 | var green: CGFloat
14 | var blue: CGFloat
15 | var alpha: CGFloat
16 |
17 | var hex: String {
18 | uiColor.hexString
19 | }
20 |
21 | var uiColor: UIColor {
22 | return UIColor(red: red, green: green, blue: blue, alpha: alpha)
23 | }
24 |
25 | var hue: CGFloat {
26 | get { uiColor.hsba.h }
27 | set {
28 | var (h,s,b,a) = uiColor.hsba
29 | h = newValue
30 | let (r, g, blue, _) = UIColor(hue: h, saturation: s, brightness: b, alpha: a).rgbaCGFloat
31 | self.red = r
32 | self.green = g
33 | self.blue = blue
34 | }
35 | }
36 |
37 | var saturation: CGFloat {
38 | get { uiColor.hsba.s }
39 | set {
40 | var (h,s,b,a) = uiColor.hsba
41 | s = newValue
42 | let (r, g, blue, _) = UIColor(hue: h, saturation: s, brightness: b, alpha: a).rgbaCGFloat
43 | self.red = r
44 | self.green = g
45 | self.blue = blue
46 | }
47 | }
48 |
49 | var brightness: CGFloat {
50 | get { uiColor.hsba.b }
51 | set {
52 | var (h, s, b, a) = uiColor.hsba
53 | b = newValue
54 | let (r, g, blue, _) = UIColor(hue: h, saturation: s, brightness: b, alpha: a).rgbaCGFloat
55 | self.red = r
56 | self.green = g
57 | self.blue = blue
58 | }
59 | }
60 |
61 | init(uiColor: UIColor) {
62 | let (r, g, b, a) = uiColor.rgbaCGFloat
63 | red = r
64 | green = g
65 | blue = b
66 | alpha = a
67 | }
68 |
69 | init(hue: CGFloat, saturation: CGFloat, brightness: CGFloat, alpha: CGFloat = 1) {
70 | let h = max(0, min(1, hue))
71 | let s = max(0, min(1, saturation))
72 | let b = max(0, min(1, brightness))
73 | let a = max(0, min(1, alpha))
74 | let uiColor = UIColor(hue: h, saturation: s, brightness: b, alpha: a)
75 | self.init(uiColor: uiColor)
76 | }
77 |
78 | init(hue: Int, saturation: Int, brightness: Int, alpha: CGFloat = 1) {
79 | self.init(hue: hue.cgFloat/359, saturation: saturation.cgFloat/100, brightness: brightness.cgFloat/100, alpha: alpha)
80 | }
81 |
82 | func save() {
83 | var array = Color.fetchSavedColors()
84 | array.append(self)
85 | if let encodedArray = try? JSONEncoder().encode(array),
86 | let string = String(data: encodedArray, encoding: .utf8) {
87 | UserDefaults.standard.set(string, forKey: "saved_colors")
88 | }
89 | }
90 |
91 | static func fetchSavedColors() -> [Color] {
92 | if let savedArray = UserDefaults.standard.string(forKey: "saved_colors"),
93 | let data = savedArray.data(using: .utf8),
94 | let decodedArray = try? JSONDecoder().decode([Color].self, from: data)
95 | {
96 | return decodedArray
97 | } else {
98 | return []
99 | }
100 | }
101 |
102 | static func defaultPalette() -> [Color] {
103 | return [
104 | UIColor.black,
105 | UIColor.systemBlue,
106 | UIColor.systemRed,
107 | UIColor.systemOrange
108 | ].map { Color(uiColor: $0) }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Models/Point.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Point.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 07/05/2023.
6 | //
7 |
8 | import Foundation
9 | import simd
10 | import UIKit
11 |
12 | struct TextureVertex {
13 | var position: vector_float2
14 | var texcoord: vector_float2
15 | }
16 |
17 | struct Point {
18 | var position: vector_float4
19 | var color: vector_float4
20 | var size: Float
21 |
22 | init(position: vector_float4, color: vector_float4, size: Float) {
23 | self.position = position
24 | self.color = color
25 | self.size = size
26 | }
27 |
28 | init(x: CGFloat, y: CGFloat, color: UIColor, size: CGFloat) {
29 | self.init(position: vector_float4(Float(x), Float(y), 0, 1), color: color.vectorFloat4, size: Float(size))
30 | }
31 |
32 | init(location: CGPoint, parentSize: CGSize, color: UIColor, size: CGFloat) {
33 | let roundedLocation = CGPoint(x: location.x.int.cgFloat, y: location.y.int.cgFloat)
34 | let xK = (1.0 / (parentSize.width / roundedLocation.x)).rounded(symbolsAfterComma: 5)
35 | let yK = (1.0 / (parentSize.height / roundedLocation.y)).rounded(symbolsAfterComma: 5)
36 | let x = xK == 0.5 ? 0.0 : (xK < 0.5 ? -(1 - xK / 0.5) : xK / 0.5 - 1)
37 | let y = yK == 0.5 ? 0.0 : (yK > 0.5 ? (1 - yK / 0.5) : 1 - (yK / 0.5))
38 | self.init(x: x, y: y, color: color, size: size)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 29/04/2023.
6 | //
7 |
8 | import UIKit
9 |
10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
11 |
12 | var window: UIWindow?
13 |
14 |
15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
16 | guard let windowScene = (scene as? UIWindowScene) else { return }
17 |
18 | let window = UIWindow(windowScene: windowScene)
19 | window.rootViewController = DrawingViewController()
20 | self.window = window
21 | window.makeKeyAndVisible()
22 | }
23 |
24 | func sceneDidDisconnect(_ scene: UIScene) {
25 | // Called as the scene is being released by the system.
26 | // This occurs shortly after the scene enters the background, or when its session is discarded.
27 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
28 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
29 | }
30 |
31 | func sceneDidBecomeActive(_ scene: UIScene) {
32 | // Called when the scene has moved from an inactive state to an active state.
33 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
34 | }
35 |
36 | func sceneWillResignActive(_ scene: UIScene) {
37 | // Called when the scene will move from an active state to an inactive state.
38 | // This may occur due to temporary interruptions (ex. an incoming phone call).
39 | }
40 |
41 | func sceneWillEnterForeground(_ scene: UIScene) {
42 | // Called as the scene transitions from the background to the foreground.
43 | // Use this method to undo the changes made on entering the background.
44 | }
45 |
46 | func sceneDidEnterBackground(_ scene: UIScene) {
47 | // Called as the scene transitions from the foreground to the background.
48 | // Use this method to save data, release shared resources, and store enough scene-specific state information
49 | // to restore the scene back to its current state.
50 | }
51 |
52 |
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/.DS_Store
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/blurEraser 1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "blurEraser.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/blurEraser 1.imageset/blurEraser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/blurEraser 1.imageset/blurEraser.png
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/blurEraser.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "blurEraser.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/blurEraser.imageset/blurEraser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/blurEraser.imageset/blurEraser.png
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/blurTip 1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "blurTip.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/blurTip 1.imageset/blurTip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/blurTip 1.imageset/blurTip.png
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/blurTip.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "blurTip.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/blurTip.imageset/blurTip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/blurTip.imageset/blurTip.png
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/brush 1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "brush.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/brush 1.imageset/brush.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/brush 1.imageset/brush.png
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/brush.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "brush.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/brush.imageset/brush.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/brush.imageset/brush.png
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/brush_tip 1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "brush.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/brush_tip 1.imageset/brush.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/brush_tip 1.imageset/brush.png
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/brush_tip.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "brush.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/brush_tip.imageset/brush.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/brush_tip.imageset/brush.png
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/close 1.imageset/Close.pdf:
--------------------------------------------------------------------------------
1 | %PDF-1.7
2 |
3 | 1 0 obj
4 | << /ExtGState << /E2 << /ca 0.600000 >>
5 | /E1 << /ca 0.240000 >>
6 | >> >>
7 | endobj
8 |
9 | 2 0 obj
10 | << /Length 3 0 R >>
11 | stream
12 | /DeviceRGB CS
13 | /DeviceRGB cs
14 | q
15 | /E1 gs
16 | 1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
17 | 0.462745 0.462745 0.501961 scn
18 | 30.000000 15.000000 m
19 | 30.000000 6.715729 23.284271 0.000000 15.000000 0.000000 c
20 | 6.715729 0.000000 0.000000 6.715729 0.000000 15.000000 c
21 | 0.000000 23.284271 6.715729 30.000000 15.000000 30.000000 c
22 | 23.284271 30.000000 30.000000 23.284271 30.000000 15.000000 c
23 | h
24 | f
25 | n
26 | Q
27 | q
28 | /E2 gs
29 | 1.000000 0.000000 -0.000000 1.000000 9.500000 9.304749 cm
30 | 0.921569 0.921569 0.960784 scn
31 | 0.292893 9.488144 m
32 | -0.097631 9.878669 -0.097631 10.511834 0.292893 10.902358 c
33 | 0.683417 11.292882 1.316583 11.292882 1.707107 10.902358 c
34 | 5.500000 7.109465 l
35 | 9.292893 10.902358 l
36 | 9.683417 11.292882 10.316583 11.292882 10.707107 10.902358 c
37 | 11.097631 10.511834 11.097631 9.878669 10.707107 9.488144 c
38 | 6.914213 5.695251 l
39 | 10.707107 1.902358 l
40 | 11.097631 1.511834 11.097631 0.878669 10.707107 0.488145 c
41 | 10.316583 0.097620 9.683417 0.097620 9.292893 0.488145 c
42 | 5.500000 4.281038 l
43 | 1.707107 0.488145 l
44 | 1.316583 0.097620 0.683418 0.097620 0.292893 0.488145 c
45 | -0.097631 0.878669 -0.097631 1.511834 0.292893 1.902358 c
46 | 4.085787 5.695251 l
47 | 0.292893 9.488144 l
48 | h
49 | f*
50 | n
51 | Q
52 |
53 | endstream
54 | endobj
55 |
56 | 3 0 obj
57 | 1153
58 | endobj
59 |
60 | 4 0 obj
61 | << /Annots []
62 | /Type /Page
63 | /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
64 | /Resources 1 0 R
65 | /Contents 2 0 R
66 | /Parent 5 0 R
67 | >>
68 | endobj
69 |
70 | 5 0 obj
71 | << /Kids [ 4 0 R ]
72 | /Count 1
73 | /Type /Pages
74 | >>
75 | endobj
76 |
77 | 6 0 obj
78 | << /Pages 5 0 R
79 | /Type /Catalog
80 | >>
81 | endobj
82 |
83 | xref
84 | 0 7
85 | 0000000000 65535 f
86 | 0000000010 00000 n
87 | 0000000132 00000 n
88 | 0000001341 00000 n
89 | 0000001364 00000 n
90 | 0000001537 00000 n
91 | 0000001611 00000 n
92 | trailer
93 | << /ID [ (some) (id) ]
94 | /Root 6 0 R
95 | /Size 7
96 | >>
97 | startxref
98 | 1670
99 | %%EOF
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/close 1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Close.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/close.imageset/Close.pdf:
--------------------------------------------------------------------------------
1 | %PDF-1.7
2 |
3 | 1 0 obj
4 | << /ExtGState << /E2 << /ca 0.600000 >>
5 | /E1 << /ca 0.240000 >>
6 | >> >>
7 | endobj
8 |
9 | 2 0 obj
10 | << /Length 3 0 R >>
11 | stream
12 | /DeviceRGB CS
13 | /DeviceRGB cs
14 | q
15 | /E1 gs
16 | 1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
17 | 0.462745 0.462745 0.501961 scn
18 | 30.000000 15.000000 m
19 | 30.000000 6.715729 23.284271 0.000000 15.000000 0.000000 c
20 | 6.715729 0.000000 0.000000 6.715729 0.000000 15.000000 c
21 | 0.000000 23.284271 6.715729 30.000000 15.000000 30.000000 c
22 | 23.284271 30.000000 30.000000 23.284271 30.000000 15.000000 c
23 | h
24 | f
25 | n
26 | Q
27 | q
28 | /E2 gs
29 | 1.000000 0.000000 -0.000000 1.000000 9.500000 9.304749 cm
30 | 0.921569 0.921569 0.960784 scn
31 | 0.292893 9.488144 m
32 | -0.097631 9.878669 -0.097631 10.511834 0.292893 10.902358 c
33 | 0.683417 11.292882 1.316583 11.292882 1.707107 10.902358 c
34 | 5.500000 7.109465 l
35 | 9.292893 10.902358 l
36 | 9.683417 11.292882 10.316583 11.292882 10.707107 10.902358 c
37 | 11.097631 10.511834 11.097631 9.878669 10.707107 9.488144 c
38 | 6.914213 5.695251 l
39 | 10.707107 1.902358 l
40 | 11.097631 1.511834 11.097631 0.878669 10.707107 0.488145 c
41 | 10.316583 0.097620 9.683417 0.097620 9.292893 0.488145 c
42 | 5.500000 4.281038 l
43 | 1.707107 0.488145 l
44 | 1.316583 0.097620 0.683418 0.097620 0.292893 0.488145 c
45 | -0.097631 0.878669 -0.097631 1.511834 0.292893 1.902358 c
46 | 4.085787 5.695251 l
47 | 0.292893 9.488144 l
48 | h
49 | f*
50 | n
51 | Q
52 |
53 | endstream
54 | endobj
55 |
56 | 3 0 obj
57 | 1153
58 | endobj
59 |
60 | 4 0 obj
61 | << /Annots []
62 | /Type /Page
63 | /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
64 | /Resources 1 0 R
65 | /Contents 2 0 R
66 | /Parent 5 0 R
67 | >>
68 | endobj
69 |
70 | 5 0 obj
71 | << /Kids [ 4 0 R ]
72 | /Count 1
73 | /Type /Pages
74 | >>
75 | endobj
76 |
77 | 6 0 obj
78 | << /Pages 5 0 R
79 | /Type /Catalog
80 | >>
81 | endobj
82 |
83 | xref
84 | 0 7
85 | 0000000000 65535 f
86 | 0000000010 00000 n
87 | 0000000132 00000 n
88 | 0000001341 00000 n
89 | 0000001364 00000 n
90 | 0000001537 00000 n
91 | 0000001611 00000 n
92 | trailer
93 | << /ID [ (some) (id) ]
94 | /Root 6 0 R
95 | /Size 7
96 | >>
97 | startxref
98 | 1670
99 | %%EOF
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/close.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Close.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/color_picker.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "color_picker_gradient.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/color_picker.imageset/color_picker_gradient.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/color_picker.imageset/color_picker_gradient.png
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/eraser.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "eraser.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/eraser.imageset/eraser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/eraser.imageset/eraser.png
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/neon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "neon.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/neon.imageset/neon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/neon.imageset/neon.png
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/neon_tip.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "neon.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/neon_tip.imageset/neon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/neon_tip.imageset/neon.png
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/objectEraser.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "objectEraser.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/objectEraser.imageset/objectEraser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/objectEraser.imageset/objectEraser.png
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/opacity_slider_background.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "VStack.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/opacity_slider_thumb.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Ellipse 26.pdf",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/opacity_slider_thumb.imageset/Ellipse 26.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/opacity_slider_thumb.imageset/Ellipse 26.pdf
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/pen.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "pen.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/pen.imageset/pen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/pen.imageset/pen.png
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/pen_tip.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "pen.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/pen_tip.imageset/pen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/pen_tip.imageset/pen.png
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/pencil.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "pencil.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/pencil.imageset/pencil.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/pencil.imageset/pencil.png
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/pencil_tip.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "pencil.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/pencil_tip.imageset/pencil.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/pencil_tip.imageset/pencil.png
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/roundTip.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "roundTip.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/roundTip.imageset/roundTip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/roundTip.imageset/roundTip.png
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/size_slslider.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Rectangle 53@2x.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/size_slslider.imageset/Rectangle 53@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aldammit/iOS-Metal-Drawing-tutorial/c58d3902adb3471215c17992519bce2df2216487/Draw_App_02_Starter/Draw App/Supporting Files/Assets.xcassets/size_slslider.imageset/Rectangle 53@2x.png
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/DrawingApp-BridgingHeader.h:
--------------------------------------------------------------------------------
1 | //
2 | // DrawingApp-BridgingHeader.h
3 | // Draw App
4 | //
5 | // Created by Bogdan Redkin on 29/04/2023.
6 | //
7 |
8 | #ifndef DrawingApp_BridgingHeader_h
9 | #define DrawingApp_BridgingHeader_h
10 |
11 | #endif /* DrawingApp_BridgingHeader_h */
12 |
--------------------------------------------------------------------------------
/Draw_App_02_Starter/Draw App/Supporting Files/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIApplicationSceneManifest
6 |
7 | UIApplicationSupportsMultipleScenes
8 |
9 | UISceneConfigurations
10 |
11 | UIWindowSceneSessionRoleApplication
12 |
13 |
14 | UISceneConfigurationName
15 | Default Configuration
16 | UISceneDelegateClassName
17 | $(PRODUCT_MODULE_NAME).SceneDelegate
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Bogdan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # iOS-Metal-Drawing-tutorial
--------------------------------------------------------------------------------