日期: 2021 年 6 月 26 日

随机获取列表中的一个IP地址进行代理

随机获取列表中的一个IP地址进行代理
如果是json文件,需要读取文件,导入json模块进行转换后再进行处理。
with open(‘ip.json’,’r’) as w:
  json.loads(f.read())
# 随机获取一个ip进行代理
from urllib import request
# 导入随机模块
import random
# 获取所有代理
def getProxy():
        data = [
            {“host”: “116.31.75.100”, “port”: “3128”},
            {“host”: “218.66.253.146”, “port”: “8800”},
            {“host”: “218.66.253.144”, “port”: “8800”}
        ]
        proxies = []
        for p in data:
            proxy = {
                ‘http’: ‘http://’ + p[‘host’] + ‘:’ + p[‘port’],
                ‘https’: ‘http://’ + p[‘host’] + ‘:’ + p[‘port’]
            }
            # 将proxy 追加到proxies列表中
            proxies.append(proxy)
        # 返回proxies
        return proxies
# 获取一个opener
def getOpener(proxies):
    proxy = random.choice(proxies)
    proxy_handler = request.ProxyHandler(proxy)
    opener = request.build_opener(proxy_handler)
    return opener
# 制作下载器
def downloader(opener, req, proxies, timeout=5, retry=2):
    ”’
    :param opener: 发请求
    :param req: 请求对象
    :param proxies:  代理列表
    :param timeout:  超时
    :param retry: 重试
    :return:
    ”’
    try:
        response = opener.open(req,timeout=timeout)
        return response.read()
    except Exception as e:
        print(e)
        if retry > 0:
            opener = getOpener(proxies)  # 换了一个代理
            return downloader(opener,req,proxies,retry=retry-1)
if __name__ == ‘__main__’:
    proxies = getProxy()
    opener = getOpener(proxies)
    # 查看ip是否切换成功
    base_url = ‘http://www.baidu.com/s?wd=ip’
    res = downloader(opener,base_url,proxies)
    print(res.decode(‘utf-8’))

Linux Ubuntu系统升级Python3版本至Python3.9版本步骤

Linux Ubuntu系统升级Python3版本至Python3.9版本步骤
写了一个Python脚本,但是不能一直在windows上运行,虽然不怕占内存,但是影响我电脑正常开关机,刚好有几台服务器,装的是linux系统Ubuntu,所以就把Python文件放在了服务器,安装对应模块后发现python3.5.2已经不行了,对应的包也不能正常使用,所以就升级Python3版本,网上也有很多把Python2升级的,升级谁都无所谓,主要是能正常使用。下面结合我自己的升级方式给大家说一下步骤,因为看很多人要不是用wget,要不就是PPA,但是后边的步骤有点多。我就精简了一下,虽然我自己成功了,但是可能大家在用同样方法升级时依然有各种问题,可以留言或者自行百度。
# 我也使用的wget ,我下载到了home中
wget https://www.python.org/ftp/python/3.9.0/Python-3.9.0.tgz
# 在home中解压
tar -zxf Python-3.9.0.tgz
# 进入python3.9
cd Python-3.9.0
# 编译文件  时间大概有1-3分钟
./configure –prefix=/usr/local/python3
# 编译好后,会有另外一个提示,让run ./configure xxx,忘了复制了
./configure xxxx
# 安装
make && make install
# 结束
 安装结束,提示PIP安装成功的提示,不过pip不是*新版本,自己升级就行。
 直接输入python3就进入到了python3的环境中,也可以查看下python3 -V 的版本,得出已经是*新版本了。
这个方法要比其他人的简单,不用来回链接进行指向。
 我要去愉快的运行我的脚本去咯。

pyinstaller 提示FileNotFoundError: [WinError 2] 系统找不到指定的文件

pyinstaller 提示FileNotFoundError: [WinError 2] 系统找不到指定的文件
上次使用pyinstaller就碰到 ucrtbase.dll 的问题,当时可能是因为权限或者杀毒软件的问题,把360退出然后在通过pyinstaller打包一次就解决问题,但是隔了一段时间后再次使用pyinstaller打包,又碰到了  ucrtbase.dll 的问题,这次提示的不少权限问题,而是 :  FileNotFoundError: [WinError 2] 系统找不到指定的文件。: ‘C:\\Users\\AppData\\Roaming\\pyinstaller\\bincache00_py39_64bit\\ucrtbase.dll’
那根据提示,已知是找不到指定文件,然后去提示的这个C盘目录下去找 ucrtbase.dll 文件,发现相关目录是真的没有这个文件,然后通过搜索去解决问题,回答和解决方法都是花里胡哨,然后就想着去下载一个ucrtbase.dll 文件,终于在dll上找到了。https://cn.dll-files.com/ucrtbase.dll.html 需要哪个版本就下载哪个版本,下载后就把这个压缩包解压,然后把dll文件放进提示缺失的文件夹即可。
再次运行pyinstaller即可完美运行,一个狗屁pyinstaller状况白出….

python命令是如何操作文件file的?

python命令是如何操作文件file的?

 

无论哪一个操作系统,亦或是哪一门语言,当使用到一些高级层次的时候,必然会和文件打交道,因为所有的内容不可能都在内存中存储和传输。用到文件,就必然会有创建文件、打开文件、写文件、复制文件、删除文件这些操作,下面我们来看看在python中是如何利用命令来操作file文件的。

 

python操作file的命令及使用

1.打开和关闭文件

file object= open(file_name [, access_mode][, buffering]),通过open命令来打开一个文件。file_name是指文件的路径,access_mode是打开的方式,包括只读r、写入w和追加a。r取readonly的首字母,w取write的首字母,a取append的首字母。buffering代表缓冲区的大小。如果取负值,寄存区的缓冲大小则为系统默认。

File 对象的 close()方法刷新缓冲区里任何还没写入的信息,并关闭该文件,这之后便不能再进行写入。在java中也是利用close来关闭文件,一般都会配合flush一起使用。

2.写文件

write()方法可将任何字符串写入一个打开的文件。需要重点注意的是,Python字符串可以是二进制数据,而不是仅仅是文字。write()方法不会在字符串的结尾添加换行符(‘\n’)。

例如:#!/usr/bin/python

# -*- coding: UTF-8 -*-

# 打开一个文件

fo = open(“welcome.txt”,”w”)

fo.write(“Welcome to Qingdao!\nHave a good rest!\n”)

# 关闭打开的文件

fo.close()

这段代码是将两行字符串写入welcome.txt文本中,写完后关闭文件。

3.读文件

读文件使用read命令,read后面跟字符的数量,如下面示例,是指从welcome.txt中读取10个字符,也就是”Welcome to”。

fo = open(“welcome.txt”,”r+”)

str = fo.read(10)

结语

本节先介绍文件的打开关闭和读写操作,下一节我们接着学习文件的查找、重命名、删除等命令的使用方法。

 

爬虫篇 | Python爬虫学前普及

爬虫篇 | Python爬虫学前普及

【摘要】*近整理一个爬虫系列方面的文章,不管大家的基础如何,我从头开始整一个爬虫系列方面的文章,让大家循序渐进的学习爬虫,小白也没有学习障碍,那么今天,环球网校的小编就来针对Python讲讲爬虫学前普及,希望今天的文章对您可以有所帮助。

恩,准备进入正题了!*近一段时间没有怎么更新公众号,主要就是在做爬虫教程的一些准备工作,看看爬虫需要用到那些技术,然后做个计划出来,确定一下学习课程中缝,这不今天就先列出一些玩爬虫需要的准备工作!

Python爬虫这门技术你可以做得很简单,你也可以玩得很深入.打比方用简单的爬虫方式爬取1000万条数据可能需要一周时间,但如果你的爬虫玩得比较厉害,你可以采用分布式爬虫技术1天就能完成了1000万条数据。虽然都是爬虫,但这就是菜鸟与大牛的区别!这就和太*拳似的,易学难精!

这里面的技术点挺多的!现在来简单聊聊爬虫需要涉及的知识点。

网页知识

html,js,css,xpath这些知识,虽然简单,但一定需要了解。你得知道这些网页是如何构成的,然后才能去分解他们.

HTTP知识

一般爬虫你需要模拟浏览器的操作,才能去获取网页的信息

如果有些网站需要登录,才能获取更多的资料,你得去登录,你得把登录的账号密码进行提交

有些网站登录后需要保存cookie信息才能继续获取更多资料

正则表达式

有了正则表达式才能更好的分割网页信息,获取我们想要的数据,所以正则表达式也是需要了解的.

一些重要的爬虫库

url,url2,requests

beautiulSoup4,re,lxml

数据库

爬取到的数据我们得有个地方来保存,可以使用文件,也可以使用数据库,这里我会使用mysql,还有更适合爬虫的MongoDB数据库,以及分布式要用到的redis 数据库

爬虫框架

PySpider和Scrapy 这两个爬虫框架是非常NB的,简单的爬虫可以使用urllib与urllib2以及正则表达式就能完成,但高级的爬虫还得用这两个框架。这两个框架需要另行安装。后面一起学习.

反爬虫

有时候你的网站数据想禁止别人爬取,可以做一些反爬虫处理操作。打比方百度上就无法去查找淘宝上的数据,这样就避开了搜索引擎的竞争,淘宝就可以搞自己的一套竞价排名

分布式爬虫

使用多个redis实例来缓存各台主机上爬取的数据。

爬虫要学的东西还是挺多的,想把爬虫玩得666,基本就是这些知识点吧!好了,上面的东西我也只是粗略整理,笔误在所难免,后面我们会一起来学习爬虫知识吧!可以为您带来帮助。以上的内容都可以为您的python学习之路带来便利,小编祝您学习之路顺利。

HTTP接口的服务端和客户端的实现

Json大量字段案例(300多个key),需要做一个客户中心抽调各个系统的客户信息的功能,在每个系统中需要写客户端代码,在客户中心系统需要写服务端代码。

客户端:

json串截图如下,注意在json结构中有对象也有数组也就是集合。在java类和json对象的互相转换过程中,如果将json串转换成java类对象,在里层的特殊类型,也就是[]集合的,我们需要进行特殊的处理。
%title插图%num

1.创建线程:(向客户中心推送客户信息时,前提是不能影响本系统的流程,也就是说不管你借口中的方法是否报错借口是否通都不能影响源代码的正常秩序,因为我们新增了线程),内部类。

try {

Thread thread = new Thread(new Runnable() {

public void run() {

//次数写客户端调用接口的相关方法

}

});

thread.start();

} catch (Exception e) {

e.printStackTrace();

}

根据json的结构构建出对应的java类来。一层一层的写java类,查询出所有需要的信息赋值到java类对象中后,将对象转化成可以推送的json对象

JSONObject fromObject = JSONObject.fromObject(customerCenter,jsonExcludeEmpty());

private JsonConfig jsonExcludeEmpty(){//去除空的json键值对

JsonConfig jsonConfig = new JsonConfig();

jsonConfig.setJsonPropertyFilter(new PropertyFilter() {

@Override

public boolean apply(Object source, String name, Object value) {

return value == null;

}

});

return jsonConfig;

}

调用HTTP工具类推送信息调用接口,*后获得接口的返回值result.

同样在获取数据本系统做保存时,调用接口后获得返回值result

JSONObject jsonObject = JSONObject.fromObject(result);

String status = jsonObject.getString(“status”).trim();

//过滤掉不需要处理的json对象

JsonConfig  config=new JsonConfig ();

config.setExcludes(new String[]{“educationInfos”,”carInfo”,”houseInfo”});

JSONObject dataJSON = new JSONObject().fromObject(data,config);

//json转java对象特殊的类型需要单独处理,比如list [ ]中括号

Map<String, Class> classMap = new HashMap<String, Class>();

classMap.put(“phones”, CustomerCenterPhone.class);

classMap.put(“companyPhones”, CustomerCenterPhone.class);

classMap.put(“emails”, CustomerCenterEmail.class);

……………………..
CustomerCenter customerCenter =(CustomerCenter)JSONObject.toBean(dataJSON,CustomerCenter.class,classMap);

这样就获得到了*外层整体的对象CustomerCenter ,在这个对象中,我做的这个项目有300多个字段。里面的对象和集合还是比较多的,在做的时候要仔细的核对好。

服务端:

http服务端的代码一般不是在service类或是接口中写,而是直接写在controller中,因为是http的嘛,就和一个页面访问一样先进入到controller层,类的注解不变还是@Controller@RequestMapping(value=”/ApplicationController”)两个后面的是访问的名称。在方法上有了一些变化,先看下面的代码:
@RequestMapping(value=”/approveResult”,method = RequestMethod.POST)
public void approveResult(@RequestBody PharosResultPojo pharosResultPojo,HttpServletRequest request,HttpServletResponse response){
//PharosResultPojo 这个是接受数据的java类
//里面直接写相应的功能,在处理完后,需要给调用者返回处理的结果
PrintWriter out;
out = response.getWriter();
JSONObject result = new JSONObject();
result.put(“result”, “ok”);
out.write(result.toString());
out.flush();
out.close();
}
//我们看到在除了平时要使用的方法访问名称后,又加了个参数method = RequestMethod.POST,这个就是规定你http服务端的这个接口方法是post还是get方式。在方法参数列表中要加上这个注解@RequestBody 以及HttpServletRequest request,HttpServletResponse response两个参数。在controller中写了以上的内容后我们只需要完成*后一步http服务端就完成了。那么我们的java代码已经写好了,想要和页面上的一样访问到我们的后台,那我们还需要一个什么操作呢?既然是接口,那么一般肯定是其它的系统访问,我们知道的是自己系统的类其它类是不能直接去访问的,我们需要将这个类给取消拦截,也就是让其它系统可以访问,这一步是在配置文件中实现的,每个公司的项目的结构一般都不一样,有很多公司是在applicationContext-security.xml这个xml中配置,
<b:bean id=”resourceDetailsService”
class=”*********.security.ResourceDetailsServiceImp”>
<b:property name=”filterURL”>
<b:map>
<b:entry key=”/ApplicationController/**” value=”ROLE_ANONYMOUS” />
直接将这个类给取消拦截,具体的在xml中的写法,有一些是公司封装的比较详细,如果你做的项目什么都没有的话,那么你可以在网上搜索一下如何在spring mvc项目(我公司用的是spring mvc项目这个框架)中取消controller类的访问拦截,应该很简单的。

献给初学iOS的小盆友们——微博app项目开发之十八搭建发送微博界面(下)

今天应该是这个项目*后一次更新啦,能坚持下来的小盆友真是棒棒哒。这节课我们将会完成发送有图片的微博,实现微博*后一个核心功能。因为微博的功能实在太多太多了,我们不可能把微博的每个功能都一一实现。小伙伴能从中学到*精髓的东西才是*主要的,万变不离其宗,得到处理问题的方法比解决问题本身更重要。

本节内容
添加上传相册视图
选择要上传的图片
代码封装
本节资料
第十八节代码资料

18.1 添加上传相册视图
要想实现发送图片微博功能,我们首先完成三件事情:
*个就是点击工具栏上的图片按钮能立马跳转到系统相册内
第二个就是把选中的图片加载到发送界面上
第三个就是创建网络请求,发送图片微博了。

经过分析可以知道,我们选择后的图片要先显示到发送界面上,也就是文字下方。所以我们要自定义一个相册视图YGComposePhotos专门用来存储和显示选择的图片。在其头文件内添加一个UIImage image属性,用来给其传递图片:

@property(nonatomic,strong) UIImage *image;

然后重写setImage方法,以及layOutSubviews方法,给图片设置位置:

-(void)setImage:(UIImage *)image
{
_image =image;
UIImageView *imageView = [[UIImageView alloc]init];
imageView.image = image;
[self addSubview:imageView];
}

-(void)layoutSubviews
{
[super layoutSubviews];
NSInteger colums = 3;
CGFloat margin = 10;
CGFloat wh = (self.width -(colums -1)*margin)/colums;
CGFloat x=0;
CGFloat y = 0;

NSInteger col = 0;
NSInteger row = 0;

for (int i= 0; i<self.subviews.count; i++) {
UIImageView *imageV = self.subviews[i];
col = i%colums;
row = i/colums;
x = col*(margin +wh);
y = row *(margin+wh);
imageV.frame = CGRectMake(x, y, wh, wh);
}
}

 

这里在set方法里,每添加一个子控件都会调用layOutSubview。

然后在控制器内添加相册视图:

-(void)setUpPhotosView
{
YGComposePhotos *photosView = [[YGComposePhotos alloc]initWithFrame:CGRectMake(0, 70, self.view.width, self.view.height – 70)];
_photosView = photosView;

photosView.backgroundColor = [UIColor clearColor];
[_textView addSubview:photosView];
}

 

18.2 选择图片
我们首先给图片按钮设置代理,当其被点击的时候,跳转到系统相册,头文件代理方法为:

#import <UIKit/UIKit.h>
@class YGComposeToolBar;

@protocol YGComposeToolBarDelegate<NSObject>

@optional
-(void)composeToolBar:(YGComposeToolBar *)toolBar didClickBtn:(NSInteger )index;

@end

 

当按钮被点击时给代理发送消息:

– (void)btnClick:(UIButton *)button
{

// 点击工具条的时候
if ([_delegate respondsToSelector:@selector(composeToolBar:didClickBtn:)]) {
[_delegate composeToolBar:self didClickBtn:button.tag];
}

}

 

我们在添加按钮的时候,给每个按钮设置一个tag标签,这样就知道是工具条上哪个按钮被点击了:

btn.tag = self.subviews.count;
1
在YGComposeViewController内执行代理方法:

#pragma mark – 点击按钮调用
-(void)composeToolBar:(YGComposeToolBar *)toolBar didClickBtn:(NSInteger)index
{
if(index ==0)
{
//弹出系统相册

UIImagePickerController *imagePicker = [[UIImagePickerController alloc]init];
imagePicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;

imagePicker.delegate = self;

[self presentViewController:imagePicker animated:YES completion:nil];

}

}

 

这里要让控制器遵守图片选择控制器的代理方法,因为当选择图片完毕后,我们要回到发送界面,并把选择的图片交给发送界面控制器的相册视图。因为我们以后发送图片时候,要知道发送什么图片,所以要设置一个可变数组保存刚才选中的图片,代码如下:

#pragma mark 选择图片完成时

-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
NSLog(@”%@”,info);
UIImage *image = info[UIImagePickerControllerOriginalImage];
//传递图片
_photosView.image = image;
//保存图片
[self.images addObject:image];
//回到首页
[self dismissViewControllerAnimated:YES completion:nil];
_rightItem.enabled =YES;
}

 

18.2 发送图片微博
发送图片微博就要再次利用微博的api文档了,找到发送图片微博的接口,了解需要什么url以及参数。在compose方法内,判断是否有文字输入,如果没有,就自动添加“分享图片”,因为微博不允许只发送图片。
代码如下:

#pragma mark- 发送微博
– (void)compose
{
//新浪上传文字不能未空 分享图片
//判断有没有图片

if (self.images.count) {
_rightItem.enabled =NO;
UIImage *image =self.images[0];
NSString *status = _textView.text.length?_textView.text:@”分享图片”;
//创建参数模型
YGComposeParam *param = [YGComposeParam param];
param.status = status;

//创建请求管理者二进制数据 不能拼接到param
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
[mgr POST:@”https://upload.api.weibo.com/2/statuses/upload.json” parameters: param.keyValues constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
//上传的文件在这里拼接到formdata
/**
* filedata 要上传到文件二进制
* name 参数名称 pic
* filename 上传到服务器的文件名称
* mimetype 文件类型
*
*/
NSData *imgData = UIImagePNGRepresentation(image);

[formData appendPartWithFileData:imgData name:@”pic” fileName:@”meinv.png” mimeType:@”image/png”];
//上传多张
//[formData appendPartWithFileData:imgData name:@”pic” fileName:@”meinv.png” mimeType:@”image/png”];
} success:^(AFHTTPRequestOperation *operation, id responseObject) {
[MBProgressHUD showSuccess:@”发送图片成功”];
[self dismissViewControllerAnimated:YES completion:nil];
_rightItem.enabled =YES;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
_rightItem.enabled =YES;

}];

}else{
//发送文字
[self sendTitle];
}
}

这里我们把发送文字微博的方法抽取了出来,方便管理。
这时候你就可以试验下发送图片是否能成功啦,我的试验效果如下:

18.3 封装代码
*后我们可以看到,发送compose方法内代码太多了,需要精简。我们首先设计compose内代码如下,就会少很多:

#pragma mark- 发送微博
– (void)compose
{
//新浪上传文字不能未空 分享图片
//判断有没有图片

if (self.images.count) {
[self sendPic];
}else{
//发送文字
[self sendTitle];
}
}

 

sendPic内代码设计如下:

//发送图片
-(void)sendPic
{
UIImage *image =self.images[0];

NSString *status = _textView.text.length?_textView.text:@”分享图片”;
_rightItem.enabled = NO;
//这里不会循环引用
[YGComposeTool composeWithStatus:status image:image success:^{
[MBProgressHUD showSuccess:@”发送图片成功”];
_rightItem.enabled = YES;
[self dismissViewControllerAnimated:YES completion:nil];
} failure:^(NSError *error) {
[MBProgressHUD showSuccess:@”发送图片失败”];
_rightItem.enabled = YES;

}];
}

这时候你设计好怎么使用composeTool方便后,就可以在YGComposeTool内定义其方法啦:

+(void)composeWithStatus:(NSString *)status image:(UIImage *)image success:(void (^)())success failure:(void (^)(NSError *))failure
{
//创建参数模型
YGComposeParam *param = [YGComposeParam param];
param.status = status;
NSData *imgData = UIImagePNGRepresentation(image);

[YGHttpTool upload:@”https://upload.api.weibo.com/2/statuses/upload.json” parameters: param.mj_keyValues uploadData:imgData success:^(id responseObject) {
if (success) {
success();
}
} failure:^(NSError *error) {
if (failure) {
failure(error);
}
}];

}

这里我们想把*终实际请求的方法让YGHttpTool来实现,所以在YGHttpTool 内定义了如下方法:

+(void)upload:(NSString *)url parameters:(id)parameters uploadData:(NSData *)imgData success:(void (^)(id))success failure:(void (^)(NSError *))failure
{
//创建请求管理者二进制数据 不能拼接到param
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
[mgr POST:url parameters: parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
//上传的文件在这里拼接到formdata
/**
* filedata 要上传到文件二进制
* name 参数名称 pic
* filename 上传到服务器的文件名称
* mimetype 文件类型
*
*/
[formData appendPartWithFileData:imgData name:@”pic” fileName:@”meinv.png” mimeType:@”image/png”];
//上传多张
//[formData appendPartWithFileData:imgData name:@”pic” fileName:@”meinv.png” mimeType:@”image/png”];
} success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (success) {
success(responseObject);
}

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (failure) {
failure(error);
}
}];
}

其实还可以把formdata内append 需要的参数也给封装一下,但是就目前的开发需求,可以不用再封装了。以后其他接口多次用到这些参数的话,再封装也可以。

到此为止我们的微博项目就全部结束啦,希望大家对我的博客还算满意,如果有什么不懂或者需求请与我留言。后面我会更新另外一个微信项目,请大家关注我的博客,获取*新动态。

献给初学iOS的小盆友们——微博app项目开发之十六搭建发送微博界面(上)

从这节课开始我们学习搭建发送微博界面。发送界面看起来简单,但是却包含了许多细节处理。所以我们分为几节课来完成,尽量讲解详细点。

本节内容
第十六节代码资料

本节代码资料
16.1 设置发送界面的导航内容
当我们点击中间加号按钮的时候,微博会弹出一个发送界面,然后才可以编辑。界面效果如下:

%title插图%num
开发之前我们先罗列出界面的几个细节:

1 导航条的字体颜色是橘色,但“发送”按钮一开时是灰色,且不能点击,只有当用户输入文字后,才能恢复。
2 输入文本框有占位符“分享新鲜事…”,且占位符的字体大小应该与输入的文字大小相等。
3 当用户点击输入文字的时候,占位符消失。
4 刚进入发送界面时,键盘是自动弹出的,如果拖拽文本输入框,键盘就会消失。
下面我们根据以上这些要求,一步一步的去完成设置。
首先,创建一个YGComposeViewController,继承自UIViewController。我们想在YGTabBarController内监听加号按钮的点击,当其被点击时,弹出发送界面。这里就需要使用代理方法,因为之前YGTabBarController就已经遵守了YGTabBar 的代理,所以我们就之前的代理内再添加一个方法。

– (void)tabBarDidClickPlusButton:(YGTabBar *)tabBar;
1
然后在YGTabBar内创建plus Button内的方法中监听按钮的点击:

[btn addTarget:self action:@selector(plusClick) forControlEvents:UIControlEventTouchUpInside]
1
点击加号按钮时调用此方法:

#pragma mark – 点击加号按钮时调用
– (void)plusClick
{
// 弹出控制器
if ([_delegate respondsToSelector:@selector(tabBarDidClickPlusButton:)]) {
[_delegate tabBarDidClickPlusButton:self];
}
}

*后我们再YGTabBarController内实现代理:

-(void)tabBarDidClickPlusButton:(YGTabBar *)tabBar
{
// 创建发送微博控制器
YGComposeViewController *composeVc = [[YGComposeViewController alloc] init];
YGNavigationController *nav = [[YGNavigationController alloc] initWithRootViewController:composeVc];

[self presentViewController:nav animated:YES completion:nil];
}

因为我们还要设置发送界面的导航条字体颜色,所以这里直接用我们之前自定义的导航条(应该没忘吧)。
此时运行项目,点击加号按钮,就会看到一个黑色的界面显示出来了。为了美观,也为了让导航条的运动不那么明显,这里把控制器背景颜色设置成白色。然后我们在view DidLoad中设置导航条内容,代码如下:

– (void)setUpNavgationBar
{
self.title = @”发微博”;

// left
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@”取消” style:0 target:self action:@selector(dismiss)];

// right
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setTitle:@”发送” forState:UIControlStateNormal];
[btn setTitleColor:[UIColor orangeColor] forState:UIControlStateNormal];
[btn setTitleColor:[UIColor lightGrayColor] forState:UIControlStateDisabled];
[btn sizeToFit];
UIBarButtonItem *rightItem = [[UIBarButtonItem alloc] initWithCustomView:btn];
rightItem.enabled = NO;
self.navigationItem.rightBarButtonItem = rightItem;

}

本来导航条的右边按钮的设置方法是跟左边按钮一样的,但是我们试过之后发现,全局导航条不能用文字属性设置disable情况下的颜色,所以这里只能使用一个新的UIButton来设置了。
[TMD,我这里插播一句话,估计那些没经过我同意就复制我微博的人也不会看到。没经过我同意就转发我微博的那些网站竟然把时间调到比我原创微博还早半天。其它我都能忍,这我忍不了啊,搞得像我是在抄袭一样。所以这里我声明,我在2016年1月31号凌晨一点发布了此博客,其它都是转载]

16.2 自定义文本输入框textView
首先跟大家解释下UITextView和UITextField两者的不同。
两者*大的区别是:
1、UITextView支持多行输入并且可以滚动显示浏览全文,而UITextField只能单行输入。
2、UITextView继承自UIScrollView,UITextField继承自UIView[UIControl]。
3、UITextview没有placeholder属性 UItextField有placeholder属性
所以这里我们选择UITextView但是要添加一个UILabel子控件,用来显示占位符。
新建YGComposeTextView继承自UITextView,因为我们要让外部可以修改占位符,所以在其头文件中添加一个placeHolder (NSString)属性。
在composeTextView中懒加载一个placeHolderLabel,代码如下:

– (UILabel *)placeHolderLabel
{
if (_placeHolderLabel == nil) {

UILabel *label = [[UILabel alloc] init];

[self addSubview:label];

_placeHolderLabel = label;

}

return _placeHolderLabel;
}

懒加载的意思就是,当控件有值的时候就创建,没有就不创建,跟在init中添加是不一样的。
重载setPlaceHolder方法:

– (void)setPlaceHolder:(NSString *)placeHolder
{
_placeHolder = placeHolder;

self.placeHolderLabel.text = placeHolder;
// label的尺寸跟文字一样
[self.placeHolderLabel sizeToFit];
}

然后将YGComposeViewController内添加YGComposeTextView。 代码如下:

#pragma mark – 添加textView
– (void)setUpTextView
{
YGComposeTextView *textView = [[YGComposeTextView alloc] initWithFrame:self.view.bounds];
_textView = textView;
// 设置占位符
textView.placeHolder = @”abc”;
[self.view addSubview:textView];
}

但是这时候运行项目就会发现,占位符的字体和位置,与输入的文字不一样,如下图,所以我们首先要设置字体大小:

我们在初始化textView时设置其字体:

– (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.font = [UIFont systemFontOfSize:13];
}
return self;
}

然后再重写setFont方法,让占位符的字体大小与文本框的字体大小相同:

– (void)setFont:(UIFont *)font
{
[super setFont:font];

self.placeHolderLabel.font = font;
[self.placeHolderLabel sizeToFit];
}

字体大小解决后,就可以解决占位符的位置了,重写layOutSubviews方法:

– (void)layoutSubviews
{
[super layoutSubviews];

self.placeHolderLabel.x = 5;
self.placeHolderLabel.y = 8;
}

16.3 输入时隐藏占位符
自定义textView还有*后*后一个功能要完成,就是在输入文字的时候自动隐藏占位符。这里采用通知的方法:

// 监听文本框的输入
/**
* Observer:谁需要监听通知
* name:监听的通知的名称
* object:监听谁发送的通知,nil:表示谁发送我都监听
*
*/
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChange) name:UITextViewTextDidChangeNotification object:nil];

当监听到文本框文字变化时,执行textChange方法:

– (void)textChange
{
// 判断下textView有木有内容
if (_textView.text.length) { // 有内容
_textView.hidePlaceHolder = YES;
}else{
_textView.hidePlaceHolder = NO;
}
}

在这里别忘了给YGComposeTextView添加一个BOOL类型的属性,表明外界要传递给它一个是否隐藏placeHolder的信息,以便外部控制里面的内容。
然后重写setHidePlaceHolder:

– (void)setHidePlaceHolder:(BOOL)hidePlaceHolder
{
_hidePlaceHolder = hidePlaceHolder;

self.placeHolderLabel.hidden = hidePlaceHolder;

}

*后别忘了移除通知:

– (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

16.4 自动弹出键盘 与隐藏键盘
这个功能应该是比较基础的内容了,首先在一开始就让键盘自动弹出:

– (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];

[_textView becomeFirstResponder];

}

然后在拖拽UITextView的时候,自动隐藏键盘。用到的方法就是让YGComposeViewController执行UITextView 的代理方法,因为UITextView的只有在内容超过大小的时候才能拖拽,所以不要忘了在setUpTextView内设置textView允许一直可以拖拽

// 默认允许垂直方向拖拽

代理方法如下:

#pragma mark – 开始拖拽的时候调用
– (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
[self.view endEditing:YES];
}

因为细节处理比较多,这节课就到这里吧,请自己主动敲代码哦。动手与不动手对开发过程的理解差别很大的。下节课将会讲解如何设置键盘上方的发送的工具栏,然后发送文字微博。

献给初学iOS的小盆友们——微博app项目开发之一项目初始化

献给初学iOS的小盆友们——微博app项目开发之一 项目初始化
本人自学iOS也有七八个月了,不敢说学到很深入了,但也算入了门。此次微博app项目参考了传智播客培训教材,主要学习内容有架构思想,封装思想,代码重构,业务逻辑等内容,项目涵盖面广泛,讲解易懂,且采用纯代码方式搭建UI,希望对那些没有时间看视频的初学者们有所帮助。相信学习完本套项目,初学者会在编程思想上有一个很大的提升。

内容
项目素材获取
环境配置
自定义tabBarController
修改tabBar内部结构
划分结构
本节资料
*节资料下载

1.1 项目素材获取
首先模仿一个项目,需要图片等素材,单凭自己是做不出来的。本项目提供了基本的图片素材供下载使用,一般项目素材的获取步骤如下:

打开苹果电脑iTunes应用,选择顶部栏“AppStore”项
然后在搜索框内输入“微博”并搜索
找到微博并点击,进去后点击微博图标下的“取得”按钮
输入 iCloud账户密码后即可下载
左上角下载按钮可以显示下载进度
点击顶部栏“我的iPhone应用按钮”
找到微博应用,右键点击,选择“在Finder中显示”
可以看到下载的是ipa类型的文件,使用解压文件解压后即可得到微博应用文件夹
进入文件夹后,选择Payload下的Weibo.app文件,右键点击后选择“显示包内容”,即可看到微博应用所需的所有图片,以后项目模仿都会用得到
1.2 环境配置
开发任何一个大型的应用都需要提前对开发环境进行配置,本次微博项目对Xcode进行了能满足我们模仿要求的简单设置。配置过程也就是修改info.plist文件而已,点击“微博模拟”项目出现的设置页面就是info.plist的图形化界面。配置过程如下:

Bundle Identifier 设置
Bundle Identifier 主要作用有app在上传app store时为了区分不同程序 时使用,开发推送功能时需要,在这里设置成YGWeibo.- – – – 。
Version 版本号
以后迭代开发时,版本号必须比之前的大,在这里不需要设置
Development Target
选择7.0以后的都可以
Devices
选择iPhone
Main Interface
此次项目采用纯代码创建,所以不需要加载storyboard,在这里设置为空,并且把左侧Main.Storyboard,ViewController.h,ViewController.m 文件删除。
Device orientation
只选择portrait
Status Bar Style
选择default后,勾选Hide Status Bar
这里讲一讲,怎么用纯代码得到跟加载main.storyboard有相同效果的界面。首先苹果应用程序的启动步骤是这样的,在一开启时,首先进入main函数,main 函数内主要执行三个步骤,首先创建UIApplication对象,然后创建AppDelegate对象,并且成为UIApplication对象的代理属性,然后开启主线程循环,*后加载info.plist文件,判断是否有main.storyboard,如果有,就会加载main.storyboard。因为我们这里才用纯代码开发,所以info.plist就没有main.storyboard文件了,需要在AppDelegate.m里的*个代理方法中设置窗口,以及创建并加载视图控制器,代码如下,但是此代码非*终的tabBarVC的设置代码,以后会有修改此处只做演示用。此代码就相当于加载Storyboard的步骤。

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 创建窗口
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];

// 为了展示效果 设置背景色为黄色
self.window.backgroundColor = [UIColor yellowColor];

// 创建tabBarViewController
UITabBarController *tabBarVc = [[UITabBarController alloc] init];
tabBarVc.view.backgroundColor = [UIColor redColor];

// 管理子控制器
// 首页
UIViewController *home = [[UIViewController alloc] init];
home.view.backgroundColor = [UIColor greenColor];
[tabBarVc addChildViewController:home];

// 消息
UIViewController *message = [[UIViewController alloc] init];
message.view.backgroundColor = [UIColor blueColor];
[tabBarVc addChildViewController:message];

// 发现
UIViewController *discover = [[UIViewController alloc] init];
discover.view.backgroundColor = [UIColor purpleColor];
[tabBarVc addChildViewController:discover];

// 我
UIViewController *profile = [[UIViewController alloc] init];
profile.view.backgroundColor = [UIColor lightGrayColor];
[tabBarVc addChildViewController:profile];

// 设置窗口的根控制器
self.window.rootViewController = tabBarVc;

// 显示窗口
[self.window makeKeyAndVisible];
return YES;
}

设置AppIcon
直接拖拽素材文件夹内的AppIcon文件夹到Xcode里Assets.xcassets内的AppIcon里即可
设置启动图片
在项目General 设置里面找到Launch Images Source 栏,点击后出现对话框,选择migrate,然后点击其后的灰色按钮,会发现自动跳转到Brand Assets 栏内。打开素材文件夹中的LaunchImages文件夹,拖拽文件到Brand Assets 内,出现绿色加号后放开即可,*后删除Launch Screen File 栏内的东西。
但是这种方法不如直接加载Launch Screen.storyboard文件夹更强大,因为Launch Screen.storyboard可以展示更多的东西,且不需要美工出很多启动图片,因为其可以自动布局以适应各种大小屏幕。如果不删除Launch Screen.storyboard,则会优先加载Launch Screen.storyboard内容。
1.3 自定义tabBarController
1.3.1 更改AppDelegate.m
因为AppDelegate.m文件以后会越来越大,应该自定义一个类专门用于创建和管理tabBarController。在新建文件之前,要设置个前缀,以方便区别和管理。选择左边栏微博模拟项目,找到*右边栏有个“Class Prefix”栏,写上你想起的前缀名称即可,如下图所示。

更改AppDelegate.m内容为如下代码:(首先要新建YGTabBarController)

#import “AppDelegate.h”
#import “YGTabBarController.h”
@interface AppDelegate ()

@end

@implementation AppDelegate

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// 创建窗口

self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];

//创建自定义tabbarcontroller

YGTabBarController *tabBarVc = [[YGTabBarController alloc]init];

// 设置窗口的根控制器

self.window.rootViewController = tabBarVc;

// 显示窗口

[self.window makeKeyAndVisible];
return YES;
}

在此,提醒下UITabBarController内的view在一创建控制器的时候[[YGTabBarController alloc]init]就会加载View,并执行ViewDidLoad方法,所以其View不是懒加载的。但是一般的UIViewController的View是懒加载的。

1.3.2 新建YGTabBarController。
新建一个YGTabBarController类继承自UITabBarController后,然后把之前在AppDelegate内创建与加载子控制器的代码封装在YGTabBarController.m中,并把tabBarVc更改为self。
本小节主要任务就是为了代码的简洁性,抽取两个方法,一个是从ViewDidLoad中抽取设置tabBarController子控件的方法:

-(void)setUpAllChildViewController

因为每加一个子控制器所写的代码都是类似的,所以又在上个方法内又抽取了设置一个子控件的方法。因为每个子控制器都要设置标题,颜色,图片以及其他属性,所以把这些属性都设置为参数传进到方法内。这里提醒一下,如果参数中会传入中文的话,一般把此参数放到*后,例如title参数。

– (void)setUpOneChildViewController:(UIViewController *)vc image:(UIImage *)image selectedImage:(UIImage *)selectedImage title:(NSString *)title

下面就要讲如何设置tabBar按钮上面的文字与图片了。
首先tabBar上的按钮是由对应的子控制器的tabBarItem属性决定。导入素材中tabBar文件夹拖入到项目中。设定按钮title和image。在ios7之后,默认会把UITabBar上面的按钮图片渲染成蓝色,但大多情况下蓝色不是我们想要的颜色,我们想要让tabBar用美工设计好的图片颜色。更改的方法可以是,如图片所示, 把每张带有selected字符后缀的图片的Render As 属性设置为Original Image。

或者用代码更改,在这里我们就写一个UIImage的分类——UIImage+Image.h/.m。目的是为了让以后也方便设置图片渲染。代码如下:

#import “UIImage+image.h”

@implementation UIImage (image)

+ (instancetype)imageWithOriginalName:(NSString *)imageName
{
UIImage *image = [UIImage imageNamed:imageName];

return [image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
}

@end

 

这里对上面的代码做下说明:为什么要使用工厂方法(返回值为instancetype)?因为其默认识别是哪个类调用此方法,并返回该类的对象。

然后在setUpOneChildViewController方法里设置按钮标题等内容。代码如下:

#pragma mark – 添加一个子控制器
– (void)setUpOneChildViewController:(UIViewController *)vc image:(UIImage *)image selectedImage:(UIImage *)selectedImage title:(NSString *)title
{
vc.tabBarItem.title = title;
vc.tabBarItem.image = image;
vc.tabBarItem.badgeValue = @”10″;
vc.tabBarItem.selectedImage = selectedImage;
[self addChildViewController:vc];
}

 

但是在设置字体颜色的时候你会发现,不能用self.tabBarItem.textColor 来设置标题颜色,因为UITabBarItem是模型而不是控件,模型是用来保存数据以决定tabBar上按钮的内容。只有控件才有textColor属性,例如UILabel。所以通过观察UITabBarItem的头文件以及其父类的头文件,发现可以用setTitleTextAttributes 方法 也就是富文本来设置标题颜色。富文本不仅可以设置控件的文字颜色,也可以设置字体空心,阴影,图文混排等。通过UIKit框架内的NSAttributesString.h头文件,可以查找到设置富文本字典等key。这里我们采用全局方法+ initialize 来设置颜色。但在+load方法内也可以。但是两者的调用事件不同,+initialize方法是在*次使用这个类或者子类的时候调用。+load方法是在程序一启动的时候就调用,然后把所有的类加载到内存,且先于main函数执行。

initialize 代码如下:

+ (void)initialize
{
// 这行代码是获取所有的tabBarItem外观标识,这样就容易把不希望改动到外观也更改了
// UITabBarItem *item = [UITabBarItem appearance];
//一般采用带 self的方法,这里self就是指 YGTabBarController

// 获取当前这个类下面的所有tabBarItem
UITabBarItem *item = [UITabBarItem appearanceWhenContainedIn:self, nil];
NSMutableDictionary *att = [NSMutableDictionary dictionary];

//这里也可以用[att setObject:[UIColor orangeColor] forKey:NSForegroundColorAttributeName];
//来设置富文本字典,两种方法都可以。
att[NSForegroundColorAttributeName] = [UIColor orangeColor];

[item setTitleTextAttributes:att forState:UIControlStateSelected];
}

 

1.4 修改tabBar内部结构
在观察实际微博界面会发现,tabBar中间有个用来发微博的加号按钮。这就需要首先调整系统tabBar 结构,改变子控件位置,然后在中间加上加号按钮 。系统的tabBar按钮位置是不能改动的,所以智能自定义一个YGTabBar,继承自UITabBar。然后把系统TabBar替换成自定义的YGTabBar。代码如下:

– (void)viewDidLoad {
[super viewDidLoad];
//添加子控制器
[self setUpAllChildViewController];

// 自定义tabBar
YGTabBar *tabBar = [[YGTabBar alloc] initWithFrame:self.tabBar.frame];

// 利用KVC更改readonly的属性
[self setValue:tabBar forKeyPath:@”tabBar”];
}

代码说明: 这里有两点需要注意,一要判断在哪个方法里去替换系统tabBar。经过测试发现,系统tabBar上的button是在执行完ViewDidLoad 和ViewWillAppear方法后才添加上去的。如下图所示:

从上如可以看到没给方法执行的顺序,所以我们要在添加tabBarButton前替换系统tabBar,这样的话,tabBarButton就会添加到自定义的YZTabBar上面。

第二个问题就是,但是这样赋值self.tabBar == tabBar 会出现错误。因为tabBar是readOnly属性,不能这样赋值。但是我们可以使用KVC赋值,如上面代码所示。

替换完系统tabBar之后,就要重写YGTabBar.m 内的 layOutSubviews方法,来计算每个item的位置。在重写之前先添加一个发微博按钮。代码如下:

– (UIButton *)plusButton
{
if (_plusButton == nil) {

UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setImage:[UIImage imageNamed:@”tabbar_compose_icon_add”] forState:UIControlStateNormal];
[btn setImage:[UIImage imageNamed:@”tabbar_compose_background_icon_add”] forState:UIControlStateHighlighted];
[btn setBackgroundImage:[UIImage imageNamed:@”tabbar_compose_button”] forState:UIControlStateNormal];
[btn setBackgroundImage:[UIImage imageNamed:@”tabbar_compose_button_highlighted”] forState:UIControlStateHighlighted];

// 默认按钮的尺寸跟背景图片一样大
// sizeToFit:默认会根据按钮的背景图片或者image和文字计算出按钮的*合适的尺寸
[btn sizeToFit];

_plusButton = btn;

[self addSubview:_plusButton];

}
return _plusButton;

 

重写layoutsubviews方法,调整子控件位置,代码如下:

– (void)layoutSubviews
{

[super layoutSubviews];
CGFloat w = self.bounds.size.width;
CGFloat h = self.bounds.size.height;

CGFloat btnX = 0;
CGFloat btnY = 0;
CGFloat btnW = w / (self.items.count + 1);
CGFloat btnH = self.bounds.size.height;
int i = 0;
// 调整系统自带的tabBar上的按钮位置
for (UIView *tabBarButton in self.subviews) {
// 判断下是否是UITabBarButton
if ([tabBarButton isKindOfClass:NSClassFromString(@”UITabBarButton” )]) {
if (i == 2) {
i = 3;
}
btnX = i * btnW;
tabBarButton.frame = CGRectMake(btnX, btnY, btnW, btnH);
i++;
}
}
// 设置添加按钮的位置
self.plusButton.center = CGPointMake(w * 0.5, h * 0.5);
}

 

代码说明:在取出tabBar每个button子控件时,需要判断类,可以利用NSClassFromString来根据一个字符串得出其类名。在interface 内添加一个UIButton 属性,并且懒加载,利用sizeToFit来默认按钮尺寸和背景图片一样大。但是我们发现tabBar上的UIBadgeView (如图所示)是继承自UIView的,是系统自带的,没法通过设置图片的方法更改。下一个微博将会讲到如何设置自定义的badgeView。

1.5 划分结构
YGTabbar上现在有五个模块,每个模块都有自己的功能,如果像我们这样直接创建每个模块的ViewController,就不能重写每个控制器的viewDidLoad方法,也就没法添加每个模块的各种功能了。这就需要我们为每个模块创建各自的MVC文件,划分结构如图所示:

IOS APP开发入门案例

1.创建新项目

2.设计布局,main.storyboard中,在控件库中

%title插图%num

3.布局控件关联控制器

%title插图%num

4.设置事件或者显示模式

%title插图%num

5.编写代码:

import UIKit

class ViewController: UIViewController {
    @IBAction func btn1(_ sender: UIButton) {
        label1.text="我的*个程序"
    }
    @IBOutlet weak var label1: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
    @IBOutlet weak var hello: UILabel!
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

*后运行:

点击按钮改变label控件文字

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速