《MonoTouch开发实践指南》一3.5 实现自定义UIView

简介: 本节书摘来自华章出版社《MonoTouch开发实践指南》一 书中的第3章,第3.5节,作者:(美)Michael Bluestein,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

3.5 实现自定义UIView

对于SecondView类,首先要将它设置为UIView的子类,同时添加MonoTouch.UIkit命名空间。要绘制视图,可以调用DrawRect方法。为了给视图添加自定义绘图代码,需要重写DrawRect方法。每一个iOS应用程序都有一个主循环。当给DrawRect添加代码时,它在下一次循环时才会调用。不能在程序中直接调用DrawRect方法,它只能由系统在需要的时候调用。当视图第一次加载的时候,会执行绘图代码,所以不需要额外的步骤去调用DrawRect。当视图在初始绘制后,如果要强制重画,可以调用视图的SetNeedDisplay方法,这样视图就可以在事件循环过程中进行重画。
在执行自定义绘图时,可通过视图的图形上下文(graphics context)进行绘制操作。图形上下文是用户绘图用的抽象画布(如屏幕),可在上面使用任何可用的画图工具(如颜色笔、几何形状等)进行画图。代码清单3-6列出了部分SecondView类用于绘制一个正方形的代码。
注意 视图的绘图工作实际通过层技术实现,这将在第6章进行详细讲述。
代码清单3-6 自定义绘图UIView

using System;
using MonoTouch.UIKit;
using MonoTouch.CoreGraphics;
using System.Drawing;

namespace LMT33
{
    public class SecondView : UIView
    {
        CGPath _path;

        public SecondView ()
        {
        }
        public override void Draw (RectangleF rect)
        {
            base.Draw (rect);

            // Get graphics context
            CGContext gctx = UIGraphics.GetCurrentContext ();

            // Set up drawing attributes
            gctx.SetLineWidth (2);
            UIColor.Gray.SetFill ();
            UIColor.Black.SetStroke ();

            // Create geometry
            _path = new CGPath ();

            _path.AddLines (new PointF[] {
                new PointF (110, 100),
                new PointF (210, 100),
                new PointF (210, 200),
                new PointF (110, 200) });

            _path.CloseSubpath ();

            // Add geometry to graphics context and draw it
            gctx.AddPath (_path);
            gctx.DrawPath (CGPathDrawingMode.FillStroke);
        }
    }
}

代码清单3-6中的绘图代码使用了核心图形(Core Graphics)功能,这会在第6章讲述。也可以在UIView实现中直接使用UIKit类。例如,在UIView内使用UILabel显示视图的标题,并通过提供标题(title)属性让控制器设置标题,代码如下:

...
string _title;
UILabel _titleLabel;

public string Title {
    get { return _title; }
    set {
          _title = value;
          _titleLabel.Text = _title;
    }
}

public SecondView ()
{
    _titleLabel = new UILabel ();
}

...

public override void Draw (RectangleF rect)
{
    ...

    _titleLabel.Frame = new RectangleF(5,5,Bounds.Width-10, 25);
    this.AddSubview (_titleLabel);
}

注意 如果不使用UILabel,而是使用核心图形(Core Graphics)功能直接绘制标题的字符串,那么需要在设置标题时调用SetNeedDisplay方法。尽管在初始化时看不到标题,但调用者会改变视图的标题。当更改标题时,调用SetNeedsDisplay方法可以重画视图。当更改标题时,UILabel会在内部调用SetNeedsDisplay方法。
代码中添加了一个包含标题的UILabel作为子视图。视图是按层次结构排列。当添加一个子视图到一个视图时,无论是在视图内部实现(如当前示例的UILabel),还是在外部通过控制器或IB的视图类实现,按照z次序放置子视图都会位于其父视图的上方。如果现在运行应用程序并选择第二个标签,将会看到绘制有标题和正方形的屏幕(如图3-11所示)。
在该示例中,标签使用了Frame属性和父视图的Bounds对象来设置其大小和位置。Frame属性将视图的大小设置为一个矩形,它的位置则由父视图坐标系中的点决定,而边界则由视图自己坐标系的大小和位置决定。通常情况下,Frame属性用来设置视图实例的大小和位置,而不是在一个实际的视图实现中作为边界使用。例如,在该示例中使用UILabel的实例,而不是SecondView的实现。在该示例中,父视图就是secondView。设置UILabel的Frame属性,定义矩形的大小和位置是以父视图的原点为测量点的。iOS坐标系的原点在左上角,往右就是+x,往下就是+y。
注意 在设置视图的Frame属性时,它实际上并不存储在视图内,而是用来从内部获得其他值,如边界。同样,这将在第6章中讲述。
视图要做的另外一件事情就是捕捉事件,如触碰事件。如果检查代码清单3-7中的UIView的类层次结构,就会看到它派生于UIResponder,除此之外,还定义了几个虚函数用来处理触碰事件。为了演示这一点,现在在视图中添加一个简单的命中测试以检测用户是否触碰了正方形。
代码清单3-7 UIResponder使用虚函数处理触碰事件

public class UIResponder : NSObject
{
    ...
    public virtual void TouchesBegan (NSSet touches, UIEvent evt);
    public virtual void TouchesMoved (NSSet touches, UIEvent evt);
    public virtual void TouchesEnded (NSSet touches, UIEvent evt);
    public virtual void TouchesCancelled (NSSet touches, UIEvent evt);
    ...
}

要判断是否触碰了正方形,需要重写UIView子类的TouchesBegan方法,详细代码请看代码清单3-8。为了简单起见,这里只处理单一的触碰。触碰点封装在UITouch类中。要找到视图的实际触碰点,可以调用UITouch对象的LocationInView方法,它将返回视图坐标系内的点。
代码清单3-8 在UIView子类中处理触碰事件

public override void TouchesBegan (NSSet touches, UIEvent evt)
{
    base.TouchesBegan (touches, evt);

    UITouch touch = touches.AnyObject as UITouch;

    if (touch != null) {
        PointF pt = touch.LocationInView (this);

        if (_path.ContainsPoint (pt, true)) {
            Title = "You touched the square";
        } else {
            Title = "You didn't touch the square";
        }
    }
}

之前使用核心图形的路径功能来绘制正方形。在路径中有个ContainsPoint函数可以用来执行命中测试,将UITouch返回的点传递到该函数,它告知点是在路径组成的正方形内还是在正方形外。然后就可以通过标题将结果输出给用户(如图3-12所示)。

screenshot

相关文章
|
Java 开发工具 Maven
IDEA安装及Clone代码
IDEA安装及Clone代码
287 0
|
9月前
|
机器学习/深度学习 XML 人工智能
我是如何基于 DeepSeek-R1 构建出高效学习Agent的?
我是如何基于 DeepSeek-R1 构建出高效学习Agent的?
|
10月前
|
机器学习/深度学习 编解码 弹性计算
【实践】操作系统智能助手OS Copilot新功能测评
OS Copilot 是一款致力于深度融合于操作系统的智能助手,它旨在成为用户与操作系统交互的得力助手。通过先进的自然语言处理技术和机器学习算法,OS Copilot 能够理解用户多样化的指令,将复杂的操作系统操作简单化。在日常使用场景中,无论是文件管理、应用程序的操作,还是系统设置的调整,OS Copilot 都能提供高效的支持。例如,在文件管理方面,用户无需手动在层层文件夹中查找文件,只需通过描述文件的大致信息,如创建时间、文件内容关键词等,就能快速定位到目标文件。然而,也存在一些不足,如代码生成时未使用正确后缀名、部分响应时间较长等问题。
258 8
【实践】操作系统智能助手OS Copilot新功能测评
|
12月前
|
安全 测试技术 Android开发
移动应用开发
移动应用开发
312 9
|
数据可视化 项目管理
项目管理怎么做?四大项目管理模型详解,让你的项目不再“忙而无效”!
本文介绍四大经典项目管理模型:瀑布模型(适合需求明确的项目)、Scrum模型(适合需求频繁变化的项目)、增量模型(分阶段推进,逐步完成)和风险管理模型(防患于未然)。同时推荐几款常用工具,如板栗看板、Trello和Asana,帮助团队更高效地协作。
461 0
|
机器学习/深度学习 算法框架/工具 C++
Caffe(Convolutional Architecture for Fast Feature Embedding)
Caffe(Convolutional Architecture for Fast Feature Embedding)是一个流行的深度学习框架,主要用于图像分类、物体检测和语义分割等计算机视觉任务。它由Berkeley Vision and Learning Center(BVLC)开发,使用C++编写,提供了高效的神经网络实现和训练工具。
405 1
|
Java Spring
AutoConfigureBefore不生效
AutoConfigureBefore不生效
391 0
|
消息中间件 微服务
微服务通信:RPC、消息队列和事件驱动架构的比较
在微服务架构中,微服务之间的通信是至关重要的。为了实现松耦合、高效可靠的通信,开发人员可以选择不同的通信方式,包括RPC(远程过程调用)、消息队列和事件驱动架构。本文将对这三种常见的微服务通信方式进行比较,探讨它们的特点、适用场景和优缺点,帮助开发人员选择合适的通信方式。
685 0
|
数据处理 Python
Python读取excel数据进行处理后生成新的Excel
Python读取excel数据进行处理后生成新的Excel
537 0
|
监控 数据库