diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..af7a2b9 Binary files /dev/null and b/.DS_Store differ diff --git a/AnimatedPaths.xcodeproj/project.pbxproj b/AnimatedPaths.xcodeproj/project.pbxproj index f6a5bcf..bb6c994 100644 --- a/AnimatedPaths.xcodeproj/project.pbxproj +++ b/AnimatedPaths.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 009F0CBC1FC67E060096B0E3 /* RCAnimatedPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009F0CBB1FC67E060096B0E3 /* RCAnimatedPath.swift */; }; 00B299751C7C413400521D96 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00B299741C7C413400521D96 /* AppDelegate.swift */; }; 00B299771C7C413400521D96 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00B299761C7C413400521D96 /* ViewController.swift */; }; 00B2997A1C7C413400521D96 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 00B299781C7C413400521D96 /* Main.storyboard */; }; @@ -15,6 +16,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 009F0CBB1FC67E060096B0E3 /* RCAnimatedPath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RCAnimatedPath.swift; sourceTree = ""; }; 00B299711C7C413400521D96 /* AnimatedPaths.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AnimatedPaths.app; sourceTree = BUILT_PRODUCTS_DIR; }; 00B299741C7C413400521D96 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 00B299761C7C413400521D96 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -55,6 +57,7 @@ isa = PBXGroup; children = ( 00B299741C7C413400521D96 /* AppDelegate.swift */, + 009F0CBB1FC67E060096B0E3 /* RCAnimatedPath.swift */, 00B299761C7C413400521D96 /* ViewController.swift */, 00B299781C7C413400521D96 /* Main.storyboard */, 00B2997B1C7C413400521D96 /* Assets.xcassets */, @@ -98,7 +101,7 @@ CreatedOnToolsVersion = 7.2.1; DevelopmentTeam = W7YDEDGW4E; LastSwiftMigration = 0820; - ProvisioningStyle = Manual; + ProvisioningStyle = Automatic; }; }; }; @@ -140,6 +143,7 @@ files = ( 00B299771C7C413400521D96 /* ViewController.swift in Sources */, 00B299751C7C413400521D96 /* AppDelegate.swift in Sources */, + 009F0CBC1FC67E060096B0E3 /* RCAnimatedPath.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -252,14 +256,16 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = W7YDEDGW4E; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = AnimatedPaths/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.Robin.AnimatedPaths; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = "3a51cda8-3387-4c26-a945-c8fb51508664"; - PROVISIONING_PROFILE_SPECIFIER = "Development: Wildcard"; + PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 1; }; @@ -269,14 +275,16 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = W7YDEDGW4E; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = AnimatedPaths/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.Robin.AnimatedPaths; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = "3a51cda8-3387-4c26-a945-c8fb51508664"; - PROVISIONING_PROFILE_SPECIFIER = "Development: Wildcard"; + PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 3.0; TARGETED_DEVICE_FAMILY = 1; }; diff --git a/AnimatedPaths.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/AnimatedPaths.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/AnimatedPaths.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/AnimatedPaths/RCAnimatedPath.swift b/AnimatedPaths/RCAnimatedPath.swift new file mode 100644 index 0000000..dbf7806 --- /dev/null +++ b/AnimatedPaths/RCAnimatedPath.swift @@ -0,0 +1,355 @@ +// +// RCAnimatedPath.swift +// AnimatedPaths +// +// Created by Robin on 17/11/23. +// Copyright © 2017年 Robin. All rights reserved. +// + +import UIKit +import QuartzCore +import CoreText +import CoreGraphics + + + +class RCAnimatedPath +{ + //////////////////////// + // CREATE A SINGLETON // + + class var shared: RCAnimatedPath + { + struct Singleton + { + static let instance = RCAnimatedPath() + } + return Singleton.instance + } + + // CREATE A SINGLETON // + //////////////////////// + + + + var animationLayer: CALayer? + var pathLayer: CAShapeLayer? + + private var inputPath: CGPath! + private var inputDuration: CFTimeInterval = 10 + private var inputLineWidth: CGFloat = 10 + private var inputLineColor = UIColor.black + + private var inputRotationAngle: CGFloat = 0 + private var inputPolygonSidesNumber: Int = 6 + private var inputCornerRadius: Float = 8 + + private var inputText = "" + private var inputFontSize: CGFloat = 20 + private var inputFontName = "PingFangSC-Bold" + + + + func drawAnimatedCustomPath(in view: UIView, path: CGPath, duration: CFTimeInterval, lineWidth: CGFloat, lineColor: UIColor) + { + self.inputDuration = duration + self.inputLineWidth = lineWidth + self.inputLineColor = lineColor + self.inputPath = path + + animationLayer = CALayer() + + animationLayer?.frame = CGRect(x: 0, y: 0, width: view.layer.bounds.width, height: view.layer.bounds.height) + view.layer.addSublayer(animationLayer!) + view.clipsToBounds = true + + setupDrawingLayer() + startAnimation() + } + + func drawAnimatedRectanglePath(in view: UIView, duration: CFTimeInterval, lineWidth: CGFloat, lineColor: UIColor) + { + self.inputDuration = duration + self.inputLineWidth = lineWidth + self.inputLineColor = lineColor + + self.inputPath = rectanglePath(view: view) + + animationLayer = CALayer() + + animationLayer?.frame = CGRect(x: 0, y: 0, width: view.layer.bounds.width, height: view.layer.bounds.height) + view.layer.addSublayer(animationLayer!) + view.clipsToBounds = true + + setupDrawingLayer() + startAnimation() + } + + func drawAnimatedPolygonPath(in view: UIView, numberOfSides polygonSidesNumber: Int?, rotationAngle: CGFloat?, polygonCornerRadius: Float?, duration: CFTimeInterval, lineWidth: CGFloat, lineColor: UIColor) + { + self.inputDuration = duration + self.inputLineWidth = lineWidth + self.inputLineColor = lineColor + if let _rotationAngle = rotationAngle + { + self.inputRotationAngle = _rotationAngle + } + if let _polygonSidesNumber = polygonSidesNumber + { + self.inputPolygonSidesNumber = _polygonSidesNumber + } + if let _polygonCornerRadius = polygonCornerRadius + { + self.inputCornerRadius = _polygonCornerRadius + } + + self.inputPath = polygonPath(view: view) + + + animationLayer = CALayer() + animationLayer?.frame = CGRect(x: 0, y: 0, width: view.layer.bounds.width, height: view.layer.bounds.height) + view.layer.addSublayer(animationLayer!) + + setupDrawingLayer() + startAnimation() + } + + func drawAnimatedText(in view: UIView, with text: String, duration: CFTimeInterval, lineWidth: CGFloat, textColor: UIColor, fontName: String?, fontSize: CGFloat?) + { + self.inputText = text + self.inputDuration = duration + self.inputLineWidth = lineWidth + self.inputLineColor = textColor + if let _fontSize = fontSize + { + self.inputFontSize = _fontSize + } + if let _fontName = fontName + { + self.inputFontName = _fontName + } + + animationLayer = CALayer() + animationLayer?.frame = CGRect(x: 0, y: 0, width: view.layer.bounds.width, height: view.layer.bounds.height) + view.layer.addSublayer(animationLayer!) + + setupTextLayer(in: view) + startAnimation() + } + + + func setupDrawingLayer() + { + clearLayer() + + if let _ = animationLayer + { + let pathRect: CGRect = animationLayer!.bounds + + + let pathShapeLayer = CAShapeLayer() + pathShapeLayer.frame = animationLayer!.bounds + pathShapeLayer.bounds = pathRect + pathShapeLayer.isGeometryFlipped = true + pathShapeLayer.path = inputPath + pathShapeLayer.strokeColor = inputLineColor.cgColor + pathShapeLayer.fillColor = nil + pathShapeLayer.lineWidth = inputLineWidth + pathShapeLayer.lineJoin = kCALineJoinBevel + + animationLayer!.addSublayer(pathShapeLayer) + + pathLayer = pathShapeLayer + } + } + + func setupTextLayer(in view: UIView) + { + clearLayer() + + if let _ = animationLayer + { + let font = CTFontCreateWithName(inputFontName as CFString?, inputFontSize, nil) + let attrStr = NSAttributedString(string: inputText, attributes: [kCTFontAttributeName as String: font]) + let line = CTLineCreateWithAttributedString(attrStr) + let runArray = CTLineGetGlyphRuns(line) + + let letters = CGMutablePath() + + for runIndex in 0.. CGPath + { + //start point - left-down corner + var point = CGPoint(x: inputLineWidth/2, y: 0) + let path = UIBezierPath() + path.move(to: point) + //left-up corner + point = CGPoint(x: inputLineWidth/2, y: view.frame.height) + path.addLine(to: point) + path.move(to: CGPoint(x: inputLineWidth/2, y: view.frame.height - inputLineWidth/2)) + //right-up corner + point = CGPoint(x: view.frame.width, y: view.frame.height - inputLineWidth/2) + path.addLine(to: point) + path.move(to: CGPoint(x: view.frame.width - inputLineWidth/2, y: view.frame.height - inputLineWidth/2)) + //right-down corner + point = CGPoint(x: view.frame.width - inputLineWidth/2, y: 0) + path.addLine(to: point) + path.move(to: CGPoint(x: view.frame.width - inputLineWidth/2, y: inputLineWidth/2)) + //start point - left-down corner + point = CGPoint(x: 0, y: inputLineWidth/2) + path.addLine(to: point) + + path.close() + + return path.cgPath + } + + private func polygonPath(view: UIView) -> CGPath + { + let path = UIBezierPath() + + let theta = Float(2.0 * .pi) / Float(inputPolygonSidesNumber) + let offset = inputCornerRadius * tanf(theta / 2.0) + let squareWidth = Float(min(view.frame.size.width, view.frame.size.height)) + + var length = squareWidth - Float(inputLineWidth) + + if inputPolygonSidesNumber % 4 != 0 + { + length = length * cosf(theta / 2.0) + offset / 2.0 + } + + let sideLength = length * tanf(theta / 2.0) + + var point = CGPoint(x: CGFloat((squareWidth / 2.0) + (sideLength / 2.0) - offset), y: CGFloat(squareWidth - (squareWidth - length) / 2.0)) + var angle = Float(Double.pi) + path.move(to: point) + + for _ in 0 ..< inputPolygonSidesNumber + { + + let x = Float(point.x) + (sideLength - offset * 2.0) * cosf(angle) + let y = Float(point.y) + (sideLength - offset * 2.0) * sinf(angle) + + point = CGPoint(x: CGFloat(x), y: CGFloat(y)) + path.addLine(to: point) + + let centerX = Float(point.x) + inputCornerRadius * cosf(angle + Float(Double.pi/2)) + let centerY = Float(point.y) + inputCornerRadius * sinf(angle + Float(Double.pi/2)) + + let center = CGPoint(x: CGFloat(centerX), y: CGFloat(centerY)) + + let startAngle = CGFloat(angle) - CGFloat(Double.pi/2) + let endAngle = CGFloat(angle) + CGFloat(theta) - CGFloat(Double.pi/2) + + path.addArc(withCenter: center, radius: CGFloat(inputCornerRadius), startAngle: startAngle, endAngle: endAngle, clockwise: true) + + point = path.currentPoint + angle += theta + } + + path.close() + + + // ROTATE + //get the center and transform the path so it's centered at the origin + let bounds = path.cgPath.boundingBox + let center = CGPoint(x:bounds.midX, y:bounds.midY) + + let toOrigin = CGAffineTransform(translationX: -center.x, y: -center.y) + path.apply(toOrigin) + + //rotate around the origin + let rotAngle = inputRotationAngle * CGFloat(Double.pi) / 180 + let rotation = CGAffineTransform(rotationAngle: CGFloat(rotAngle)) + path.apply(rotation) + + //translate back to the origin + let fromOrigin = CGAffineTransform(translationX: center.x, y: center.y) + path.apply(fromOrigin) + + return path.cgPath + } + +}//end of class diff --git a/AnimatedPaths/ViewController.swift b/AnimatedPaths/ViewController.swift index 220261e..9f47f15 100644 --- a/AnimatedPaths/ViewController.swift +++ b/AnimatedPaths/ViewController.swift @@ -21,12 +21,25 @@ class ViewController: UIViewController, CAAnimationDelegate { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. - animationLayer = CALayer() - animationLayer?.frame = CGRect(x: 20.0, y: 40.0, width: self.view.layer.bounds.width - 40.0, height: self.view.layer.bounds.height - 84.0) - self.view.layer.addSublayer(animationLayer!) + let targetView: UIView = self.view - setupDrawingLayer() - startAnimation() + RCAnimatedPath().drawAnimatedRectanglePath(in: targetView, duration: 10, lineWidth: 20, lineColor: UIColor.red) + + RCAnimatedPath().drawAnimatedPolygonPath(in: targetView, numberOfSides: 8, rotationAngle: 30, polygonCornerRadius: 8, duration: 10, lineWidth: 5, lineColor: UIColor.gray) + + RCAnimatedPath().drawAnimatedText(in: targetView, with: "K E G", duration: 10, lineWidth: 2, textColor: UIColor.blue, fontName: "anyFontName", fontSize: 50) + +// RCAnimatedPath().drawAnimatedCustomPath(in: targetView, path: myPath, duration: 15, lineWidth: 5, lineColor: UIColor.blue) + + RCAnimatedPath.shared.drawAnimatedRectanglePath(in: targetView, duration: 10, lineWidth: 20, lineColor: UIColor.red) + + +// animationLayer = CALayer() +// animationLayer?.frame = CGRect(x: 20.0, y: 40.0, width: self.view.layer.bounds.width - 40.0, height: self.view.layer.bounds.height - 84.0) +// self.view.layer.addSublayer(animationLayer!) +// +// setupDrawingLayer() +// startAnimation() } func clearLayer() { @@ -96,8 +109,8 @@ class ViewController: UIViewController, CAAnimationDelegate { func setupTextLayer() { clearLayer() - let font = CTFontCreateWithName("STHeitiSC-Light" as CFString, 120, nil) - let attrStr = NSAttributedString(string: "你好Swift", attributes: [kCTFontAttributeName as String: font]) + let font = CTFontCreateWithName("PingFangSC-Light" as CFString, 120, nil) + let attrStr = NSAttributedString(string: "Hello Swift", attributes: [kCTFontAttributeName as String: font]) let line = CTLineCreateWithAttributedString(attrStr) let runArray = CTLineGetGlyphRuns(line)