除了极少部分纯展现的 APP,大部分 APP 都需要通过表单向用户搜集数据。MCCSframework 的表单符合 “MCCS” 的理念。主控制器(C)将表单界面托管给子控制器(S),子控制器通过 Cell 展现表单控件。子控制器将表单控件和模型(M)进行绑定,完成对用户输入数据的搜集。

接下来演示一个 APP 表单的例子。在这个例子中包含了键盘输入、日期选择、拍照/图片上传、下拉列表、true/false 单选框等 APP 中常见的用户输入方式。最终效果如下:

模型

首先需要一个模型,以存储表单中的数据。新建类 NewMeetingParam,类的定义如下:

#import <Foundation/Foundation.h>
#import "MeetingInfo.h"NS_ASSUME_NONNULL_BEGIN@interface NewMeetingParam : NSObject
@property (strong, nonatomic) NSString* id;
@property (strong, nonatomic) NSString* isPublic;// "1" 公开 "0" 不公开
@property (strong, nonatomic) NSString* status;
@property(assign, nonatomic) int effect;
@property (strong, nonatomic) NSString* address;
@property (strong, nonatomic) NSString* title;
@property (strong, nonatomic) NSString* host;
@property (strong, nonatomic) NSString* remark;
@property (strong, nonatomic) NSString* beginTime;
@property (strong, nonatomic) NSString* endTime;
@property (strong, nonatomic) NSDictionary* baseFileStorePlus;
@property (strong, nonatomic) NSString* company;@endNS_ASSUME_NONNULL_END

实现单行文本的输入

我们首先从单行文本的输入开始。这需要分别实现一个 Cell 和一个子控制器。

实现 Cell

新建一个 UICollectionViewCell 子类 MeetingTextFieldCell,勾选 Also create XIB file。

这将创建一个 .h 文件、一个 .m 文件和一个 .xib 文件。打开 .xib 文件,在 IB 中分别拖入一个UIView、一个 UILabel、一个 UITextField。创建必要的约束,效果如下图所示:

分别为三个控件创建 IBOutlet:

#import <UIKit/UIKit.h>
#import <MCCSframework/NibCollectionViewCell.h>NS_ASSUME_NONNULL_BEGIN@interface MeetingTextFieldCell : NibCollectionViewCell
@property (weak, nonatomic) IBOutlet UILabel *lbTitle;
@property (weak, nonatomic) IBOutlet UIView *line;
@property (weak, nonatomic) IBOutlet UITextField *tfText;@endNS_ASSUME_NONNULL_END

实现子控制器

新建类 MeetingAddSC。类的定义如下:

#import <Foundation/Foundation.h>
#import <MCCSframework/SubController.h>
#import "NewMeetingParam.h"NS_ASSUME_NONNULL_BEGIN@interface MeetingAddSC : SubController@property (strong, nonatomic) NewMeetingParam* meetingParam;@endNS_ASSUME_NONNULL_END

meetingParam 用于搜集用户输入,就不多解释了。companyArr 是一个字符串数组,用于提供给下拉列表提供列表数据。

然后是类的实现:

#import "MeetingAddSC.h"
#import <MCCSframework/UIColor+Hex.h>
#import <MCCSframework/dimensions.h>
#import "MeetingTextFieldCell.h"
#import <ReactiveObjC/RACEXTScope.h>@interface MeetingAddSC()<UITextFieldDelegate>@end@implementation MeetingAddSC- (NSInteger)numberOfItems{return 1;}- (CGSize)sizeForItemAtIndex:(NSInteger)index{return CGSizeMake(SCREEN_WIDTH-20 , 60);
}-(UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index{return [self tfCellAtIndex:index title:@"标题" placeholder:@"请输入会议主题" content:_meetingParam.title ];
}
// MARK: - Private
-(MeetingTextFieldCell *)tfCellAtIndex:(NSInteger)index title:(NSString*)title placeholder:(NSString*)placeholder content:(NSString* _Nullable)content{MeetingTextFieldCell* cell = [self.collectionContext dequeueReusableCellOfClass:MeetingTextFieldCell.class forSectionController:self atIndex:index];cell.contentView.backgroundColor = hex_color(0x3c4150);cell.line.backgroundColor = hex_color(0x4a4f5e);cell.line.hidden = NO;cell.lbTitle.text = title;cell.tfText.placeholder = placeholder;cell.tfText.enabled = YES;// 修改 placeholder 颜色NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:placeholder attributes:@{NSForegroundColorAttributeName:[UIColor blackColor],NSFontAttributeName:cell.tfText.font}];cell.tfText.attributedPlaceholder = attrString;cell.tfText.text = content;cell.tfText.delegate = self;cell.tfText.tag = index;return cell;
}// MARK: - UITextFieldDelegate
-(void)textFieldDidEndEditing:(UITextField *)textField{switch (textField.tag) {case 0:{// 标题_meetingParam.title = textField.text;break;}default:break;}
}
@end

代码非常的“MCCS”,相信经过前面的教程,不需要做太多的解释。核心的地方有两处:

  1. tfCellAtIndex:title:placeholder:content 方法

    这个方法被 cellForItemAtIndex 方法调用,用于创建和返回我们刚刚创建的专门用于单行文本输入的 cell MeetingTextFieldCell。之所以不直接将代码写在 cellForItemAtIndex 方法里,是因为便于复用,因为这个表单中需要进行单行输入的 地方有好几处。这个方法你需要仔细阅读一下。

  2. UITextFieldDelegate

    这个子控制器实现了 UITextFieldDelegate 协议。因为我们需要利用这个协议在适当的时机将 TextField 中的文本保存到 meetingParam 中去。如果你仔细看过 tfCellAtIndex:title:placeholder:content 方法,其中有 2 句:

     cell.tfText.delegate = self;cell.tfText.tag = index;
    

    这是将 TextField 的 delegate 设置为子控制器了。然后子控制器实现了 textFieldDidEndEditing 委托方法。在其中根据 TextField 的 tag 属性来判断是哪个 cell 的 TextField。

将子控制器添加到主控制器

在你的控制器中,将子控制器添加到主控制器的代码如下:

self.meetingSC = [MeetingAddSC new];
self.meetingSC.meetingParam = [NewMeetingParam new];[self addSC:self.meetingSC];[self.adapter reloadDataWithCompletion:nil];

运行效果如下:

添加其他 MeetingTextFieldCell

除了会议主题,还有会议地点,会议主持会用到单行文本输入框,因此我们可以在子控制器中,增加两行 MeetingTextFieldCell。

首先,修改 numberOfItems 方法为:

- (NSInteger)numberOfItems{return 3;
}

然后是 cellForItemAtIndex 方法:


-(UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index{if(index == 0){return [self tfCellAtIndex:index title:@"标题" placeholder:@"请输入会议主题" content:_meetingParam.title ];}else if(index==1){return [self tfCellAtIndex:index title:@"地点" placeholder:@"请输入会议地点" content:_meetingParam.address ];}else{return [self tfCellAtIndex:index title:@"主持" placeholder:@"请输入会议主持" content:_meetingParam.host ];}
}

同样需要修改的还有 textFieldDidEndEditing 方法:

// MARK: - UITextFieldDelegate
-(void)textFieldDidEndEditing:(UITextField *)textField{switch (textField.tag) {case 0:{// 标题_meetingParam.title = textField.text;break;}case 1:{// 地点_meetingParam.address = textField.text;break;}default:{// 主持_meetingParam.host = textField.text;break;}}
}

再次运行 APP,现在的界面变成:

实现下拉列表输入

表单中的“单位”一项允许用户从下拉列表进行选择。它的数据来自于一个字符串数组。首先我们在子控制器中增加一个数组属性。打开 MeetingAddSC.h,增加一个属性:

@property (strong, nonatomic) NSArray<NSString*>* companyArr;

我们用 companyArr 数组保存用户能够进行选择的单位列表。在构建子控制器时,我们需要初始化这个数组:

... ...self.meetingSC = [MeetingAddSC new];
self.meetingSC.companyArr = @[@"施工单位",@"监理单位"];... ...

然后打开 MeetingAddSC.m,增加以下几个属性:

@property(assign, nonatomic) BOOL companyListExpanded;// 单位地址是否展开
@property(assign, nonatomic) NSInteger selCompnayIndex;// 选中的单位索引

增加 2 个私有方法:

-(NSInteger)countOfCompanyArr{return (_companyArr ? _companyArr.count : 0);
}
-(BOOL)isCompanyList:(NSInteger)index{return index>3 && index <= 3+self.countOfCompanyArr;
}

前者返回 companyArr 数组元素的个数,后者根据 cell 索引判断该 cell 是否是单位列表中的 cell。

很显然,单位列表只会显示在 index 为 4 和 5 的 cell 上:

所以 isCompanyList 方法使用的逻辑表达式就是:

index>3 && index <= 3+self.countOfCompanyArr

为了简单起见,无论列表是否展开(即 companyListExpanded 无论为 yes 或 no),列表 cell 都是显示的,但是,当 companyListExpanded 为 NO 时,我们会将 cell 的高度设置为 0,从而达到隐藏列表的效果。因此在 sizeForItemAtIndex 方法会改成:

- (CGSize)sizeForItemAtIndex:(NSInteger)index{if([self isCompanyList:index]){return CGSizeMake(SCREEN_WIDTH-20 , _companyListExpanded ? 40 : 0);}else{return CGSizeMake(SCREEN_WIDTH-20 , 60);}
}

首先判定 cell 是否是列表中的 cell,如果是,再判定列表当前的隐藏状态,如果是隐藏状态,高度设置为 0,否者设置为 40。

相应地,numberOfItems 方法也需要改为:

- (NSInteger)numberOfItems{NSInteger i = 4;i = i + self.countOfCompanyArr;return i;
}

然后是 cellForItemAtIndex 方法:

-(UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index{if(index == 0){return [self tfCellAtIndex:index title:@"标题" placeholder:@"请输入会议主题" content:_meetingParam.title ];}else if(index==1){return [self tfCellAtIndex:index title:@"地点" placeholder:@"请输入会议地点" content:_meetingParam.address ];}else if(index==2){return [self tfCellAtIndex:index title:@"主持" placeholder:@"请输入会议主持" content:_meetingParam.host ];}else if(index==3){MeetingTextFieldCell* cell = [self tfCellAtIndex:index title:@"单位" placeholder:@"选择单位" content:_meetingParam.company];cell.tfText.enabled = NO;return cell;}else{if(_companyListExpanded){return [self labelCellAtIndex:index text:_companyArr[index-4]];}else{return [self spacerAtIndex:index];}}

从 index == 3 开始,就是“单位”下拉列表的 cell 了。当 index == 3 时,我们仍然使用单行文本输入 cell 显示,但 TextField 的 enabled 属性设为 NO 了,因为我们不需要用户从键盘输入,而是通过下面两行 cell 中进行选择。

在后面的两个 cell 中,我们使用 labelCellAtIndex:text 方法返回了新的 CollectionViewCell 子类:

-(MeetingCompanyCell*)labelCellAtIndex:(NSInteger)index text:(NSString*)text{MeetingCompanyCell* cell = [self.collectionContext dequeueReusableCellOfClass:MeetingCompanyCell.class forSectionController:self atIndex:index];cell.lbTitle.text = text;cell.contentView.backgroundColor= [UIColor clearColor];cell.line.backgroundColor = hex_color(0x4a4f5e);cell.lineLeading.constant = [self isCompanyListEnd:index] ? 10 : 70;return cell;
}

MeetingCompanyCell 是一个新的 CollectionViewCell 子类。这个 cell 非常简单,上面只有一个 UILabel (用于显示文本)和一个 UIView(用于显示一条分隔线),你可以自己设计这个 cell :

我们可以通过 didSelectItemAtIndex 方法来显示/隐藏单位列表,并处理用户在下拉列表中的点击:

-(void)didSelectItemAtIndex:(NSInteger)index{if(index==3){self.companyListExpanded = !self.companyListExpanded;[self.collectionContext reloadSectionController:self];}else if([self isCompanyList:index]){self.selCompnayIndex = index-4;self.meetingParam.company = self.companyArr[index-4];self.companyListExpanded = NO;[self.collectionContext reloadSectionController:self];}
}

当用户点击到单位 cell 时,我们修改 companyListExpanded 属性,然后刷新子孔子器。这样子控制器会根据修改后的 companyListExpanded 属性动态设置列表 cell 的高度( 0 或者 40),从而隐藏或显示下拉列表。

如果用户选择了下拉列表中的某个 cell,则子控制器会更新自己的状态以及模型,然后刷新子控制器,从而更新 UI。

运行 APP,效果如下图所示:

日期选择

首先导入框架提供的 SimpleDatePicker 组件:

#import <MCCSframework/SimpleDatePicker.h>

在子控制器中声明两个属性,一个用于开始时间,一个用于结束时间:

@property (strong, nonatomic) SimpleDatePicker* beginPicker;
@property (strong, nonatomic) SimpleDatePicker* endPicker;

以及它们的懒加载:

// MARK: - Lazy load
-(SimpleDatePicker*)beginPicker{if (!_beginPicker) {_beginPicker = [SimpleDatePicker new];_beginPicker.dateMode = UIDatePickerModeDateAndTime;_beginPicker.datePicker.locale = [[NSLocale alloc]initWithLocaleIdentifier:@"zh_CN"];@weakify(self)_beginPicker.okBlock = ^(NSDate* date){@strongify(self)NSString *str = dateToString(date,@"yyyy-MM-dd HH:mm");self.meetingParam.beginTime = str;[self.collectionContext reloadSectionController:self];};}return _beginPicker;
}
-(SimpleDatePicker*)endPicker{if (!_endPicker) {_endPicker = [SimpleDatePicker new];_endPicker.dateMode = UIDatePickerModeDateAndTime;_endPicker.datePicker.locale = [[NSLocale alloc]initWithLocaleIdentifier:@"zh_CN"];@weakify(self)_endPicker.okBlock = ^(NSDate* date){@strongify(self)NSString *str = dateToString(date,@"yyyy-MM-dd HH:mm");self.meetingParam.endTime = str;[self.collectionContext reloadSectionController:self];};}return _endPicker;
}

注意,这两个日期选择控件的 okBlock 块中,我们修改了模型值,刷新了 UI。

子控制器中,增加两个实用工具方法:

-(BOOL)isBeginPicker:(NSInteger)index{return index == 4+self.countOfCompanyArr;
}
-(BOOL)isEndPicker:(NSInteger)index{return index == 5+self.countOfCompanyArr;
}

用这两个方法来根据 index 判断 cell 是开始日期选择控件还是结束日期选择控件。

先在原来 cell 数量的基础上 + 2 :

- (NSInteger)numberOfItems{NSInteger i = 6;i = i + self.countOfCompanyArr;return i;
}

cellForIndex 方法修改:

-(UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index{... ...}else if([self isCompanyList:index]){if(_companyListExpanded){return [self labelCellAtIndex:index text:_companyArr[index-4]];}else{return [self spacerAtIndex:index];}}else if([self isBeginPicker:index]){return [self pickCellAtIndex:index title:@"开始时间" content:_meetingParam.beginTime?:@"请选择"];}else if([self isEndPicker:index]){return [self pickCellAtIndex:index title:@"结束时间" content:_meetingParam.endTime?:@"请选择"];}else{return [self spacerAtIndex:index];}
}

其中调用到的 pickerCellAtIndex:title:content 方法定义如下:

-(MeetingPickerCell*)pickCellAtIndex:(NSInteger)index title:(NSString*)title content:(NSString*)content{MeetingPickerCell* cell = [self.collectionContext dequeueReusableCellOfClass:MeetingPickerCell.class forSectionController:self atIndex:index];cell.lbTitle.text = title;cell.lbContent.text = content;cell.line.backgroundColor = hex_color(0x4a4f5e);cell.line.hidden = NO;return cell;
}

MeetingPickerCell 是我们新增的 CollectionViewCell。打开 MeetingPickerCell.xib,大概是这个样子的:

分别是 4 个 UI 元素:1 个 UIView、2 个UILable、一个 UIImageView。

显示 SimpleDatePicker 控件

这个比较简单,调用它的 show 方法即可:

-(void)didSelectItemAtIndex:(NSInteger)index{... ...}else if([self isBeginPicker:index]){[self.beginPicker show];}else if([self isEndPicker:index]){[self.endPicker show];}
}

运行 APP,如下图所示:

点击开始/结束时间:

单选

新建一个 CollectionViewCell 子类 MeetingRadioCell,本例表单中单选所用到的 cell 如下图所示,相信你已经能够很熟练地创建出来:

MeetingRadioCell

MeetingRadioCell 中需要编写点代码。在 MeetingRadioCell.h 中,声明 2 个属性:

@property (nonatomic,assign) BOOL selectedBool;@property (strong, nonatomic) void(^onselect)(BOOL on);

前者用于保存用户选择( Yes/No)。后者提供一个块,允许当用户改变选择后通知子控制器执行一些操作,比如刷新 UI。

在 MeetingRadioCell.m 中:

@implementation MeetingRadioCell
@synthesize selectedBool=_selectedBool;- (void)awakeFromNib {[super awakeFromNib];// Initialization codeself.selectedBool =NO;
}
- (IBAction)onBtNo:(id)sender {self.selectedBool=NO;
}- (IBAction)onBtYes:(id)sender {self.selectedBool = YES;
}
-(BOOL)selectedBool{return _selectedBool;
}
-(void)setSelectedBool:(BOOL)on{_selectedBool= on;_btYes.selected = on;_btNo.selected = !on;if(_onselect)_onselect(on);
}
@end

很简单,无论用户选择了哪个按钮,另外一颗按钮将会 deselect,同时调用 onselect 块。

修改子控制器

在子控制器中,cell 数目再次加 1:

- (NSInteger)numberOfItems{NSInteger i = 7;i = i + self.countOfCompanyArr;return i;
}

然后定义一个工具方法:

-(BOOL)isPublicCell:(NSInteger)index{return index == 6+self.countOfCompanyArr;
}

在 cellForItemAtIndex 中增加这个 cell:

 ... ...}else if([self isPublicCell:index]){MeetingRadioCell* cell=[self radioCellAtIndex:index];cell.selectedBool = @"1".equal(_meetingInfo.isPublic);return cell;}... ...

这是 cell 的实例化方法:

-(MeetingRadioCell*)radioCellAtIndex:(NSInteger)index{MeetingRadioCell* cell = [self.collectionContext dequeueReusableCellOfClass:MeetingRadioCell.class forSectionController:self atIndex:index];cell.line.backgroundColor = hex_color(0x4a4f5e);cell.line.hidden = NO;@weakify(self)cell.onselect = ^(BOOL on) {@strongify(self)self.meetingInfo.isPublic = on ? @"1":@"0";[self.collectionContext reloadSectionController:self];};return cell;
}

这是运行 APP 后的效果:

多行文本输入

首先仍然是从创建 cell 开始。新建一个 CollectionViewCell 子类 MeetingRemarkCell。设计其 UI 如下:

创建相应的连接。

在子控制器 MeetingAddSC 中,cell 的数量再次 +1:

- (NSInteger)numberOfItems{NSInteger i = 8;i = i + self.countOfCompanyArr;return i;}

定义一个工具方法:

-(BOOL)isRemarkCell:(NSInteger)index{return index == 7+self.countOfCompanyArr;
}

在 cellForItemAtIndex 方法中:

 }else if([self isRemarkCell:index]){MeetingRemarkCell* cell = [self    remarkCellAtIndex:index];cell.tvContent.text= _meetingInfo.remark;return cell;}

cell 的实例化方法:

-(MeetingRemarkCell*)remarkCellAtIndex:(NSInteger)index{MeetingRemarkCell* cell = [self.collectionContext dequeueReusableCellOfClass:MeetingRemarkCell.class forSectionController:self atIndex:index];cell.line.backgroundColor = hex_color(0x4a4f5e);cell.line.hidden = NO;cell.tvContent.delegate = self;cell.tvContent.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"请输入备注" attributes:@{NSForegroundColorAttributeName:[UIColor blackColor],NSFontAttributeName:cell.lbTitle.font}];;return cell;
}

然后,让子控制器实现 UITextViewDelegate 协议:

// MARK: - UITextViewDelegate
-(void)textViewDidEndEditing:(UITextView *)textView{_meetingInfo.remark= textView.text;
}

这样,就可以将用户输入保存到实体模型。
APP 运行后的效果:

接下来是什么?

接下来就是文件上传了。iOS 的图片(拍照/相册)上传要稍微复杂一些,我们计划在下一篇教程中单独介绍。感谢阅读,下次教程中再见。

MCCSframework 教程(四)表单相关推荐

  1. 下列关于html5表单的多样输入方式,IT兄弟连 HTML5教程 HTML5表单 多样的输入类型1...

    原标题:IT兄弟连 HTML5教程 HTML5表单 多样的输入类型1 HTML5拥有多个新的表单输入类型,这些新特性提供了更好的输入控制和验证.并不是所有的主浏览器都支持新的input类型,不过我们可 ...

  2. 简单表单提交php教程,php教程之表单提交实例

    一个超简单的初学者用的php教程之表单提交实例有需要的朋友参考一下. 下面我们将创建一个复杂的表单,代码如下所示. 实例代码如下: 姓名: 密码: 年龄: 16">小于16 16-30 ...

  3. 简单表单提交php教程,php教程之表单提交实例_PHP教程

    一个超简单的初学者用的php教程之表单提交实例有需要的朋友参考一下.下面我们将创建一个复杂的表单,代码如下所示. 代码如下 复制代码 姓名: 密码: 年龄: 16">小于16 16-3 ...

  4. Flask教程(十)表单处理Flask-WTF

    软硬件环境 windows 10 64bit anaconda3 with python 3.7 pycharm 2020.1.2 flask 1.1.2 flask_wtf 0.14.3 简介 we ...

  5. 零基础HTML教程(23)--表单

    点此查看本系列全部文章 零基础Java全栈教程 文章目录 1. 任务背景 2. 任务目标 3. 相关知识点 3.1 表单的属性 3.2 表单的写法 4. 任务实操 5. 任务总结 1. 任务背景 之前 ...

  6. Python+Django+SAE系列教程14-----使表单更安全

    还记得我们上一章提到过的加入页面吗? 加入完以后我们注意一下地址栏: 表单里的数据赤裸裸的显示在了地址栏中,这时候假设我们改动一下内容 刷新,这样数据库里面就会又加入了一条数据,也就是说用户假设知道表 ...

  7. html中内联的form,bootstrap3.0教程之表单(form)使用详解

    本文主要讲解的是表单,这个其实对于做过网站的人来说,并不陌生,而且可以说是最为常用的提交数据的Form表单.本文主要来讲解一下内容: 1.基本案例 2.内联表单 3.水平排列的表单 4.被支持的控件 ...

  8. dreamweaver 正则表达式为属性值加上双引号_IT兄弟连 HTML5教程 HTML5表单 新增的表单属性3...

    9 novalidate novalidate是属性规定在提交表单时不应该验证form和input域.novalidate属性适用于的类型有:text.search.url.telephone.ema ...

  9. php表单yii2,yii2教程-ActiveForm表单组件

    简介 yii2中最常用的组件activeform,通过对activeform的灵活运用,能有效的提升开发效率,所以这个是不得不说的一个yii2组件,那么下面就来了解一下yii2.0的ActiveFor ...

  10. 菜鸟教程 php表单验证码,PHP完整表单实例 | 菜鸟教程

    PHP 完整表单实例 本章节将介绍如何让用户在点击"提交(submit)"按钮提交数据前保证所有字段正确输入. PHP - 在表单中确保输入值 在用户点击提交按钮后,为确保字段值是 ...

最新文章

  1. 【Android】 01. APP 进程启动和 ActivityThread 的关系
  2. 2.0版本的日历控件在ie8显示不全的解决办法
  3. Go 语言编程 — 程序运行环境
  4. Fedora 12 安装配置subversion
  5. Linux CP文件夹略过目录的解决
  6. scanf 输入十六进制_在C语言中使用scanf()输入一个十六进制值
  7. Redis高可用sentinel
  8. Yii2 behaviors中verbs access的一些理解
  9. C++函数的返回值是指针
  10. [08001] Could not create connection to database server. Attempted reconnect 3 times. Giving up.解决办法
  11. 编译时Collected errors
  12. 昆仑通泰历史数据导出到u盘_昆仑通态(mcgsTpc)触摸屏 U盘功能包下载步骤
  13. oracle sql的优化方法
  14. 【Python+Appium】开展自动化测试(八)swipe()滑动页面
  15. Win10系统下激活系统和office2013教程
  16. 使用python切割图片
  17. 使用thinkadmin内置WeChatDeveloper发送公众号模板消息
  18. Win10系统台式机如何调节系统亮度
  19. 智能照明控制系统在城市夜景照明工程中的应用
  20. Unity(如何把方形图片整成圆形)

热门文章

  1. 用C语言调用.bat批处理命令
  2. 迎来智能数据分析的新时代
  3. 简约大气昼夜双色导航主题模板/WordPress导航主题模板
  4. readelf 显示文件完整段表
  5. SpringBoot-项目2-收货地址(新增,修改,删除,设为默认收货地址)
  6. win10计算机磁盘图标,win10系统本地磁盘图标显示异常如何恢复
  7. 分享在工作生活中更好地运用思维导图使用攻略
  8. python之自动生成器(持续更新)
  9. faster rcnn源码解析1
  10. 震惊·X话最少的~“原理图更新到PCB时出现Unknown Pin: PinXXX 问题解决方案”