在 Stanford CS193P 作业 3 (2016) 上绘制 x 与 y 图时遇到问题

Having trouble plotting the x vs. y graph on Stanford CS193P Assignment 3 (2016)

我是 iOS 开发的初学者。除了实际绘制 x 与 y 图之外,我已经完成了 2016 年斯坦福 CS193P iOS iTunes U 开发课程的作业 3(图形计算器)的所有内容(手势、视图控制器、视图、segues 等)。我对将代码放在哪里以及如何执行此操作感到很困惑;以及 drawRect 将从哪里获取 y 值。我已经在互联网上搜索了很长时间的解决方案。我假设图形视图控制器的模型是 CalculatorBrain 或者可能是大脑的程序,但我不太确定视图将如何从 drawRect 函数与该控制器通信以获得 y 值点 x。如果你能让我走上正轨,那将非常有帮助。我将在下面粘贴我的图形视图控制器和图形视图代码。提前致谢。

//GraphViewController

import UIKit

class GraphViewController: UIViewController {

    private var brain = CalculatorBrain()

    var program: [AnyObject]?

    @IBOutlet weak var graphView: GraphingView! {
        didSet {
            graphView.addGestureRecognizer(UIPinchGestureRecognizer(target: graphView, action: #selector (GraphingView.changeScale(_:))))
            graphView.addGestureRecognizer(UIPanGestureRecognizer(target: graphView, action: #selector(GraphingView.panView(_:))))
            graphView.addGestureRecognizer(UITapGestureRecognizer(target: graphView, action: #selector(GraphingView.doubleTapOrigin(_:))))
        }
    }
}




//GraphingView
import UIKit

@IBDesignable
class GraphingView: UIView {

    @IBInspectable var scale: CGFloat = 50 { didSet { setNeedsDisplay() } }
    @IBInspectable var linewidth: CGFloat = 2 { didSet { setNeedsDisplay() } }
    var origin: CGPoint! { didSet { setNeedsDisplay() } }

    /* Gesture Code

    func changeScale (recognizer: UIPinchGestureRecognizer) {
        switch recognizer.state {
        case .Changed,.Ended:
            scale *= recognizer.scale
            recognizer.scale = 1.0
        default:
            break
        }
    }

    func panView (recognizer: UIPanGestureRecognizer) {
        switch recognizer.state {
        case .Changed,.Ended:
            origin.x += recognizer.translationInView(self).x
            origin.y += recognizer.translationInView(self).y
            recognizer.setTranslation(CGPoint(x:0,y:0), inView: self)
        default:
            break
        }
    }

    func doubleTapOrigin (recognizer: UITapGestureRecognizer) {
        recognizer.numberOfTapsRequired = 2
        switch recognizer.state {
        case .Ended :
            origin = CGPoint(x: recognizer.locationInView(self).x, y:recognizer.locationInView(self).y)
        default: break
        }
    }
    */

    var axesDrawer = AxesDrawer()

    override func drawRect(rect: CGRect) {

        if origin == nil {
            origin = CGPoint(x: bounds.midX, y: bounds.midY)
        }
        axesDrawer.drawAxesInRect(rect, origin: origin, pointsPerUnit: scale)

        var pixelX = 0

        while pixelX <= bounds.maxX {

            //do some code to get value of y for current x from the brain in the view controller and plot it with UIBezierPath

            pixelX += 1/contentScaleFactor
        }

    }

}

更新答案 - 使用函数指针

前面的答案是使用数据源来完成的一种方法。它是根据 UITableViewController 获取数据的方式建模的。

阅读作业后,教授似乎希望您改用可选的函数指针。

执行以下操作:

  1. GraphingView中创建一个可选变量来保存函数指针:

    var computeYForX: ((Double) -> Double)?
    
  2. didSet 应用于该变量,并在设置时调用 self.setNeedsDisplay()。这将告诉 iOS 需要调用 drawRect

  3. 在计算器中绘制时,将 GraphingViewcomputeYForX 属性 设置为 (Double) -> Double 函数 GraphViewController。如果您想删除该功能(例如,当有人重置计算器时),请将 属性 设置为 nil

  4. drawRect 中,检查以确保 computeYForX 不是 nil,然后再使用它绘制函数图形。在使用之前使用 guardif let 安全地解包函数。

上一个答案 - 使用数据源

您需要将 数据源 添加到您的 GraphingView。首先定义一个协议叫GraphDataSource:

protocol GraphDataSource: class {
    func computeYforX(x: Double) -> Double
}

添加一个dataSource 属性到GraphingView:

class GraphingView: UIView {
    weak var dataSource: GraphDataSource?

通过将 GraphDataSource 添加到 class 定义行、实现 computeYForX() 并将自身设置为 dataSource,让您的 GraphViewController 实现该协议] 在 didSetgraphView:

class GraphViewController: UIViewController, GraphDataSource {

    private var brain = CalculatorBrain()

    var program: [AnyObject]?

    @IBOutlet weak var graphView: GraphingView! {
        didSet {
            graphView.dataSource = self
            graphView.addGestureRecognizer(UIPinchGestureRecognizer(target: graphView, action: #selector (GraphingView.changeScale(_:))))
            graphView.addGestureRecognizer(UIPanGestureRecognizer(target: graphView, action: #selector(GraphingView.panView(_:))))
            graphView.addGestureRecognizer(UITapGestureRecognizer(target: graphView, action: #selector(GraphingView.doubleTapOrigin(_:))))
        }
    }

    func computeYForX(x: Double) -> Double {
        // call the brain to calculate y and return it
    }
}

然后在 drawRect 中,当您需要 y 值时,在 dataSource 上调用 computeYForX()

let y = dataSource?.computeYForX(x)

我使用了计算器大脑中的现有功能。具体来说,在 brain 中,我创建了一个 public 方法,该方法基于程序进行计算,通过 属性 列表类型转换存储为任何对象(我们在 save/restore 部分中这样做)。所有的命令都在那里——我只是把它打包成一个方法。我将这种方法用于很多事情 - 所以我从不重写代码来评估一系列操作数。

在public界面中,这只是一个可以从任何大脑实例访问的功能。特别是,在准备转至图表视图时,我将图表视图控制器指向此函数。我也通过了当时脑中存在的程序,这样在计算器中有效的任何表达式都可以被绘制出来。后者对我来说是一个目标,因为它很有意义。我想避免重写解释操作数的函数。

图形视图需要一个函数(可以是大脑或任何其他规范)和一个 "program" 来绘制表达式。所以它是一个通用的 class 需要一个特定的形式来描述操作。我认为这是一个完美的 "legal" 解决方案。

然后Drawrect使用函数和程序绘制曲线。