在 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
获取数据的方式建模的。
阅读作业后,教授似乎希望您改用可选的函数指针。
执行以下操作:
在GraphingView
中创建一个可选变量来保存函数指针:
var computeYForX: ((Double) -> Double)?
将 didSet
应用于该变量,并在设置时调用 self.setNeedsDisplay()
。这将告诉 iOS 需要调用 drawRect
。
在计算器中绘制时,将 GraphingView
的 computeYForX
属性 设置为 (Double) -> Double
函数 GraphViewController
。如果您想删除该功能(例如,当有人重置计算器时),请将 属性 设置为 nil
。
在 drawRect
中,检查以确保 computeYForX
不是 nil
,然后再使用它绘制函数图形。在使用之前使用 guard
或 if 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
实现该协议] 在 didSet
中 graphView
:
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使用函数和程序绘制曲线。
我是 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
获取数据的方式建模的。
阅读作业后,教授似乎希望您改用可选的函数指针。
执行以下操作:
在
GraphingView
中创建一个可选变量来保存函数指针:var computeYForX: ((Double) -> Double)?
将
didSet
应用于该变量,并在设置时调用self.setNeedsDisplay()
。这将告诉 iOS 需要调用drawRect
。在计算器中绘制时,将
GraphingView
的computeYForX
属性 设置为(Double) -> Double
函数GraphViewController
。如果您想删除该功能(例如,当有人重置计算器时),请将 属性 设置为nil
。在
drawRect
中,检查以确保computeYForX
不是nil
,然后再使用它绘制函数图形。在使用之前使用guard
或if 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
实现该协议] 在 didSet
中 graphView
:
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使用函数和程序绘制曲线。