Skip to content

Commit 8129ecc

Browse files
author
chenbing
committed
实时显示 FPS 指示器
1 parent b66f84e commit 8129ecc

File tree

4 files changed

+278
-0
lines changed

4 files changed

+278
-0
lines changed

AYYUtils/AYYUtils.xcodeproj/project.pbxproj

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
3414EF831E1653CE008BC725 /* AYYFPSIndicator.m in Sources */ = {isa = PBXBuildFile; fileRef = 3414EF821E1653CE008BC725 /* AYYFPSIndicator.m */; };
1011
342091F21DF80C65001AA421 /* UIView+ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 342091F11DF80C65001AA421 /* UIView+ViewController.m */; };
1112
342C50FB1E03998100DD5E05 /* NSString+Size.m in Sources */ = {isa = PBXBuildFile; fileRef = 342C50FA1E03998100DD5E05 /* NSString+Size.m */; };
1213
3455CA9C1DEEB99800D77786 /* NSDictionary+NullExamine.m in Sources */ = {isa = PBXBuildFile; fileRef = 3455CA9B1DEEB99800D77786 /* NSDictionary+NullExamine.m */; };
@@ -20,6 +21,8 @@
2021
/* End PBXBuildFile section */
2122

2223
/* Begin PBXFileReference section */
24+
3414EF811E1653CE008BC725 /* AYYFPSIndicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AYYFPSIndicator.h; sourceTree = "<group>"; };
25+
3414EF821E1653CE008BC725 /* AYYFPSIndicator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AYYFPSIndicator.m; sourceTree = "<group>"; };
2326
342091F01DF80C65001AA421 /* UIView+ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+ViewController.h"; sourceTree = "<group>"; };
2427
342091F11DF80C65001AA421 /* UIView+ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+ViewController.m"; sourceTree = "<group>"; };
2528
342C50F91E03998100DD5E05 /* NSString+Size.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Size.h"; sourceTree = "<group>"; };
@@ -105,6 +108,8 @@
105108
3455CA9B1DEEB99800D77786 /* NSDictionary+NullExamine.m */,
106109
342091F01DF80C65001AA421 /* UIView+ViewController.h */,
107110
342091F11DF80C65001AA421 /* UIView+ViewController.m */,
111+
3414EF811E1653CE008BC725 /* AYYFPSIndicator.h */,
112+
3414EF821E1653CE008BC725 /* AYYFPSIndicator.m */,
108113
);
109114
path = AYYUtils;
110115
sourceTree = "<group>";
@@ -185,6 +190,7 @@
185190
3455CA9C1DEEB99800D77786 /* NSDictionary+NullExamine.m in Sources */,
186191
342091F21DF80C65001AA421 /* UIView+ViewController.m in Sources */,
187192
342C50FB1E03998100DD5E05 /* NSString+Size.m in Sources */,
193+
3414EF831E1653CE008BC725 /* AYYFPSIndicator.m in Sources */,
188194
349B6F791DDD7FDD004B1475 /* main.m in Sources */,
189195
349B6F911DDD808F004B1475 /* AYYAppRate.m in Sources */,
190196
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//
2+
// AYYFPSIndicator.h
3+
// AYYUtils
4+
//
5+
// Created by 艾呦呦 on 16/12/30.
6+
// Copyright © 2016年 none. All rights reserved.
7+
// Details --> https://github.com/cheenbee/AYYFPSIndicator
8+
9+
#import <Foundation/Foundation.h>
10+
11+
typedef NS_ENUM(NSUInteger, AYYFPSIndicatorPosition) {
12+
AYYFPSIndicatorPositionNormal = 0, // 默认, 位置在屏幕左下角
13+
AYYFPSIndicatorPositionStatusBar = 1 // 位置在状态栏时间显示右侧
14+
};
15+
16+
@interface AYYFPSIndicator : NSObject
17+
18+
+ (AYYFPSIndicator *)sharedInstance;
19+
20+
/** 开始监控并显示当前 fps 值 */
21+
- (void)start;
22+
23+
/** 停止监控并隐藏 fps 值 */
24+
- (void)stop;
25+
26+
/** FPS 指示器位置 默认为:AYYFPSIndicatorPositionNormal */
27+
@property (nonatomic, assign) AYYFPSIndicatorPosition indicatorPosition;
28+
29+
@end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
//
2+
// AYYFPSIndicator.m
3+
// AYYUtils
4+
//
5+
// Created by 艾呦呦 on 16/12/30.
6+
// Copyright © 2016年 none. All rights reserved.
7+
//
8+
9+
#import "AYYFPSIndicator.h"
10+
#import <UIKit/UIKit.h>
11+
12+
#define kUIScreenSize [UIScreen mainScreen].bounds.size
13+
#define kFPSLabelNormalWidth 60
14+
#define kFPSLabelNormalHeight 30
15+
#define kFPSLabelInStatusBarWidth 50
16+
#define kStatusBarHeight 20
17+
#define kNavigationBarHeight 64
18+
#define kTabBarHeight 49
19+
20+
@interface AYYFPSIndicator ()
21+
22+
@property (nonatomic, strong) UILabel *fpsLabel;
23+
@property (nonatomic, strong) CADisplayLink *displayLink;
24+
@property (nonatomic, assign) NSUInteger count;
25+
@property (nonatomic, assign) NSTimeInterval lastTimeStamp;
26+
27+
@end
28+
29+
@implementation AYYFPSIndicator
30+
31+
+ (AYYFPSIndicator *)sharedInstance {
32+
static AYYFPSIndicator *indicator;
33+
34+
static dispatch_once_t onceToken;
35+
_dispatch_once(&onceToken, ^{
36+
indicator = [[AYYFPSIndicator alloc] init];
37+
});
38+
39+
return indicator;
40+
}
41+
42+
- (instancetype)init {
43+
if (self = [super init]) {
44+
[self setupSubviews];
45+
[self setupDisplayLink];
46+
[self observeNotifications];
47+
}
48+
49+
return self;
50+
}
51+
52+
- (void)dealloc {
53+
[_displayLink invalidate];
54+
[[NSNotificationCenter defaultCenter] removeObserver:self];
55+
}
56+
57+
- (void)setIndicatorPosition:(AYYFPSIndicatorPosition)indicatorPosition {
58+
_indicatorPosition = indicatorPosition;
59+
[self setupFPSLabel:_fpsLabel indicatorPosition:indicatorPosition];
60+
}
61+
62+
63+
#pragma mark - private methods
64+
- (void)setupSubviews {
65+
_indicatorPosition = AYYFPSIndicatorPositionNormal;
66+
67+
_fpsLabel = [[UILabel alloc] init];
68+
[self setupFPSLabel:_fpsLabel indicatorPosition:_indicatorPosition];
69+
70+
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(didPan:)];
71+
[_fpsLabel addGestureRecognizer:panGesture];
72+
73+
[[UIApplication sharedApplication].delegate.window addSubview:_fpsLabel];
74+
}
75+
76+
- (void)setupFPSLabel:(UILabel *)fpsLabel indicatorPosition:(AYYFPSIndicatorPosition)position {
77+
if (position == AYYFPSIndicatorPositionStatusBar) {
78+
CGFloat fpsLabelInStatusBarX = (kUIScreenSize.width - kFPSLabelInStatusBarWidth) *0.5 + kFPSLabelInStatusBarWidth;
79+
fpsLabel.frame = CGRectMake(fpsLabelInStatusBarX, 0, kFPSLabelInStatusBarWidth, kStatusBarHeight);
80+
fpsLabel.backgroundColor = [UIColor clearColor];
81+
fpsLabel.font = [UIFont boldSystemFontOfSize:12];
82+
fpsLabel.textAlignment = NSTextAlignmentRight;
83+
fpsLabel.userInteractionEnabled = NO;
84+
85+
} else {
86+
CGFloat fpsLabelNormalPositionY = kUIScreenSize.height - (kNavigationBarHeight + kTabBarHeight);
87+
fpsLabel.frame = CGRectMake(0, fpsLabelNormalPositionY, kFPSLabelNormalWidth, kFPSLabelNormalHeight);
88+
fpsLabel.layer.cornerRadius = 5;
89+
fpsLabel.clipsToBounds = YES;
90+
fpsLabel.textAlignment = NSTextAlignmentCenter;
91+
fpsLabel.textColor = [UIColor whiteColor];
92+
fpsLabel.backgroundColor = [UIColor colorWithWhite:0.000 alpha:0.700];
93+
fpsLabel.font = [UIFont systemFontOfSize:14];
94+
fpsLabel.userInteractionEnabled = YES;
95+
}
96+
}
97+
98+
- (void)setupDisplayLink {
99+
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];
100+
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
101+
}
102+
103+
- (void)tick:(CADisplayLink *)displayLink {
104+
if (_lastTimeStamp == 0) {
105+
_lastTimeStamp = displayLink.timestamp;
106+
return;
107+
}
108+
109+
_count++;
110+
NSTimeInterval delta = displayLink.timestamp - _lastTimeStamp;
111+
if (delta < 1) return;
112+
_lastTimeStamp = displayLink.timestamp;
113+
float fps = _count / delta;
114+
_count = 0;
115+
116+
UIColor *fpsColor;
117+
if (fps >= 55) {
118+
fpsColor = [UIColor greenColor];
119+
} else if (fps >= 45) {
120+
fpsColor = [UIColor yellowColor];
121+
} else {
122+
fpsColor = [UIColor redColor];
123+
}
124+
125+
NSString *fpsStr = [NSString stringWithFormat:@"%d", (int)roundf(fps)];
126+
NSString *totalStr = [fpsStr stringByAppendingString:@" FPS"];
127+
NSRange fpsRange = [totalStr rangeOfString:fpsStr];
128+
129+
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:totalStr];
130+
[attributedText addAttribute:NSForegroundColorAttributeName value:fpsColor range:fpsRange];
131+
[_fpsLabel setAttributedText:attributedText];
132+
133+
[[UIApplication sharedApplication].delegate.window bringSubviewToFront:_fpsLabel];
134+
}
135+
136+
- (void)observeNotifications {
137+
[[NSNotificationCenter defaultCenter] addObserver:self
138+
selector:@selector(applicationDidBecomeActiveNotification)
139+
name:UIApplicationDidBecomeActiveNotification
140+
object:nil];
141+
142+
[[NSNotificationCenter defaultCenter] addObserver:self
143+
selector:@selector(applicationWillResignActiveNotification)
144+
name:UIApplicationWillResignActiveNotification
145+
object:nil];
146+
}
147+
148+
149+
#pragma mark - operation
150+
- (void)start {
151+
if (!_displayLink) {
152+
[self setupDisplayLink];
153+
}
154+
155+
if (!_displayLink.isPaused) return;
156+
157+
_count = 0;
158+
_lastTimeStamp = 0;
159+
160+
[self show];
161+
}
162+
163+
- (void)stop {
164+
if (_displayLink) {
165+
[_displayLink invalidate];
166+
_displayLink = nil;
167+
}
168+
[self hide];
169+
}
170+
171+
- (void)show {
172+
if (_displayLink.isPaused) {
173+
[_displayLink setPaused:NO];
174+
}
175+
176+
[_fpsLabel removeFromSuperview];
177+
[[UIApplication sharedApplication].delegate.window addSubview:_fpsLabel];
178+
[[UIApplication sharedApplication].delegate.window bringSubviewToFront:_fpsLabel];
179+
}
180+
181+
- (void)hide {
182+
[_displayLink setPaused:YES];
183+
[_fpsLabel removeFromSuperview];
184+
}
185+
186+
187+
#pragma mark - notification
188+
- (void)applicationDidBecomeActiveNotification {
189+
if (_displayLink.isPaused) {
190+
[_displayLink setPaused:NO];
191+
}
192+
}
193+
194+
- (void)applicationWillResignActiveNotification {
195+
[_displayLink setPaused:YES];
196+
}
197+
198+
199+
#pragma mark - gesture
200+
- (void)didPan:(UIPanGestureRecognizer *)gesture {
201+
UIWindow *superView = [UIApplication sharedApplication].delegate.window;
202+
CGPoint position = [gesture locationInView:superView];
203+
204+
switch (gesture.state) {
205+
case UIGestureRecognizerStateBegan:
206+
{
207+
_fpsLabel.alpha = 0.5;
208+
}
209+
break;
210+
211+
case UIGestureRecognizerStateChanged:
212+
{
213+
_fpsLabel.center = position;
214+
}
215+
break;
216+
217+
case UIGestureRecognizerStateEnded:
218+
{
219+
CGFloat fpsLabelX = 0;
220+
if (position.x > kUIScreenSize.width * 0.5) {
221+
fpsLabelX = kUIScreenSize.width - _fpsLabel.frame.size.width;
222+
}
223+
224+
// 防止和 statusBar 下拉手势冲突 , 上方始终保持 20 间距
225+
CGFloat fpsLabelY = position.y + kStatusBarHeight;
226+
if (position.y > kUIScreenSize.height - _fpsLabel.frame.size.height) {
227+
fpsLabelY = kUIScreenSize.height - _fpsLabel.frame.size.height;
228+
}
229+
230+
CGRect newFrame = CGRectMake(fpsLabelX, fpsLabelY, _fpsLabel.frame.size.width, _fpsLabel.frame.size.height);
231+
[UIView animateWithDuration:0.25 animations:^{
232+
_fpsLabel.frame = newFrame;
233+
_fpsLabel.alpha = 1;
234+
}];
235+
}
236+
break;
237+
238+
default:
239+
break;
240+
}
241+
}
242+
243+
@end

0 commit comments

Comments
 (0)