(作业)自定义视图(UIView绘制任意函数曲线)

感觉好久没发作业了,想想也是,国庆+上周阶段考,两周没做iOS作业了,手都有点生了。老样子,先看作业要求。
这里写图片描述

这次作业很基础,但也开始设计到UI方面的开发了。所以现在要创建这个工程:
这里写图片描述
这里写图片描述

然后我们需要从UIView派生一个子类出来,这里就叫MyView吧。我们需要重写它的draw(rect:)方法。这个方法是UIView中负责绘制UIView的显示的,我们绘制函数曲线就是通过它。首先我们需要通过它绘制一个简易的坐标系,然后我们还需要一个私有的函数类型的属性,它用来保存传入的需要绘制的函数,在draw方法中,判断该函数是否有值,有值的话就绘制函数曲线,没有值时就只绘制坐标系。最后在视图控制器中创建MyView并显示即可。(当前只实现了绘制一元函数,读者想要扩展可自行思考)

MyView.swift

import UIKit

class MyView: UIView {

    private var function: ((CGFloat) -> CGFloat)? //一元函数

    override func draw(_ rect: CGRect) {
        //调用父类的draw方法
        super.draw(rect)

        //创建一个UIBezierPath变量,UIBezierPath可创建基于矢量的路径,常用来绘图
        let rectPath = UIBezierPath(rect: rect)
        //设置白色填充
        UIColor.white.setFill()
        //先将MyView填充一层白色
        rectPath.fill()

        //再创建一个UIBezierPath变量,用于绘制坐标系
        let path = UIBezierPath()
        //坐标系用红色描边
        UIColor.black.setStroke()
        //坐标系以MyView中心为原点,向右为x正方向,向上为y正方向
        //先将path移动到左边线中点处
        path.move(to: CGPoint(x: 0, y: rect.height / 2))
        //然后添加一条到右边线中点处的直线
        path.addLine(to: CGPoint(x: rect.width, y: rect.height / 2))
        //绘制这条直线
        path.stroke()
        //这样就完成了x轴的绘制

        //绘制y轴原理同上
        path.move(to: CGPoint(x: rect.width / 2, y: 0))
        path.addLine(to: CGPoint(x: rect.width / 2, y: rect.height))
        path.stroke()

        //判断函数是否为空,方便后面会再次调用draw函数
        if function != nil {
            let path = curve(rect: rect, color: UIColor.red, function: function!)
            path.stroke()
        }
    }


    /// 绘制图形的函数
    ///
    /// - Parameter function: 需要绘制的一元函数
    func drawCurve(function: @escaping (CGFloat) -> CGFloat) {
        self.function = function
        self.draw(self.frame)
    }


    /// 计算函数绘制的路径
    ///
    /// - Parameters:
    ///   - rect: 绘制区域
    ///   - color: 绘制函数的颜色
    ///   - function: 需要绘制的函数
    /// - Returns: 返回最终绘制的路径
    private func curve(rect: CGRect, color: UIColor, function: (CGFloat) -> CGFloat) -> UIBezierPath {
        let path = UIBezierPath()

        //该绘制区域的宽度的一半
        let center = rect.width / 2
        //y轴的高度的一半
        let height = rect.height / 2
        //需要计算多少个x值对应的y值(x轴正半轴)
        let rate: CGFloat = 100
        color.setStroke()

        //从原点开始,先计算x轴正半轴的所有y值
        path.move(to: CGPoint(x: center, y: height - function(0) / rate)))
        //stride为步进函数,设置起始值、结尾值和步进值
        for item in stride(from: center / rate, through: center, by: center / rate) {
            path.addLine(to: CGPoint(x: center + item, y: height - function(item) / rate))
        }
        //因为自己建立的简易坐标系与UIView视图默认的坐标系不同(UIView默认视图坐标系原点在左上角,并向右为x轴正方向,向下为y轴正方向)。所以需要用到类似仿射变换的方式转换坐标系,将当前坐标系中的点转换成UIView坐标系中的值。UIView中x值为当前值加上x轴宽度的一半,y值为y轴高度的一半减去在当前坐标系中的y值

        //绘制x轴负半轴的所有y值,原理类似
        path.move(to: CGPoint(x: center, y: height - function(0) / rate)))
        for item in stride(from: center / rate, through: center, by: center / rate) {
            path.addLine(to: CGPoint(x: center - item, y: height - function(-item) / rate))
        }

        return path
    }

}

ViewController.swift

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        //设置MyView的区域
        let rect = CGRect(x: 0, y: 20, width: self.view.frame.width, height: self.view.frame.height - 20)
        //创建MyView
        let view = MyView(frame: rect)
        //传入需要绘制的函数
        view.drawCurve { x in
            return x * x * x
        }
        //将当前视图加入到视图控制器中
        self.view.addSubview(view)

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

最后来看看绘制的效果吧。

传入 y = 10000
这里写图片描述

传入 y = x2
这里写图片描述

传入 y = x3
这里写图片描述

发布了45 篇原创文章 · 获赞 20 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/average17/article/details/78276546