Is there any way to measure scroll performance in an iPhone app, e.g. updates per second? I'm trying various techniques to improve the scrolling performance but it's sometimes hard to judge if they're actually having an effect.
if you have a scroll delegate, you can implement the method scrollViewDidScroll:. Specifically:
//header:
#interface MyClass : NSObject <UIScrollViewDelegate> {
CFAbsoluteTime last;
int updateCount;
CFTimeInterval timeTotal;
}
#end
//implementation:
#implementation MyClass
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CFAbsoluteTime time = CFAbsoluteTimeGetCurrent();
if (last > 0) {
timeTotal += time - last;
++updateCount;
if (timeTotal > 1) {
NSLog(#"Updates Per Second: %.2f", updateCount / timeTotal);
updateCount = 0;
timeTotal = 0;
}
}
last = time;
}
#end
Something like that. It's untested, so the logging may be incorrect. But to use it, just assign your delegate to the scrollview.delegate property.
Related
I have an iOS app with about 50 views. I want to perform some operation after every 5th screen that the user visits. I know I can create a sort of global counter variable and update that on viewDidLoad of each view, and if count is 5, then perform that operation, and reset that counter variable. Is there a better, more efficient way of doing this? Also looking ahead, if I require to alter something, I would rather do it in a single file than all of my views. Would really appreciate some inputs on this.
I would create a singleton class to keep track of your counter logic, create a base class for all of your view controllers and then make your call to the counter singleton in the viewDidLoad of your base class.
I think something like this would work for you:
#interface ViewCountManager()
#property(nonatomic) NSInteger viewCount;
#end
#implementation ViewCountManager
#define kOperateOnCount 5
+(ViewCountManager *)viewCountManager
{
static ViewCountManager *viewCountManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
viewCountManager = [[self alloc] init];
});
return viewCountManager;
}
-(BOOL)shouldPerformOperation
{
BOOL retValue = NO;
if( self.viewCount == kOperateOnCount - 1 )
{
retValue = YES;
self.viewCount = 0;
}
else
{
self.viewCount++;
}
return retValue;
}
#end
#implementation CountedViewController
-(void)viewDidLoad:(BOOL)animated
{
[super viewDidLoad:animated];
BOOL shouldPerform = [[ViewCountManager viewCountManager] shouldPerformOperation];
[self performOperation];
}
#end
all. I'm trying to follow a tutorial on making a ball bounce around the screen of an iPhone. The tutorial constructs the application in a MVC scheme. I'm having trouble wrapping my head around this concept when it comes to the drawRect method in the View implementation.
This is my Model header file:
#import <Foundation/Foundation.h>
#import "TestView.h"
#define BALL_SIZE 20.0
#define VIEW_WIDTH 320.0
#define VIEW_HEIGHT 460.0
#interface TestModel : NSObject
{
TestView* ball;
CGPoint ballVelocity;
CGFloat lastTime;
CGFloat timeDelta;
}
- (void) updateModelWithTime:(CFTimeInterval) timestamp;
- (void) checkCollisionWithScreenEdges;
#property (readonly) TestView* ball;
#end
The tutorial instructs me the user to override the init method of NSObject. I've also included the methods for controlling the "animation" logic:
- (id) init {
self = [super init];
if (self) {
ball = [[TestView alloc] initWithFrame: CGRectMake(0.0, 0.0, BALL_SIZE, BALL_SIZE)];
// Set the initial velocity for the ball
ballVelocity = CGPointMake(200.0, -200.0);
// Initialize the last time
lastTime = 0.0;
}
return self;
}
- (void) checkCollisionWithScreenEdges {
// Left Edge
if (ball.frame.origin.x <= 0) {
ballVelocity.x = abs(ballVelocity.x);
}
// Right Edge
if (ball.frame.origin.x >= VIEW_WIDTH - BALL_SIZE) {
ballVelocity.x = -1 * abs(ballVelocity.x);
}
// Top Edge
if (ball.frame.origin.y <= 0) {
ballVelocity.y = abs(ballVelocity.y);
}
// Bottom Edge
if (ball.frame.origin.y >= VIEW_HEIGHT - BALL_SIZE) {
ballVelocity.y = -1 * abs(ballVelocity.y);
}
}
- (void) updateModelWithTime:(CFTimeInterval) timestamp {
if (lastTime == 0.0) {
// initialize lastTime if first time through
lastTime = timestamp;
} else {
// Calculate time elapsed since last call
timeDelta = timestamp - lastTime;
// Update the lastTime
lastTime = timestamp;
[self checkCollisionWithScreenEdges];
// Calculate the new position of the ball
CGFloat x = ball.frame.origin.x + ballVelocity.x * timeDelta;
CGFloat y = ball.frame.origin.y + ballVelocity.y * timeDelta;
ball.frame = CGRectMake(x, y, BALL_SIZE, BALL_SIZE);
}
}
The View implementation file is the following:
#import "TestView.h"
#implementation TestView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect) rect {
}
#end
Finally, my View Controller:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
gameModel = [[TestModel alloc] init];
[self.view addSubview:gameModel.ball];
// Set up the CADisplayLink for the animation
gameTimer = [CADisplayLink displayLinkWithTarget:self selector:#selector(updateDisplay:)];
// Add the display link to the current run loop
[gameTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void) updateDisplay:(CADisplayLink *) sender {
[gameModel updateModelWithTime:sender.timestamp];
}
OK, so now that I've provided a look at the structure of the code (hopefully I've given enough) I can get to my question. So when I add anything to drawRect a new object is drawn and does not get "animated" by the model logic methods.
Right now I have a bouncing square. When I try to fill the square with an ellipse in drawRect, I get a new object, drawn how I want, that just sits at 0,0 while the bouncing square is still active.
I'm sure I'm missing something really big here, but I've been banging my head against the wall for hours and can't figure it out. Any help would be greatly appreciated!
A couple of things here:
1- Have you overriden the view class in SB to TestView?
2- Looking at your code, I do not see how your model is connected to your View through your controller. Recall from the MVC model, that the Controller is on top and talks down (pyramid style) to the model and the view and so, somewhere in your view controller:
a) you need to instantiate your model
b) get values from your model and pass them to your view variables - which would be called in drawrect
Last but not least, lookup setNeedsDisplay which is the way to call and refresh the view.
Hope this helps without spoiling the assignment.
I am writing an iPhone camera App. When user is about to take a picture, I would like to check if the iPhone is shaking and wait for the moment that there is no shaking and then capture the phone.
How can I do it?
Anit-shake feature is quite a complicated feature to pull off. I reckon it's a combination of some powerful blur detection/removal algorithms, and the gyroscope on iPhone.
You may start by looking into how to detect motion with the iPhone, and see what kind of results you can get with that. If it's not enough, start looking into shift/blur direction detection algorithms. This is not a trivial problem, but is something that you could probably accomplish given enough time. Hope that Helps!
// Ensures the shake is strong enough on at least two axes before declaring it a shake.
// "Strong enough" means "greater than a client-supplied threshold" in G's.
static BOOL L0AccelerationIsShaking(UIAcceleration* last, UIAcceleration* current, double threshold) {
double
deltaX = fabs(last.x - current.x),
deltaY = fabs(last.y - current.y),
deltaZ = fabs(last.z - current.z);
return
(deltaX > threshold && deltaY > threshold) ||
(deltaX > threshold && deltaZ > threshold) ||
(deltaY > threshold && deltaZ > threshold);
}
#interface L0AppDelegate : NSObject <UIApplicationDelegate> {
BOOL histeresisExcited;
UIAcceleration* lastAcceleration;
}
#property(retain) UIAcceleration* lastAcceleration;
#end
#implementation L0AppDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[UIAccelerometer sharedAccelerometer].delegate = self;
}
- (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
if (self.lastAcceleration) {
if (!histeresisExcited && L0AccelerationIsShaking(self.lastAcceleration, acceleration, 0.7)) {
histeresisExcited = YES;
/* SHAKE DETECTED. DO HERE WHAT YOU WANT. */
} else if (histeresisExcited && !L0AccelerationIsShaking(self.lastAcceleration, acceleration, 0.2)) {
histeresisExcited = NO;
}
}
self.lastAcceleration = acceleration;
}
// and proper #synthesize and -dealloc boilerplate code
#end
I Googled it and found at How do I detect when someone shakes an iPhone?
I'm trying to see where my expensive queries are and want to measure if it's the query that's the bottleneck, or something else. And also measure if certain queries are faster than others within our app. I was wondering how I could do this with Instruments. I see that there's a time profiler tool and was wondering if I could use this.
I tried running Time Profiler, clicking on the button that presents my viewController that accesses the database. I don't see my sql call itself and was wondering what the best way to measure the time a method takes from start to finish. Thanks.
Why you don't print in console the time when it starts and when it finish. I do that in many languages like iOS/Java[Android]/PHP. To debug plugins times
I hope it helps
For finding time differences between calls, I usually use a StopWatch class that uses the machine time (at least milliseconds, maybe microseconds accuracy).
This version is designed specifically for autorelease and objective-c. It has a method, stopWithContext:, which will also log a message with the time. This is handy for start/stop measurements in multiple functions. Tailor it as you see fit.
I have a c++ version as well if needed. [You can find the c++ version here][1].
StopWatch.h
#import <Foundation/Foundation.h>
#interface StopWatch : NSObject
{
uint64_t _start;
uint64_t _stop;
uint64_t _elapsed;
}
-(void) Start;
-(void) Stop;
-(void) StopWithContext:(NSString*) context;
-(double) seconds;
-(NSString*) description;
+(StopWatch*) stopWatch;
-(StopWatch*) init;
#end
StopWatch.m
#import "StopWatch.h"
#include <mach/mach_time.h>
#implementation StopWatch
-(void) Start
{
_stop = 0;
_elapsed = 0;
_start = mach_absolute_time();
}
-(void) Stop
{
_stop = mach_absolute_time();
if(_stop > _start)
{
_elapsed = _stop - _start;
}
else
{
_elapsed = 0;
}
_start = mach_absolute_time();
}
-(void) StopWithContext:(NSString*) context
{
_stop = mach_absolute_time();
if(_stop > _start)
{
_elapsed = _stop - _start;
}
else
{
_elapsed = 0;
}
NSLog([NSString stringWithFormat:#"[%#] Stopped at %f",context,[self seconds]]);
_start = mach_absolute_time();
}
-(double) seconds
{
if(_elapsed > 0)
{
uint64_t elapsedTimeNano = 0;
mach_timebase_info_data_t timeBaseInfo;
mach_timebase_info(&timeBaseInfo);
elapsedTimeNano = _elapsed * timeBaseInfo.numer / timeBaseInfo.denom;
double elapsedSeconds = elapsedTimeNano * 1.0E-9;
return elapsedSeconds;
}
return 0.0;
}
-(NSString*) description
{
return [NSString stringWithFormat:#"%f secs.",[self seconds]];
}
+(StopWatch*) stopWatch
{
StopWatch* obj = [[[StopWatch alloc] init] autorelease];
return obj;
}
-(StopWatch*) init
{
[super init];
return self;
}
#end
The class has a static stopWatch method that returns an autoreleased object.
Once you call start, use the seconds method to get the elapsed time. Call start again to restart it. Or stop to stop it. You can still read the time (call seconds) anytime after calling stop.
Example In A Function (Timing call of execution)
-(void)SomeFunc
{
StopWatch* stopWatch = [StopWatch stopWatch];
[stopWatch Start];
... do stuff
[stopWatch StopWithContext:[NSString stringWithFormat:#"Created %d Records",[records count]]];
}
I'm looking for a little help. I'm usually pretty good at finding what I need, but this one's tricky.
Here's the scene for my test app: I have 2 scrollviews that are 1024x85 and they only move horizontal. I also have a UISwitch below them. Above the scrollviews I have two labels that display the content offset of each scrollview as it moves (so I can see what's going on).
What I want to do: After the user slides each of the views side to side I would like to use the UISwitch to lock those scrollviews together wherever they may be sitting.
This is the updated code:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint p = scrollOne.contentOffset;
CGPoint r = scrollTwo.contentOffset;
// Print the contentOffset labels
scrollOneLabel.text = [NSString stringWithFormat:#"%.2f", p.x];
scrollTwoLabel.text = [NSString stringWithFormat:#"%.2f", r.x];
// If lock is on, the distance between offsets is locked (but limited to max and min)
if (lockSwitch.on) {
NSInteger offset = scrollOne.contentOffset.x - scrollTwo.contentOffset.x;
if (scrollView == scrollOne) {
NSInteger maxOffset = scrollTwo.contentSize.width - scrollTwo.frame.size.width;
[scrollTwo setContentOffset: CGPointMake(MIN(MAX(0.0,scrollOne.contentOffset.x - offset), maxOffset), 0.0)];
} else if (scrollView == scrollTwo) {
NSInteger maxOffset = scrollOne.contentSize.width - scrollOne.frame.size.width;
[scrollOne setContentOffset: CGPointMake(MIN(MAX(0.0,scrollTwo.contentOffset.x + offset), maxOffset), 0.0)];
}
}
// If the lock is not on, both move independently
}
It locks the scrollviews together, but as soon as I move one of the scrollviews the second scrollview jumps to the same content offset as the first. I'm trying to lock them where they are at that moment instead of lining them up when the user touches one of them.
Thank is in advance for any help.
So basically you want to keep the offset between the two constant? In that case you need a variable, let's call it offset, which would be scrollOne.contentOffset.x - scrollTwo.contentOffset.x and after locking them just react to the delegate method
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// If lock is on, the distance between offsets is locked (but limited to max and min)
if (lockSwitch.on) {
if (scrollView == scrollOne) {
NSInteger maxOffset = scrollTwo.contentSize.width - scrollTwo.frame.size.width;
[scrollTwo setContentOffset: CGPointMake(MIN(MAX(0.0,scrollOne.contentOffset.x - offset), maxOffset), 0.0)];
} else if (scrollView == scrollTwo) {
NSInteger maxOffset = scrollOne.contentSize.width - scrollOne.frame.size.width;
[scrollOne setContentOffset: CGPointMake(MIN(MAX(0.0,scrollTwo.contentOffset.x + offset), maxOffset), 0.0)];
}
}
// If the lock is not on, both move independently
}
EDIT: You have to set the offset when the UISwitch changes state, not on every scrolling event. Have something like this tied to your UISwitch with the [addTarget:selector:forControlEvents:] method. Remember, the offset has to be a global variable in your class.
- (void)lockChanged:(id)sender {
UISwitch *lock = sender;
if (lock.on) {
offset = (int)(scrollOne.contentOffset.x - scrollTwo.contentOffset.x);
} else {
offset = 0;
}
}