0 本文的程序例子
先說說代碼例子,文章最后面有個程序,直接將代碼拷貝到新建的playground文件中,就可以這樣展示和調試了
普通的代碼編寫過程中直接就在右欄中顯示執行結果,點擊執行結果右邊有兩個小圖標,一個眼睛的圖標,可以直觀看到圖形或數值(當前狀態),另外一個是+號,可以回溯歷史數據和變量之間的相關性。 點擊+號就可以看到上面的蹺蹺板和變量執行圖,還有右下角的時間軸,可以手動拖放回滾。
1、什么是playground?playground是Xcode的新的調試程序的一個工具,它提供靈活的數據展示方式,彌補了我們之前調試程序的手段的不足之處,它支持QuickLook多樣式調試顯示,不用添加測試代碼、也不用按Run執行程序,就可以直觀地查看運行情況,實時查看變量,可以直接查看的類型有:Color類型、String類型、Image類型、View類型、數據等等,可以自已開發動態展示的代碼(實現接口),使用XCPCaptureValue函數觀察和回溯動態過程。
有什么情況不用playground ?游樂場雖好,但畢竟還是游樂場,目前它不支持界面交互,也就是說暫時無法在Playground上玩你實現的游戲。只能看不能動,還有就是無法直接執行你的App程序,畢竟它不是模擬器。
2、playground有什么好處呢?從學習的用途上來說,它便於練習Swift,對初學編程的人來講更是有好處,不用整天按F6,F7,也不用等虛擬機運行程序看結果。對熟悉的開發者來講,它便於調試核心算法,測試一些涉及繪制的程序、圖像處理等一些又要看得見又不太方便用其他測試代碼做到的
對使用開發環境的人來說,Playground有利於學習和嘗試各種API,因為你不用為此設立項目,帶着一個文件就可以到處跑
3、 用個例子說明playground怎么用,我們直接上代碼吧,我從WWDC2014會上展示的內容好象沒有找到代碼下載,所以先從視頻上抄下一些代碼,動手測試一下。這些代碼只要直接拷到playground里面去就可以了,我們只要做幾個事情
a、在文件頭引入 import XCPlayground,用於下文實現playground的一些接口
b、實現func XCPCaptureValue<T>(identifier: String, value: T)用於顯示程序執行過程中的歷史數據,你可以用時間軸回滾,同時也看到變量與變量之間的關系。
c、實現func XCPShowView(identifier: String, view: NSView),直接顯示程序的動態執行過程,兩個小孩在蹺蹺板上玩,然后你看到蹺蹺板變角度變量的歷史過程和實現手動操作回滾
代碼如下:
import Cocoa
import QuartzCore
import XCPlayground
class PlaygroundIconView:NSView {
let backgroundLayer=CAShapeLayer()
let seesawBaseLayer=CAShapeLayer()
let seesawLayer=CAShapeLayer()
init(){
super.init(frame:NSRect(x:0,y:0,width:568,height:568))
backgroundLayer.frame=self.bounds
seesawBaseLayer.frame=NSRect(x:254,y:124,width:60,height:111)
seesawLayer.frame=NSRect(x:40,y:197, width:488,height:30)
setUpBackgroundLayer()
setUpSeesawBaseLayer()
setUpSeesawLayer()
self.wantsLayer=true
self.layer.addSublayer(backgroundLayer)
self.layer.addSublayer(seesawBaseLayer)
self.layer.addSublayer(seesawLayer)
}
func setUpBackgroundLayer(){
let lineWidth=9.0
let backgroundPath=NSBezierPath(roundedRect:NSInsetRect(bounds, lineWidth/2, lineWidth/2),xRadius:35.0,yRadius:35.0)
backgroundPath.lineWidth=lineWidth
backgroundLayer.strokeColor=NSColor.playgroundIconStrokeColor().CGColor
backgroundLayer.fillColor=NSColor.playgroundIconFillColoer().CGColor
backgroundLayer.lineWidth=lineWidth
backgroundLayer.path=CGPathFromNSBezierPath(backgroundPath)
}
func setUpSeesawBaseLayer(){
let seesawBasePath=NSBezierPath()
let rectHeight:Int=50;
seesawBasePath.moveToPoint(NSPoint(x:0,y:rectHeight))
seesawBasePath.lineToPoint(NSPoint(x:seesawBaseLayer.bounds.width/2,y:seesawBaseLayer.bounds.height))
seesawBasePath.lineToPoint(NSPoint(x:seesawBaseLayer.bounds.width,y:50))
seesawBaseLayer.fillColor=NSColor.whiteColor().CGColor
seesawBaseLayer.path=CGPathFromNSBezierPath(seesawBasePath)
}
func setUpSeesawLayer(){
let createChildLayer:()->CAShapeLayer={
let childLayer=CAShapeLayer()
let headPath=NSBezierPath(ovalInRect:NSRect(x:60,y:150,width:49,height:49))
let bodyPath=NSBezierPath()
bodyPath.moveToPoint(NSPoint(x:58,y:155))
bodyPath.lineToPoint(NSPoint(x:88,y:140))
bodyPath.lineToPoint(NSPoint(x:126,y:100))
bodyPath.lineToPoint(NSPoint(x:120,y:90))
bodyPath.lineToPoint(NSPoint(x:125,y:71))
bodyPath.lineToPoint(NSPoint(x:113,y:71))
bodyPath.lineToPoint(NSPoint(x:112,y:94))
bodyPath.lineToPoint(NSPoint(x:83,y:113))
bodyPath.lineToPoint(NSPoint(x:68,y:94))
bodyPath.lineToPoint(NSPoint(x:97,y:70))
bodyPath.lineToPoint(NSPoint(x:122,y:12))
bodyPath.lineToPoint(NSPoint(x:98,y:0))
bodyPath.lineToPoint(NSPoint(x:64,y:41))
bodyPath.lineToPoint(NSPoint(x:7,y:71))
bodyPath.lineToPoint(NSPoint(x:0,y:94))
bodyPath.moveToPoint(NSPoint(x:58,y:155))
let childPath=NSBezierPath()
childPath.appendBezierPath(headPath)
childPath.appendBezierPath(bodyPath)
childLayer.fillColor=NSColor.whiteColor().CGColor
childLayer.path=CGPathFromNSBezierPath(childPath)
return childLayer
}
let leftChildLayer = createChildLayer()
let rightChildLayer = createChildLayer()
rightChildLayer.transform=CATransform3DMakeRotation(M_PI,0.0,0.0,1.0)
rightChildLayer.geometryFlipped=true
let benchLayer = CALayer()
benchLayer.frame=NSRect(x:0,y:41,width:self.seesawLayer.bounds.width,height:30)
benchLayer.backgroundColor=NSColor.whiteColor().CGColor
leftChildLayer.frame=NSRect(x:25,y:0,width:126,height:200)
rightChildLayer.frame=NSRect(x:488-(126+25),y:0,width:126,height:200)
seesawLayer.addSublayer(leftChildLayer)
seesawLayer.addSublayer(rightChildLayer)
seesawLayer.addSublayer(benchLayer)
seesawLayer.delegate=self
}
let maxSeesawAngle=M_PI / 12
var currentSeesawAngle = 0.0
var animate:Bool = false{
didSet(oldAnimate){
if animate != oldAnimate && animate {
if currentSeesawAngle == 0 {
//@Bailey
//設置捕捉動態記錄和顯示的參數
XCPCaptureValue("Left Seesaw Position",0 )
animateSeesawToAngle(maxSeesawAngle,duration: 0.75)
}
else
{
animateSeesawToAngle(currentSeesawAngle * -1)
}
}
}
}
func animateSeesawToAngle(angle:CGFloat,duration:CFTimeInterval = 1.5 )-> CAAnimation{
let angleAnimation = CABasicAnimation(keyPath:"transform")
angleAnimation.fromValue=NSValue(CATransform3D:seesawLayer.transform)
angleAnimation.toValue=NSValue(CATransform3D:CATransform3DMakeRotation(angle, 0.0, 0.0, 1.0))
angleAnimation.timingFunction = CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseInEaseOut)
angleAnimation.duration = duration
angleAnimation.delegate=self
seesawLayer.addAnimation(
angleAnimation, forKey: "transform")
seesawLayer.transform=CATransform3DMakeRotation(angle,0.0, 0.0, 1.0)
currentSeesawAngle=angle
return angleAnimation
}
override func animationDidStop(_:CAAnimation!,finished:Bool){
if finished && animate {
//@Bailey
//設置捕捉動態記錄和顯示的參數
XCPCaptureValue("Left Seesaw Position",-currentSeesawAngle )
animateSeesawToAngle(currentSeesawAngle * -1)
}
}
}
extension NSColor {
class func playgroundIconFillColoer()->NSColor{
return NSColor(red:12/255,green:65/255,blue:135/255,alpha:1.0)
}
class func playgroundIconStrokeColor()->NSColor{
return NSColor(red:9/255,green:44/255,blue:91/255,alpha:1.0)
}
}
func CGPathFromNSBezierPath(nsPath:NSBezierPath)->CGPath! {
if nsPath.elementCount==0{
return nil
}
let path=CGPathCreateMutable()
var didClosePath=false
for i in 0..nsPath.elementCount{
var points=NSPoint[](count:3,repeatedValue:NSZeroPoint)
switch nsPath.elementAtIndex(i, associatedPoints: &points){
case .MoveToBezierPathElement:
CGPathMoveToPoint(path,nil,points[0].x,points[0].y)
case .LineToBezierPathElement:
CGPathAddLineToPoint(path, nil, points[0].x, points[0].y)
case .CurveToBezierPathElement:
CGPathAddCurveToPoint(path,nil, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y)
case .ClosePathBezierPathElement:
CGPathCloseSubpath(path)
didClosePath=true
}
}
if !didClosePath{
CGPathCloseSubpath(path)
}
return CGPathCreateCopy(path)
}
let view=PlaygroundIconView()
view.animate=true
//@Bailey
//顯示游樂場蹺蹺板動態圖標以及時間軸用於程序計算回溯
XCPShowView("20140605",view)
view
//修正程序以適應新swift的語法 ,在 xcode 6.1 下運行
import Cocoa
import Foundation
import QuartzCore
import XCPlayground
import SpriteKit
class PlaygroundIconView:NSView {
let backgroundLayer=CAShapeLayer()
let seesawBaseLayer=CAShapeLayer()
let seesawLayer=CAShapeLayer()
required init?(coder: NSCoder){
super.init(frame:NSRect(x:0,y:0,width:568,height:568))
backgroundLayer.frame=self.bounds
seesawBaseLayer.frame=NSRect(x:254,y:124,width:60,height:111)
seesawLayer.frame=NSRect(x:40,y:197, width:488,height:30)
setUpBackgroundLayer()
setUpSeesawBaseLayer()
setUpSeesawLayer()
self.wantsLayer=true
self.layer?.addSublayer(backgroundLayer)
self.layer?.addSublayer(seesawBaseLayer)
self.layer?.addSublayer(seesawLayer)
}
func setUpBackgroundLayer(){
let lineWidth=9.0
let backgroundPath=NSBezierPath(roundedRect:NSInsetRect(bounds, CGFloat(lineWidth/2), CGFloat(lineWidth/2)),xRadius:35.0,yRadius:35.0)
backgroundPath.lineWidth=CGFloat(lineWidth)
backgroundLayer.strokeColor=NSColor.playgroundIconStrokeColor().CGColor
backgroundLayer.fillColor=NSColor.playgroundIconFillColoer().CGColor
backgroundLayer.lineWidth=CGFloat(lineWidth)
backgroundLayer.path=CGPathFromNSBezierPath(backgroundPath)
}
func setUpSeesawBaseLayer(){
let seesawBasePath=NSBezierPath()
let rectHeight:Int=50;
seesawBasePath.moveToPoint(NSPoint(x:0,y:rectHeight))
seesawBasePath.lineToPoint(NSPoint(x:seesawBaseLayer.bounds.width/2,y:seesawBaseLayer.bounds.height))
seesawBasePath.lineToPoint(NSPoint(x:seesawBaseLayer.bounds.width,y:50))
seesawBaseLayer.fillColor=NSColor.whiteColor().CGColor
seesawBaseLayer.path=CGPathFromNSBezierPath(seesawBasePath)
}
func setUpSeesawLayer(){
let createChildLayer:()->CAShapeLayer={
let childLayer=CAShapeLayer()
let headPath=NSBezierPath(ovalInRect:NSRect(x:60,y:150,width:49,height:49))
let bodyPath=NSBezierPath()
bodyPath.moveToPoint(NSPoint(x:58,y:155))
bodyPath.lineToPoint(NSPoint(x:88,y:140))
bodyPath.lineToPoint(NSPoint(x:126,y:100))
bodyPath.lineToPoint(NSPoint(x:120,y:90))
bodyPath.lineToPoint(NSPoint(x:125,y:71))
bodyPath.lineToPoint(NSPoint(x:113,y:71))
bodyPath.lineToPoint(NSPoint(x:112,y:94))
bodyPath.lineToPoint(NSPoint(x:83,y:113))
bodyPath.lineToPoint(NSPoint(x:68,y:94))
bodyPath.lineToPoint(NSPoint(x:97,y:70))
bodyPath.lineToPoint(NSPoint(x:122,y:12))
bodyPath.lineToPoint(NSPoint(x:98,y:0))
bodyPath.lineToPoint(NSPoint(x:64,y:41))
bodyPath.lineToPoint(NSPoint(x:7,y:71))
bodyPath.lineToPoint(NSPoint(x:0,y:94))
bodyPath.moveToPoint(NSPoint(x:58,y:155))
let childPath=NSBezierPath()
childPath.appendBezierPath(headPath)
childPath.appendBezierPath(bodyPath)
childLayer.fillColor=NSColor.whiteColor().CGColor
childLayer.path=CGPathFromNSBezierPath(childPath)
return childLayer
}
let leftChildLayer = createChildLayer()
let rightChildLayer = createChildLayer()
rightChildLayer.transform=CATransform3DMakeRotation(CGFloat(M_PI),0.0,0.0,1.0)
rightChildLayer.geometryFlipped=true
let benchLayer = CALayer()
benchLayer.frame=NSRect(x:0,y:41,width:self.seesawLayer.bounds.width,height:30)
benchLayer.backgroundColor=NSColor.whiteColor().CGColor
leftChildLayer.frame=NSRect(x:25,y:0,width:126,height:200)
rightChildLayer.frame=NSRect(x:488-(126+25),y:0,width:126,height:200)
seesawLayer.addSublayer(leftChildLayer)
seesawLayer.addSublayer(rightChildLayer)
seesawLayer.addSublayer(benchLayer)
seesawLayer.delegate=self
}
let maxSeesawAngle=M_PI / 12
var currentSeesawAngle = 0.0
var animate:Bool = false{
didSet(oldAnimate){
if animate != oldAnimate && animate {
if currentSeesawAngle == 0 {
//@Bailey
//設置捕捉動態記錄和顯示的參數
XCPCaptureValue("Left Seesaw Position",0 )
animateSeesawToAngle(CGFloat(maxSeesawAngle),duration: 0.75)
}
else
{
animateSeesawToAngle(CGFloat(currentSeesawAngle * -1))
}
}
}
}
func animateSeesawToAngle(angle:CGFloat,duration:CFTimeInterval = 1.5 )-> CAAnimation{
let angleAnimation = CABasicAnimation(keyPath:"transform")
angleAnimation.fromValue=NSValue(CATransform3D:seesawLayer.transform)
angleAnimation.toValue=NSValue(CATransform3D:CATransform3DMakeRotation(angle, 0.0, 0.0, 1.0))
angleAnimation.timingFunction = CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseInEaseOut)
angleAnimation.duration = duration
angleAnimation.delegate=self
seesawLayer.addAnimation(
angleAnimation, forKey: "transform")
seesawLayer.transform=CATransform3DMakeRotation(angle,0.0, 0.0, 1.0)
currentSeesawAngle=Double(angle) //tag
return angleAnimation
}
override func animationDidStop(_:CAAnimation!,finished:Bool){
if finished && animate {
//@Bailey
//設置捕捉動態記錄和顯示的參數
XCPCaptureValue("Left Seesaw Position",-currentSeesawAngle )
animateSeesawToAngle( CGFloat(currentSeesawAngle * -1) )
}
}
}
extension NSColor {
class func playgroundIconFillColoer()->NSColor{
return NSColor(red:12/255,green:65/255,blue:135/255,alpha:1.0)
}
class func playgroundIconStrokeColor()->NSColor{
return NSColor(red:9/255,green:44/255,blue:91/255,alpha:1.0)
}
}
func CGPathFromNSBezierPath(nsPath:NSBezierPath)->CGPath! {
if nsPath.elementCount==0{
return nil
}
let path=CGPathCreateMutable()
var didClosePath=false
for i in 0..<nsPath.elementCount{
var points=[NSPoint](count:3,repeatedValue:NSZeroPoint)
switch nsPath.elementAtIndex(i, associatedPoints: &points){
case .MoveToBezierPathElement:
CGPathMoveToPoint(path,nil,points[0].x,points[0].y)
case .LineToBezierPathElement:
CGPathAddLineToPoint(path, nil, points[0].x, points[0].y)
case .CurveToBezierPathElement:
CGPathAddCurveToPoint(path,nil, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y)
case .ClosePathBezierPathElement:
CGPathCloseSubpath(path)
didClosePath=true
}
}
if !didClosePath{
CGPathCloseSubpath(path)
}
return CGPathCreateCopy(path)
}
let view=PlaygroundIconView(coder: NSCoder())
view?.animate=true
//@Bailey
//顯示游樂場蹺蹺板動態圖標以及時間軸用於程序計算回溯
XCPShowView("20140605",view!)
view