阅读 635

使用 PaintCode 绘制自定义的省份地图控件

Demo链接

准备工作

需要用到的软件 (解压密码 xclient.info)
  • Sketch

    Sketch 是一款Mac上小巧但功能强大的矢量绘图软件,这是专为设计师而打造的矢量绘图软件,拥有简约的设计,调色板,面板,菜单,窗口,控件和功能强大的矢量绘图和文字工具;包含针对UI设计的操作和交互模式,让你设计图标、移动手机UI、网站UI等更加简单高效。

  • PaintCode

    PaintCode 是一款 Mac 上的 iOS 矢量绘图编程软件,无论是程序员还是设计师,PaintCode 能够让你像在PS中画图一样绘制各种UI图形,PaintCode 会自动帮你生成针对 Mac OS X 或 iOS 平台 Objective-C 或 C# 代码,能够节约大量的编程时间,支持将代码直接导入到 Xcode 中或导出为 PSD 文件,支持最新的 iOS 和 iWatch SDK,非常的实用!

地图 svg 文件的获取

推荐使用 HighChart 地图数据,在这里你可以自由的选择国内的某个省份地图下载相应的 svg 文件,这里选择福建省作为一个例子。

image-20191027233307531

使用 Sketch 对下载的 svg 文件进行再加工

由于通过 HighChart 地图数据下载的 svg 文件有一些 logo 文字信息的部分图层是我们开发时不需要的,而且会干扰地图的显示,所以使用 Sketch 去除这些部分。

使用 Sketch 打开下载的 svg 文件,屏蔽这些无用的信息图层

image-20191027234921580

重新导出得新 svg 文件才是我们接下来要用到地图文件

image-20191027235144789

使用 PaintCode 根据 svg 文件生成 Objective-C 代码

通过 PaintCode 打开 svg 文件

image-20191028000419208

我们可以看到 PaintCode 已经自动为我们生成了 Objective-C 代码,但是这只是图块的 UIBezierPath 路径代码,我们还需要添加文字和图钉的相应位置,接下来我们通过 PaintCode 添加这些内容。

  • 添加市名

    添加 Text,设置文字,调节文字位置以及字体大小为11,PaintCode 也自动生成了相应的代码。

    image-20191028001128976

  • 添加图钉位置

    添加图钉 Rect,调节 Rect 的位置,宽高都设置为20。

    image-20191028001948626

  • 导出代码

    按照上面的步骤添加其他的市名和图钉位置,并且导出代码文件。

    image-20191028002543468

整理代码文件

PaintCode 生成的文件包含了图块,文字,图钉三项内容的绘制代码,我们需要对这三项内容进行抽取和整理,这里以福建省为例。

+ (void)drawCanvas2WithFrame: (CGRect)targetFrame resizing: (FujianResizingBehavior)resizing
{
    //// General Declarations
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    //// Resize to Target Frame
    CGContextSaveGState(context);
    CGRect resizedFrame = FujianResizingBehaviorApply(resizing, CGRectMake(0, 0, 308, 340), targetFrame);
    CGContextTranslateCTM(context, resizedFrame.origin.x, resizedFrame.origin.y);
    CGContextScaleCTM(context, resizedFrame.size.width / 308, resizedFrame.size.height / 340);
  
UIColor* strokeColor = [UIColor colorWithRed: 0.8 green: 0.8 blue: 0.8 alpha: 1];
    UIColor* fillColor = [UIColor colorWithRed: 0.486 green: 0.71 blue: 0.925 alpha: 1];

    //// 页面1
    {
        //// chart-(1)
        {
            //// Clipped
            {
                //// Group 编组 5
                {
                    //// Group 6
                    {
                        //// Bezier 形状 2 Drawing
                        UIBezierPath* bezier2Path = [UIBezierPath bezierPath];
                        [bezier2Path moveToPoint: CGPointMake(215.48, 200.9)];
                        [bezier2Path addLineToPoint: CGPointMake(214.85, 200.27)];
                        [bezier2Path addLineToPoint: CGPointMake(215.17, 199.01)];
                        [bezier2Path addLineToPoint: CGPointMake(215.8, 199.32)];
                        [bezier2Path addLineToPoint: CGPointMake(216.43, 198.06)];

                      ......
                        [bezier2Path closePath];
                        [bezier2Path moveToPoint: 
                        [bezier2Path closePath];
                        [fillColor setFill];
                        [bezier2Path fill];
                        [strokeColor setStroke];
                        bezier2Path.lineWidth = 1;
                        bezier2Path.miterLimit = 4;
                        [bezier2Path stroke];
                                             }
                         ......
                }
            }
        }
    }
                 
                          //// Text Drawing
    CGRect textRect = CGRectMake(128, 71, 39, 26);
    {
        NSString* textContent = @"南平市";
        NSMutableParagraphStyle* textStyle = [[NSMutableParagraphStyle alloc] init];
        textStyle.alignment = NSTextAlignmentLeft;
        NSDictionary* textFontAttributes = @{NSFontAttributeName: [UIFont systemFontOfSize: 11], NSForegroundColorAttributeName: UIColor.blackColor, NSParagraphStyleAttributeName: textStyle};

        CGFloat textTextHeight = [textContent boundingRectWithSize: CGSizeMake(textRect.size.width, INFINITY) options: NSStringDrawingUsesLineFragmentOrigin attributes: textFontAttributes context: nil].size.height;
        CGContextSaveGState(context);
        CGContextClipToRect(context, textRect);
        [textContent drawInRect: CGRectMake(CGRectGetMinX(textRect), CGRectGetMinY(textRect) + (textRect.size.height - textTextHeight) / 2, textRect.size.width, textTextHeight) withAttributes: textFontAttributes];
        CGContextRestoreGState(context);
    }
                         
                         ......
                         //// Rectangle Drawing
    UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRect: CGRectMake(135, 56, 20, 20)];
    [UIColor.grayColor setFill];
    [rectanglePath fill];
   											......
        CGContextRestoreGState(context);

}

复制代码
  1. 新建一个 FujianMapPath 文件继承于 ChinaMapPath 类,这个文件用来存放整理分类后的代码
  2. 图块部分

    我们只需要摘取 UIBezierPath 的路径部分代码即可,也就是每一个 bezierPath 在以下代码之前的路径代码(换个说法就是删除每个bezierPath 的以下代码)

                            [fillColor setFill];
                            [bezier2Path fill];
                            [strokeColor setStroke];
                            bezier2Path.lineWidth = 1;
                            bezier2Path.miterLimit = 4;
                            [bezier2Path stroke];
    复制代码

    整理后的 bezierPath 放在 FujianMapPathpathArray 中。

  3. 文字部分

    我们只需要摘取每段 textRect 的 frame 即可,

    CGRect textRect = CGRectMake(128, 71, 39, 26);
    复制代码

    整理后的 textRect 放在 FujianMapPathtextRectArray 中,使用 NSValue存储。文字内容需要另外存储在在 FujianMapPathtextArray 中。

  4. 图钉部分

    我们只需要摘取每段 rectanglePath 的起始点即可,

    UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRect: CGRectMake(135, 56, 20, 20)];
    =>  CGPointMake(135, 56)
    复制代码

    整理后的 point 放在 FujianMapPathpinPointArray 中。

需要注意的是,我们自行绘制的文字部分和图块部分的市区顺序一般是不一致的,所以我们要根据图块顺序调换 textArray、textRectArray 和 pinPointArray 中市区的顺序

使用 CXProvincesMapView 展示我们获取的代码数据

/// 自定义的地图快捷创建方法
/// @param mapPath svg 绘图数据
/// @param mapSize svg 绘图尺寸,即 svg 文件中图层的宽高
/// @param frame   视图控件的frame
- (instancetype)initWithMapPath:(ChinaMapPath *)mapPath andMapSize:(CGSize)mapSize andFrame:(CGRect)frame;
复制代码

示例:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    // 福建省市数据
    FujianMapPath *mapPath = [[FujianMapPath alloc] init];
    self.chinaMapView = [[CXProvincesMapView alloc]initWithMapPath:mapPath andMapSize:CGSizeMake(308, 340) andFrame:CGRectMake(0, 0, self.view.bounds.size.width, 400)];
    _chinaMapView.backgroundColor = [UIColor colorWithRed:230/255.0 green:1.0 blue:1.0 alpha:1.0];
    _chinaMapView.maximumZoomScale = 5.0;
    _chinaMapView.center = self.view.center;
    _chinaMapView.delegate = self;
    //    _chinaMapView.pinAnimation = NO;
    // 直接设置图片
    //    _chinaMapView.pinImage = [UIImage imageNamed:@"pin"];
    // 添加按钮点击
    UIButton *pinButton = [[UIButton alloc]initWithFrame:_chinaMapView.pinView.bounds];
    [pinButton setImage:[UIImage imageNamed:@"pin"] forState:UIControlStateNormal];
    [pinButton addTarget:self action:@selector(pinTest) forControlEvents:UIControlEventTouchUpInside];
    [_chinaMapView.pinView addSubview:pinButton];
    [self.view addSubview:_chinaMapView];
}
复制代码

效果:

image-20191028011542388

总结

以上步骤看起来繁琐,实际上操作起来并不是很复杂,主要的代码部分 PaintCode 已经为我们生成好了,可以在有 svg 图片的前提下生成任意的地图区域控件,CXProvincesMapView 中国省份地图区域的控件也是这样子生成的。假如你不需要文字或者图钉部分,你可以不用另外绘制文字或者图钉,相对应的数组传入空值就行了。

完结散花(^▽^),希望这篇教程对你有帮助!