2016-12-16 49 views
0

我正在試圖製作一個圖形計算器應用程序,我無法讓AxesDrawer工作。這是與SWIFT 2斯坦福大學期間,我不知道如何使用繪製了這一點UIBezierPath等AxesDrawer無法繪製swift3 xcode 8

AxesDrawer.swift:

import UIKit 

class AxesDrawer 
{ 
    private struct Constants { 
     static let HashmarkSize: CGFloat = 6 
    } 

    var color = UIColor.blue 
    var minimumPointsPerHashmark: CGFloat = 40 
    var contentScaleFactor: CGFloat = 1 // set this from UIView's contentScaleFactor to position axes with maximum accuracy 

    convenience init(color: UIColor, contentScaleFactor: CGFloat) { 
     self.init() 
     self.color = color 
     self.contentScaleFactor = contentScaleFactor 
    } 

    convenience init(color: UIColor) { 
     self.init() 
     self.color = color 
    } 

    convenience init(contentScaleFactor: CGFloat) { 
     self.init() 
     self.contentScaleFactor = contentScaleFactor 
    } 

    // this method is the heart of the AxesDrawer 
    // it draws in the current graphic context's coordinate system 
    // therefore origin and bounds must be in the current graphics context's coordinate system 
    // pointsPerUnit is essentially the "scale" of the axes 
    // e.g. if you wanted there to be 100 points along an axis between -1 and 1, 
    // you'd set pointsPerUnit to 50 

    func drawAxesInRect(bounds: CGRect, origin: CGPoint, pointsPerUnit: CGFloat) 
    { 
     UIGraphicsGetCurrentContext()!.saveGState() 
     color.set() 
     let path = UIBezierPath() 
     path.move(to: CGPoint(x: bounds.minX, y: align(coordinate: origin.y))) 
     path.addLine(to: CGPoint(x: bounds.maxX, y: align(coordinate: origin.y))) 
     path.move(to: CGPoint(x: align(coordinate: origin.x), y: bounds.minY)) 
     path.addLine(to: CGPoint(x: align(coordinate: origin.x), y: bounds.maxY)) 
     path.stroke() 
     drawHashmarksInRect(bounds: bounds, origin: origin, pointsPerUnit: abs(pointsPerUnit)) 
     UIGraphicsGetCurrentContext()!.restoreGState() 
    } 

    // the rest of this class is private 

    private func drawHashmarksInRect(bounds: CGRect, origin: CGPoint, pointsPerUnit: CGFloat) 
    { 
     if ((origin.x >= bounds.minX) && (origin.x <= bounds.maxX)) || ((origin.y >= bounds.minY) && (origin.y <= bounds.maxY)) 
     { 
      // figure out how many units each hashmark must represent 
      // to respect both pointsPerUnit and minimumPointsPerHashmark 
      var unitsPerHashmark = minimumPointsPerHashmark/pointsPerUnit 
      if unitsPerHashmark < 1 { 
       unitsPerHashmark = pow(10, ceil(log10(unitsPerHashmark))) 
      } else { 
       unitsPerHashmark = floor(unitsPerHashmark) 
      } 

      let pointsPerHashmark = pointsPerUnit * unitsPerHashmark 

      // figure out which is the closest set of hashmarks (radiating out from the origin) that are in bounds 
      var startingHashmarkRadius: CGFloat = 1 
      if !bounds.contains(origin) { 
       let leftx = max(origin.x - bounds.maxX, 0) 
       let rightx = max(bounds.minX - origin.x, 0) 
       let downy = max(origin.y - bounds.minY, 0) 
       let upy = max(bounds.maxY - origin.y, 0) 
       startingHashmarkRadius = min(min(leftx, rightx), min(downy, upy))/pointsPerHashmark + 1 
      } 

      // now create a bounding box inside whose edges those four hashmarks lie 
      let bboxSize = pointsPerHashmark * startingHashmarkRadius * 2 
      var bbox = CGRect(center: origin, size: CGSize(width: bboxSize, height: bboxSize)) 

      // formatter for the hashmark labels 
      let formatter = NumberFormatter() 
      formatter.maximumFractionDigits = Int(-log10(Double(unitsPerHashmark))) 
      formatter.minimumIntegerDigits = 1 

      // radiate the bbox out until the hashmarks are further out than the bounds 
      while !bbox.contains(bounds) 
      { 

       let label = formatter.string(from: NSNumber(value: Int(origin.x-bbox.minX/pointsPerUnit))) 
       if let leftHashmarkPoint = alignedPoint(x: bbox.minX, y: origin.y, insideBounds:bounds) { 
        drawHashmarkAtLocation(location: leftHashmarkPoint, .Top("-\(label)")) 
       } 
       if let rightHashmarkPoint = alignedPoint(x: bbox.maxX, y: origin.y, insideBounds:bounds) { 
        drawHashmarkAtLocation(location: rightHashmarkPoint, AnchoredText.Top(label!)) 
       } 
       if let topHashmarkPoint = alignedPoint(x: origin.x, y: bbox.minY, insideBounds:bounds) { 
        drawHashmarkAtLocation(location: topHashmarkPoint, AnchoredText.Left(label!)) 
       } 
       if let bottomHashmarkPoint = alignedPoint(x: origin.x, y: bbox.maxY, insideBounds:bounds) { 
        drawHashmarkAtLocation(location: bottomHashmarkPoint, .Left("-\(label)")) 
       } 
       bbox.insetBy(dx: -pointsPerHashmark, dy: -pointsPerHashmark) 
      } 
     } 
    } 

    private func drawHashmarkAtLocation(location: CGPoint, _ text: AnchoredText) 
    { 
     var dx: CGFloat = 0, dy: CGFloat = 0 
     switch text { 
      case .Left: dx = Constants.HashmarkSize/2 
      case .Right: dx = Constants.HashmarkSize/2 
      case .Top: dy = Constants.HashmarkSize/2 
      case .Bottom: dy = Constants.HashmarkSize/2 
     } 

     let path = UIBezierPath() 
     path.move(to: CGPoint(x: location.x-dx, y: location.y-dy)) 
     path.addLine(to: CGPoint(x: location.x+dx, y: location.y+dy)) 
     path.stroke() 

     text.drawAnchoredToPoint(location: location, color: color) 
    } 

    private enum AnchoredText 
    { 
     case Left(String) 
     case Right(String) 
     case Top(String) 
     case Bottom(String) 

     static let VerticalOffset: CGFloat = 3 
     static let HorizontalOffset: CGFloat = 6 

     func drawAnchoredToPoint(location: CGPoint, color: UIColor) { 
      let attributes = [ 
       NSFontAttributeName : UIFont.preferredFont(forTextStyle: UIFontTextStyle.footnote), 
       NSForegroundColorAttributeName : color 
      ] 
      var textRect = CGRect(center: location, size: text.size(attributes: attributes)) 
      switch self { 
       case .Top: textRect.origin.y += textRect.size.height/2 + AnchoredText.VerticalOffset 
       case .Left: textRect.origin.x += textRect.size.width/2 + AnchoredText.HorizontalOffset 
       case .Bottom: textRect.origin.y -= textRect.size.height/2 + AnchoredText.VerticalOffset 
       case .Right: textRect.origin.x -= textRect.size.width/2 + AnchoredText.HorizontalOffset 
      } 
      text.draw(in: textRect, withAttributes: attributes) 
     } 

     var text: String { 
      switch self { 
       case .Left(let text): return text 
       case .Right(let text): return text 
       case .Top(let text): return text 
       case .Bottom(let text): return text 
      } 
     } 
    } 

    // we want the axes and hashmarks to be exactly on pixel boundaries so they look sharp 
    // setting contentScaleFactor properly will enable us to put things on the closest pixel boundary 
    // if contentScaleFactor is left to its default (1), then things will be on the nearest "point" boundary instead 
    // the lines will still be sharp in that case, but might be a pixel (or more theoretically) off of where they should be 

    private func alignedPoint(x x: CGFloat, y: CGFloat, insideBounds: CGRect? = nil) -> CGPoint? 
    { 
     let point = CGPoint(x: align(coordinate: x), y: align(coordinate: y)) 
     if let permissibleBounds = insideBounds, !permissibleBounds.contains(point) { 
      return nil 
     } 
     return point 
    } 

    private func align(coordinate: CGFloat) -> CGFloat { 
     return round(coordinate * contentScaleFactor)/contentScaleFactor 
    } 
} 

extension CGRect 
{ 
    init(center: CGPoint, size: CGSize) { 
     self.init(x: center.x-size.width/2, y: center.y-size.height/2, width: size.width, height: size.height) 
    } 
} 

ViewController.swift: 

    import UIKit 
var calculatorCount = 0 
class CalculatorViewController: UIViewController { 
    var graphl = GraphView() 
    private var on = true 
    @IBOutlet private var display: UILabel! 

    private var userIsInTheMiddleOfTyping = false 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     calculatorCount += 1 
     //print("Loaded up a new Calculator (count = \(calculatorCount))") 
     brain.addUnaryOperation(symbol: "Z") { [ weak weakSelf = self ] in 
      weakSelf?.display.textColor = UIColor.red 
      return sqrt($0) 
     } 
     graphl.print2() 
    } 


    deinit { 
     calculatorCount -= 1 
     //print(" Calculator left the heap (count = \(calculatorCount))") 
    } 


    @IBAction func off(_ sender: UIButton) { 
     on = false 
    } 

    @IBAction func on(_ sender: UIButton) { 
     on = true 
    } 

    @IBAction private func tocuhDigit(_ sender: UIButton) { 
     if on { 
      let digit = sender.currentTitle! 
      if userIsInTheMiddleOfTyping { 
       let textCurrentlyInDisplay = display.text! 
       display.text = textCurrentlyInDisplay + digit 
      } else { 
       display.text = digit 
      } 
      userIsInTheMiddleOfTyping = true 
     } 
    } 
    private var displayValue: Double { 
     get { 
      return Double(display.text!)! 
     } 
     set { 
      display.text = String(newValue) 

     } 
    } 
    var savedProgram: CalculatorBrain.PropertyList? 
    @IBAction func save() { 
     savedProgram = brain.program 

    } 

    @IBAction func restore() { 
     if savedProgram != nil { 
      brain.program = savedProgram! 
      displayValue = brain.result 
     } 
    } 

    private var brain = CalculatorBrain() 

    @IBAction func Reset(_ sender: UIButton) { 
     if on { 

      displayValue = 0 
     } 
    } 
    @IBAction private func performOperation(_ sender: UIButton) { 
     if userIsInTheMiddleOfTyping && on { 
      brain.setOperand(operand: displayValue) 
      userIsInTheMiddleOfTyping = false 
     } 

     if let mathematicalSymbol = sender.currentTitle { 
      brain.perofrmOperation(symbol: mathematicalSymbol) 

     } 
     displayValue = brain.result 
    } 

} 

CalculatorBrain.swift: 

    import Foundation 


class CalculatorBrain { 

    private var accumulator = 0.0 
    private var internalProgram = [AnyObject]() 

    func setOperand(operand: Double) { 
     accumulator = operand 
     internalProgram.append(operand as AnyObject) 

    } 
    func addUnaryOperation(symbol: String, operation: @escaping (Double) -> Double) { 
     operations[symbol] = Operation.UnaryOperation(operation) 
    } 

    private var operations: Dictionary<String, Operation> = [ 
     "π" :Operation.Constant(M_PI), 
     "e" : Operation.Constant(M_E), 
     "±" : Operation.UnaryOperation({ -$0 }), 
     "∓" : Operation.UnaryOperation({+$0}), 
     "√" : Operation.UnaryOperation(sqrt), //sqrt, 
     "cos" : Operation.UnaryOperation(cos), 
     "×" : Operation.BinaryOperation({ $0 * $1 }), 
     "-" : Operation.BinaryOperation({ $0 - $1 }), 
     "+" : Operation.BinaryOperation({ $0 + $1 }), 
     "÷" : Operation.BinaryOperation({ $0/$1 }), 
     "=" : Operation.Equals, 
     "i" : Operation.Constant(sqrt(-1)), 
     "x2" : Operation.UnaryOperation({$0 * $0}), 
     "xb" : Operation.BinaryOperation2({pow($0, $1)}) 
    ] 

    private enum Operation { 
     case Constant(Double) 
     case UnaryOperation((Double) -> Double) 
     case BinaryOperation((Double, Double) -> Double) 
     case Equals 
     case BinaryOperation2((Double, Double) -> Double) 

    } 

    func perofrmOperation(symbol: String) { 
     internalProgram.append(symbol as AnyObject) 
     if let operation = operations[symbol] { 
      switch operation { 
      case .Constant(let value): accumulator = value 
      case .UnaryOperation(let function): accumulator = function(accumulator) 
      case .BinaryOperation(let function): executePendingBinaryOperation() 
      pending = PendingBinaryOperationInfo(binaryFunction: function, firstOperand: accumulator) 
      case .Equals: 
       executePendingBinaryOperation() 

      case .BinaryOperation2(let function): 
       pending = PendingBinaryOperationInfo(binaryFunction: function, firstOperand: accumulator) 




      } 
     } 


    } 
    private func executePendingBinaryOperation() { 
     if pending != nil { 
      accumulator = pending!.binaryFunction(pending!.firstOperand, accumulator) 
     } 
    } 

    private var pending: PendingBinaryOperationInfo? 

    private struct PendingBinaryOperationInfo { 
     var binaryFunction: (Double, Double) -> Double 
     var firstOperand: Double 

    } 

    typealias PropertyList = AnyObject 
    var program: PropertyList { 
     get { 
      return internalProgram as CalculatorBrain.PropertyList 
     } 
     set { 
      clear() 
      if let arrayOfOps = newValue as? [AnyObject] { 
       for op in arrayOfOps { 
        if let operand = op as? Double { 
         setOperand(operand: operand) 
        } else if let operation = op as? String { 
         perofrmOperation(symbol: operation) 
        } 
       } 
      } 
     } 
    } 
    func clear() { 
     accumulator = 0.0 
     pending = nil 
     internalProgram.removeAll() 
    } 

    var result: Double { 
     get { 
      return accumulator 

     } 
    } 

} 

AppDelegate.swift: 

    import UIKit 

@UIApplicationMain 
class AppDelegate: UIResponder, UIApplicationDelegate { 

    var window: UIWindow? 


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 
     // Override point for customization after application launch. 
     return true 
    } 

    func applicationWillResignActive(_ application: UIApplication) { 
     // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 
     // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 
    } 

    func applicationDidEnterBackground(_ application: UIApplication) { 
     // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
     // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 
    } 

    func applicationWillEnterForeground(_ application: UIApplication) { 
     // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 
    } 

    func applicationDidBecomeActive(_ application: UIApplication) { 
     // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 
    } 

    func applicationWillTerminate(_ application: UIApplication) { 
     // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 
    } 


} 

Main.storyboard: 
[buttons are in stack a stack view and created a UIView for the graph][1] 


    [1]: https://i.stack.imgur.com/oxwmw.png 

回答

0

我知道有很多的時間,因爲這個問題已經取得,但我做了同樣的問題,今天,我希望這個答案可以幫助別人:

您可以按照下列步驟操作:

  1. 創建於Xcode的一個新項目。
  2. 添加「AxesDrawer.swift」文件到您的項目(File - >添加文件「項目名」 ...
  3. 使用亞類的UIView你的項目的主文件夾創建一個新的可可觸摸類文件
  4. 添加查看Main.storyboard並設置這個觀點等於上述創建的文件的類的名稱。
  5. 使用下面的代碼重寫繪製函數在文件CREA特德第3步:

    覆蓋FUNC平局(_ RECT:的CGRect){

    //Draw axes 
    let axes: AxesDrawer = AxesDrawer.init(color: UIColor.black, contentScaleFactor: CGFloat(1)) 
    
    axes.drawAxes(in: CGRect(origin: CGPoint(x: bounds.midX, y: bounds.midY), 
             size: CGSize(width: 1000, height: -1000)), 
           origin: CGPoint(x: bounds.midX, y: bounds.midY), 
           pointsPerUnit: CGFloat(2)) 
    
    axes.drawAxes(in: CGRect(origin: CGPoint(x: bounds.midX, y: bounds.midY), 
             size: CGSize(width: -1000, height: 1000)), 
           origin: CGPoint(x: bounds.midX, y: bounds.midY), 
           pointsPerUnit: CGFloat(2)) 
    //End Draw axes 
    

    }

-2
// 
// AxesDrawer.swift 
// Calculator 
// 
// Created by CS193p Instructor. 
// Copyright © 2015-17 Stanford University. 
// All rights reserved. 
// 

import UIKit 

struct AxesDrawer 
{ 
    var color: UIColor 
    var contentScaleFactor: CGFloat    // set this from UIView's contentScaleFactor to position axes with maximum accuracy 
    var minimumPointsPerHashmark: CGFloat = 40 // public even though init doesn't accommodate setting it (it's rare to want to change it) 

    init(color: UIColor = UIColor.blue, contentScaleFactor: CGFloat = 1) { 
     self.color = color 
     self.contentScaleFactor = contentScaleFactor 
    } 

    // this method is the heart of the AxesDrawer 
    // it draws in the current graphic context's coordinate system 
    // therefore origin and bounds must be in the current graphics context's coordinate system 
    // pointsPerUnit is essentially the "scale" of the axes 
    // e.g. if you wanted there to be 100 points along an axis between -1 and 1, 
    // you'd set pointsPerUnit to 50 

    func drawAxes(in rect: CGRect, origin: CGPoint, pointsPerUnit: CGFloat) 
    { 
     UIGraphicsGetCurrentContext()?.saveGState() 
     color.set() 
     let path = UIBezierPath() 
     path.move(to: CGPoint(x: rect.minX, y: origin.y).aligned(usingScaleFactor: contentScaleFactor)!) 
     path.addLine(to: CGPoint(x: rect.maxX, y: origin.y).aligned(usingScaleFactor: contentScaleFactor)!) 
     path.move(to: CGPoint(x: origin.x, y: rect.minY).aligned(usingScaleFactor: contentScaleFactor)!) 
     path.addLine(to: CGPoint(x: origin.x, y: rect.maxY).aligned(usingScaleFactor: contentScaleFactor)!) 
     path.stroke() 
     drawHashmarks(in: rect, origin: origin, pointsPerUnit: abs(pointsPerUnit)) 
     UIGraphicsGetCurrentContext()?.restoreGState() 
    } 

    // the rest of this class is private 

    private struct Constants { 
     static let hashmarkSize: CGFloat = 6 
    } 

    private let formatter = NumberFormatter() // formatter for the hashmark labels 

    private func drawHashmarks(in rect: CGRect, origin: CGPoint, pointsPerUnit: CGFloat) 
    { 
     if ((origin.x >= rect.minX) && (origin.x <= rect.maxX)) || ((origin.y >= rect.minY) && (origin.y <= rect.maxY)) 
     { 
      // figure out how many units each hashmark must represent 
      // to respect both pointsPerUnit and minimumPointsPerHashmark 
      var unitsPerHashmark = minimumPointsPerHashmark/pointsPerUnit 
      if unitsPerHashmark < 1 { 
       unitsPerHashmark = pow(10, ceil(log10(unitsPerHashmark))) 
      } else { 
       unitsPerHashmark = floor(unitsPerHashmark) 
      } 

      let pointsPerHashmark = pointsPerUnit * unitsPerHashmark 

      // figure out which is the closest set of hashmarks (radiating out from the origin) that are in rect 
      var startingHashmarkRadius: CGFloat = 1 
      if !rect.contains(origin) { 
       let leftx = max(origin.x - rect.maxX, 0) 
       let rightx = max(rect.minX - origin.x, 0) 
       let downy = max(origin.y - rect.minY, 0) 
       let upy = max(rect.maxY - origin.y, 0) 
       startingHashmarkRadius = min(min(leftx, rightx), min(downy, upy))/pointsPerHashmark + 1 
      } 

      // pick a reasonable number of fraction digits 
      formatter.maximumFractionDigits = Int(-log10(Double(unitsPerHashmark))) 
      formatter.minimumIntegerDigits = 1 

      // now create a bounding box inside whose edges those four hashmarks lie 
      let bboxSize = pointsPerHashmark * startingHashmarkRadius * 2 
      var bbox = CGRect(center: origin, size: CGSize(width: bboxSize, height: bboxSize)) 

      // radiate the bbox out until the hashmarks are further out than the rect 
      while !bbox.contains(rect) 
      { 
       let label = formatter.string(from: (origin.x-bbox.minX)/pointsPerUnit)! 
       if let leftHashmarkPoint = CGPoint(x: bbox.minX, y: origin.y).aligned(inside: rect, usingScaleFactor: contentScaleFactor) { 
        drawHashmark(at: leftHashmarkPoint, label: .top("-\(label)")) 
       } 
       if let rightHashmarkPoint = CGPoint(x: bbox.maxX, y: origin.y).aligned(inside: rect, usingScaleFactor: contentScaleFactor) { 
        drawHashmark(at: rightHashmarkPoint, label: .top(label)) 
       } 
       if let topHashmarkPoint = CGPoint(x: origin.x, y: bbox.minY).aligned(inside: rect, usingScaleFactor: contentScaleFactor) { 
        drawHashmark(at: topHashmarkPoint, label: .left(label)) 
       } 
       if let bottomHashmarkPoint = CGPoint(x: origin.x, y: bbox.maxY).aligned(inside: rect, usingScaleFactor: contentScaleFactor) { 
        drawHashmark(at: bottomHashmarkPoint, label: .left("-\(label)")) 
       } 
       bbox = bbox.insetBy(dx: -pointsPerHashmark, dy: -pointsPerHashmark) 
      } 
     } 
    } 

    private func drawHashmark(at location: CGPoint, label: AnchoredText) 
    { 
     var dx: CGFloat = 0, dy: CGFloat = 0 
     switch label { 
      case .left: dx = Constants.hashmarkSize/2 
      case .right: dx = Constants.hashmarkSize/2 
      case .top: dy = Constants.hashmarkSize/2 
      case .bottom: dy = Constants.hashmarkSize/2 
     } 

     let path = UIBezierPath() 
     path.move(to: CGPoint(x: location.x-dx, y: location.y-dy)) 
     path.addLine(to: CGPoint(x: location.x+dx, y: location.y+dy)) 
     path.stroke() 

     label.draw(at: location, usingColor: color) 
    } 

    private enum AnchoredText 
    { 
     case left(String) 
     case right(String) 
     case top(String) 
     case bottom(String) 

     static let verticalOffset: CGFloat = 3 
     static let horizontalOffset: CGFloat = 6 

     func draw(at location: CGPoint, usingColor color: UIColor) { 
      let attributes = [ 
       NSFontAttributeName : UIFont.preferredFont(forTextStyle: .footnote), 
       NSForegroundColorAttributeName : color 
      ] 
      var textRect = CGRect(center: location, size: text.size(attributes: attributes)) 
      switch self { 
       case .top: textRect.origin.y += textRect.size.height/2 + AnchoredText.verticalOffset 
       case .left: textRect.origin.x += textRect.size.width/2 + AnchoredText.horizontalOffset 
       case .bottom: textRect.origin.y -= textRect.size.height/2 + AnchoredText.verticalOffset 
       case .right: textRect.origin.x -= textRect.size.width/2 + AnchoredText.horizontalOffset 
      } 
      text.draw(in: textRect, withAttributes: attributes) 
     } 

     var text: String { 
      switch self { 
       case .left(let text): return text 
       case .right(let text): return text 
       case .top(let text): return text 
       case .bottom(let text): return text 
      } 
     } 
    } 
} 

private extension CGPoint 
{ 
    func aligned(inside bounds: CGRect? = nil, usingScaleFactor scaleFactor: CGFloat = 1.0) -> CGPoint? 
    { 
     func align(_ coordinate: CGFloat) -> CGFloat { 
      return round(coordinate * scaleFactor)/scaleFactor 
     } 
     let point = CGPoint(x: align(x), y: align(y)) 
     if let permissibleBounds = bounds, !permissibleBounds.contains(point) { 
      return nil 
     } 
     return point 
    } 
} 

private extension NumberFormatter 
{ 
    func string(from point: CGFloat) -> String? { 
     return string(from: NSNumber(value: Double(point))) 
    } 
} 

private extension CGRect 
{ 
    init(center: CGPoint, size: CGSize) { 
     self.init(x: center.x-size.width/2, y: center.y-size.height/2, width: size.width, height: size.height) 
    } 
} 
+0

當你回答一個問題,說明你有什麼完成而不是將所有代碼發佈在一起。請參閱[如何回答](https://stackoverflow.com/help/how-to-answer) – siddiq