iOS 网速测试

最近公司项目需求需要做一个网络测试的小功能,在网上查了很多资料,关于网络测速差到了两种方式:

(参考: [Joy___]https://www.jianshu.com/u/9c51a213b02e)

方案1:通过上传和下载数据包,使用 TotalSize / TotalTime 来计算真实的上传和下载速率是多少。

方案2:通过读取网卡数据来计算,读取上一秒的整体流量消耗 T1,然后读取当前的流量消耗 T2,那么 T2 - T1 其实可以表示为当前的一个网速情况。同时这个流量数据是可以区分蜂窝网络、Wi-Fi的,也可以区分哪些是上行流量,那些是下行流量。

两种方案各有优劣,可以在合适的场合来选择对应的方案

第一种方案感觉是比较准确,这个时候是真实的在下载或上传数据,比较充分的利用了当前的带宽,计算的网速也比较接近真实的网速值。但是蜂窝网络下,会消耗用户的少量流量。

第二种方案在下载和上传东西时,计算的值和第一种方案比较接近。但是如果当前系统内没有 App 在被使用,处于静止状态的话,其实当前读取的流量值是比较小的,无法反映出网速情况,但是可以实时反映流量消耗状况。

最终我选择的是第一种方式去做,我个人理解的是想要进行网络测速就一定要有流量的消耗,若是采用第二种方式会造成测速不准确,第一种虽然会造成用户在流量方面有一定的损耗,但是考虑到目前公司对于网络测速的应用环境所以最佳的方案就是第一种。

关于具体怎么测速就不多说,我参考了@Joy___的代码。

先上效果图:


0469F579-F482-4CC1-8D3E-43771615030D.png

效果图中显示的指针滑过的部分和未滑过的部分有颜色的区分,所以我采用了CAShapeLayer自己绘制的表盘:

UIBezierPath *outArc=[UIBezierPath bezierPathWithArcCenter:LuCenter radius:self.arcRadius startAngle:startAngle endAngle:endAngle clockwise:YES];
    CAShapeLayer* shapeLayer=[CAShapeLayer layer];
    shapeLayer.lineWidth=lineWitdth;
    shapeLayer.fillColor=filleColor.CGColor;
    shapeLayer.strokeColor=strokeColor.CGColor;
    shapeLayer.path=outArc.CGPath;
    shapeLayer.lineCap=kCALineCapRound;
    [self.layer addSublayer:shapeLayer];

绘制最外圈的弧线,起始角度我设置的是-M_PI_4*5,结束角度设置的为M_PI_4

CGFloat perAngle=self.arcAngle/divide;
    for (NSInteger i = 0; i<= divide; i++) {
        //我们需要计算出每段弧线的起始角度和结束角度
        CGFloat startAngel = (self.startAngle+ perAngle * i);
        CGFloat endAngel   = startAngel + perAngle/5;
        
        UIBezierPath *tickPath = [UIBezierPath bezierPathWithArcCenter:LuCenter radius:self.scaleRadius startAngle:startAngel endAngle:endAngel clockwise:YES];
        CAShapeLayer *perLayer = [CAShapeLayer layer];
        perLayer.fillColor = [UIColor clearColor].CGColor;
        if((remainder!=0)&&(i % remainder) == 0) {
            perLayer.strokeColor = strokeColor.CGColor;
            perLayer.lineWidth   = scaleLineBigWidth;
            
        }else{
            perLayer.strokeColor = strokeColor.CGColor;;
            perLayer.lineWidth   = scaleLineNormalWidth;
            
        }
        
        perLayer.path = tickPath.CGPath;
        [self.layer addSublayer:perLayer];
        
    }

画刻度线,我画了100个刻度线,具体什么情况可以自己去设置

/**
 *  画刻度值,逆时针设定label的值,将整个仪表切分为N份,每次递增仪表盘弧度的N分之1
 *
 *  @param divide 刻度值几等分
 */
-(void)DrawScaleValueWithDivide:(NSInteger)divide{
    CGFloat textAngel =self.arcAngle/divide;
    if (divide==0) {
        return;
    }
    for (NSUInteger i = 0; i <= divide; i++) {
        CGPoint point = [self calculateTextPositonWithArcCenter:LuCenter Angle:-(self.endAngle-textAngel*i)];
        NSString *tickText = [NSString stringWithFormat:@"%ld",(divide - i)*10/divide];
        //默认label的大小23 * 14
        UILabel *text = [[UILabel alloc] initWithFrame:CGRectMake(point.x - 8, point.y - 7, 30, 14)];
        text.text = tickText;
        text.font = [UIFont systemFontOfSize:14.f];
        text.textColor = RGBA(255, 255, 255, 0.33);
        text.textAlignment = NSTextAlignmentLeft;
        [self.textArray addObject:text];
        [self addSubview:text];
    }
}
//默认计算半径-10,计算label的坐标
- (CGPoint)calculateTextPositonWithArcCenter:(CGPoint)center Angle:(CGFloat)angel {
    CGFloat x = (self.scaleValueRadius - 15)* cosf(angel);
    CGFloat y = (self.scaleValueRadius - 15)* sinf(angel);
    return CGPointMake(center.x + x, center.y - y);
}

画刻度值,我采用的是计算出每一个刻度值的位置,然后添加label

- (void)refreshDashboard:(CGFloat)currentValue {
    // 控制范围
    if (currentValue > self.maxValue) {
        currentValue = self.maxValue;
    }
    if (currentValue <= self.minValue) {
        currentValue = self.minValue;
    }
    
  //移除上一次的layer,否则不会出现进度条变化的效果
    if (self.progressLayer) {
        [self.progressLayer removeFromSuperlayer];
    }
    if (self.insideProgressLayer) {
        [self.insideProgressLayer removeFromSuperlayer];
    }
    
    if (self.layerArray.count > 0) {
        for (CAShapeLayer *layer in self.layerArray) {
            [layer removeFromSuperlayer];
        }
        [self.layerArray removeAllObjects];
    }
    
    // 百分比
    CGFloat percent = (currentValue - self.minValue) / (self.maxValue - self.minValue);

    // 当前角度
    CGFloat currentAngle = self.startAngle + (fabs(self.endAngle - self.startAngle) * percent);
    
    
    //计算当前指针转动的角度
    CGFloat imageCurrentAngle = M_PI_4*5+(M_PI*3/2 * percent);
    
    // 前景弧形绘制
    [self dashboardDrawPercent:percent startAngle:self.startAngle endAngle:currentAngle imageCurrentAngle:imageCurrentAngle currentValue:currentValue];
}

为了实现指针转动是滑过的部分和未滑过的部分刻度和刻度值颜色不同,我声明了两个属性

//存放刻度layer
@property (nonatomic, strong) NSMutableArray *layerArray;
//存放刻度值label
@property (nonatomic, strong) NSMutableArray *textArray;

刷新进度的时候会走一个循环,在我创建的layerArray中存放的是当前进度下创建出来的刻度,因此我们只需要将这个数组中的layer都从Superlayer中移除就可以了并且清空数组

if (self.layerArray.count > 0) {
        for (CAShapeLayer *layer in self.layerArray) {
            [layer removeFromSuperlayer];
        }
        [self.layerArray removeAllObjects];
    }

实际上刻度值颜色的变化和刻度颜色的变化同理,根据当前的值和textArray中的label.text去做对比

for (UILabel *textLabel in self.textArray) {
        if (currentValue > [textLabel.text floatValue]) {
            textLabel.textColor = [UIColor whiteColor];
        } else {
            textLabel.textColor = RGBA(255, 255, 255, 0.33);
        }
    }

这样基本上的核心部分就完成了,还有一个指针的旋转,我使用的时候一张指针图片

- (UIImageView *)innerCursorImageView {
    if (!_innerCursorImageView) {
        UIImageView *innerCursorImageView = [[UIImageView alloc] init];
        innerCursorImageView.image = [UIImage imageNamed:@"指针"];
        innerCursorImageView.frame = CGRectMake(self.bounds.size.width / 2 - 10, Calculate_radius - 125, innerCursorImageView.image.size.width * 0.5, innerCursorImageView.image.size.height * 0.5);
        // 旋转成和仪表盘角度一致
        [self setAnchorPoint:CGPointMake(0.5, 0.816993) forView:innerCursorImageView];//因为layer位置会发生变化,需要重新设置
        innerCursorImageView.transform = CGAffineTransformMakeRotation(M_PI_4*5);//图片绕某一点旋转
        _innerCursorImageView = innerCursorImageView;
    }
    return _innerCursorImageView;
}

这里遇到了一个坑,开始的时候我直接设置imageView.transform,发现是围绕imageView的中心的去旋转的,时间指针转动的时候围绕的是指针偏下部的一个点,所以需要设置一下,调用- (void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view这个方法:

- (void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view {
    CGPoint oldOrigin = view.frame.origin;
   //anchorPoint(锚点):以自己的左上角为原点(0, 0)
   //它的x、y取值范围都是0~1,默认值为(0.5, 0.5)
    view.layer.anchorPoint = anchorPoint;
    CGPoint newOrigin = view.frame.origin;
    CGPoint transition;
    transition.x = newOrigin.x - oldOrigin.x;
    transition.y = newOrigin.y - oldOrigin.y;
    view.center = CGPointMake (view.center.x - transition.x, view.center.y - transition.y);
}

imageView转动的起始点是M_PI_45 结束点是M_PI_43
实际上当角度为M_PI时,指针指向的正好是时钟的6点的位置根据我们的设计图来看得出的起始点和结束点,然后再每一次刷新时去计算新的结束点。

Demo:https://github.com/rongwf/NetworkSpeed

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容