MaLiang is a painting framework based on Metal. It supports drawing and handwriting with customized textures. The name of "MaLiang" comes from a boy who had a magical brush in Chinese ancient fairy story.
☕️ If I have saved your time, buy me a cup of coffee
📱 App based on MaLiang is now avaliable on the App Store
- Lines with Bezier Curve
- Texture Rotation
- Glowing Lines
- Chartlet element (for image and any other content from an UIView snapshot)
- Force based Adjustment for stroke size
- Pencil supported
- 3D Touch supported
- Undo & Redo
- Zoom & Scale
- Export to image
- Save vector contents to disk
- support macOS Catalyst
iOS 9.0, Swift 5
The core painting module is based on Metal
You can simply make it compatible with lower version of iOS and swift by changing only serval lines of code.
MaLiang is available through CocoaPods. To install
it, simply add the following line to your Podfile
:
pod 'MaLiang'
To use the old OpenGL ES verion:
pod 'MaLiang', '~> 1.1'
To integrate MaLiang into your Xcode project using Carthage, specify it in your Cartfile
:
github "Harley-xk/MaLiang"
Run carthage update
to build the framework and drag the built MaLiang.framework
into your Xcode project.
Make sure to add MaLiang.framework
to your target's Embedded Binaries
MaLiang is simple to use.
- import MaLiang
- enjoy painting!
open class Canvas: MetalView
A Canvas
is the basic component of MaLiang
. You will paint all things on it.
Canvas
extends from MetalView
, whitch extends from MTKView
. MetalView
handles all the logic with MetalKit and hides them from you.
Canvas
can be simply created with xib or code.
- with xib or storyboard, simply drag and drop an
UIView
object into your view controller and change it's class toCanvas
and module toMaLiang
- with code, just create with
init(frame:)
as anyUIView
you do before.
Now, all things necessary is done!
You can take snapshot on canvas now. Just call snapshot
function on Canvas
and you will get an optional UIImage
object.
With all things done, you can do more with Brush
!
Brush
is the key feature to MaLiang
. It holds textures and colors, whitch makes it possiable to paint amazing things.
Register a Brush
with image data or file to Canvas and paint with it:
let path = Bundle.main.path(forResource: "pencil", ofType: "png")!
let pencil = try? canvas.registerBrush(with: URL(fileURLWithPath: path))
pencil?.use()
Brush
have serval properties for you to custmize:
// opacity of texture, affects the darkness of stroke
// set opacity to 1 may cause heavy aliasing
open var opacity: CGFloat = 0.3
// width of stroke line in points
open var pointSize: CGFloat = 4
// this property defines the minimum distance (measureed in points) of nearest two textures
// defaults to 1, this means erery texture calculated will be rendered, dictance calculation will be skiped
open var pointStep: CGFloat = 1
// sensitive of pointsize changed from force, from 0 - 1
open var forceSensitive: CGFloat = 0
/// color of stroke
open var color: UIColor = .black
// indicate if the stroke size in visual will be scaled along with the Canvas
// defaults to false, the stroke size in visual will stay with the original value
open var scaleWithCanvas = false
With all these properties, you can create you own brush as your imagination.
MaLiang supports automatically adjustment of stroke size with painting force. 3D Touch is supported by default, and simulated force will be setup on devices those are not supporting this.
forceSensitive
is the property that force affects the storke size. It should be set between 0
to 1
. the smaller the value is, the less sensitive will be. if sets to 0
, then force will not affects the stroke size.
Chartlet elements are supported from 2.1.0. A chartlet must be registered to canvas with its' texture data. You can simply get image data from its' pngData()
method.
let data = UIImage(named: "chartlet").pngData()
let texture = try canvas.makeTexture(with: data)
You can apply rotation to chartlet by passing a counter clockwise angle in radius when adding it to the canvas:
canvas.renderChartlet(at: location, size: chartletSize, textureID: texture.id, rotation: angle)
Text element can be rendered to canvas by the Chartlet feature. MaLiang leaves the work of text layout and styles to your self.
- Firtst, put your text content to a label, a text view or any other customized views
- Second, make it properly styled and layouted
- Then Third, take a snapshot from that view.
- Finally, now you shoud have an image to your text content, render this image to canvas using the Chartlet apis.
Refer to the samples for more details.
CanvasData
is now configured by default. It holds all the data on the Canvas
, and makes the undo and redo actions to be possiable.
And you can implement your own saving logic with the data holds by CanvasData
.
🎉 You can save your paintings to disk now.
// 1. create an instance of `DataExporter` with your canvas:
let exporter = DataExporter(canvas: canvas)
// 2. save to empty folders on disk:
exporter.save(to: localPath, progress: progressHandler, result: resultHandler)
// also you can use another synchronous method to do this work Synchronously
exporter.saveSynchronously(to: locakPath, progress: progressHandler)
Then, contents of canvas and some document infomations will be saved to files in the directory you provided.
MaLiang
does not zip the folders, you can implement your own archive Logics refer to the sample project
Use DataImporter
to read data saved by MaLiang
to your canvas:
DataImporter.importData(from: localPath, to: canvas, progress: progressHandler, result: resultHandler)
Also, the localPath passed into DataImporter must be a folder where your contents files place. If you are using your own archive logic, unzip the contents first by your own.
MaLiang is available under the MIT license. See the LICENSE file for more info.