I have a little trouble with a progress bar since iOS 5 came out. The code below was working fine before iOS 5 but with iOS 5 the progress bar is no longer displaying the new progress that is set within a loop.
The code is expected to work like this:
Create the progress bar (works)
In a new background process: Set an initial progress of 0.25 (works)
In the same background process: Update the progress while going thru the loop (worked in iOS 4)
Here's the code for the bar init:
// create a progress bar
UIProgressView *progressBar = [[UIProgressView alloc] initWithFrame:CGRectMake(coverSizeX*0.25, coverSizeY - 34.0, coverSizeX*0.5, 9.0)];
progressBar.progress = 0.0;
progressBar.progressViewStyle = UIProgressViewStyleBar;
and in a different thread it sets a starting point for the progress to 0.25:
// set an initial progress
[progressBar setProgress: 0.25];
a little later it is updating the progress within a loop to display the download progress:
// within a for-loop:
NSNumber *counterPercentage;
for ( pageDownload = 1; pageDownload < pagesToDownload; pageDownload++ ) {
counterPercentage = [[NSNumber alloc] initWithFloat: (float)pageDownload / (float)((float)pagesToDownload)];
[progressBar setProgress: [counterPercentage floatValue]];
[progressBar performSelectorOnMainThread:#selector(setNeedsDisplay) withObject:nil waitUntilDone:YES];
}
… but the progress is not shown on the screen, the progress bar is stuck at the initial 0.25 progress that was set.
Were there any changes with the iOS 5 release that could have broken it?
I've seen a lot of questions like this one since the iOS 5 switch, and I'm not sure why there is a problem only in iOS 5. But mainly because I'm not sure why there wasn't a problem before.
In your code you call [progressBar setProgress: [counterPercentage floatValue]]; from a background thread. This is a UI call and should not be made from a background thread. Also you call setNeedsDisplay which is not necessary to update the progressBar since an UIProgressView knows how to display itself. iOS 5 seems to have made the requirements for updating the UI more stringent, but only to the point of what are best practices anyway.
To my eye this looks like a perfect use for blocks. Using blocks your for loop could be written this way:
for ( pageDownload = 1; pageDownload < pagesToDownload; pageDownload++ ) {
// Other stuff in background
dispatch_async(dispatch_get_main_queue(), ^{
progressBar.progress = ((float)pageDownload/(float)pagesToDownload);
});
// Other stuff in backgroud
}
It does work in iOS 5 and the easiest way to to it is here:
.h file:
IBOutlet UIProgressView *WhateverYouWantToCallIt;
.m file:
[WhateverYouWantToCallIt setProgress:(float) 0.3];
And where it says 0.3, you can put whatever value you like (within 0 to 1)
Related
Pixijs (3.0.8) supports multi-touch as shown in their demos, and I've set up start, move and end listeners for touches on my mobile device.
The touches are registered on a square within the canvas which I'll call the interactiveArea, but the touchend events trigger when let go outside of the area as well. This is behavior that works fine with a single mouse cursor.
However, when using more fingers, having touches with the identifiers 0,1 and 2, only the first touchEnd is triggered outside of the area.
So I press and hold 3 fingers inside the interactiveArea and move them all outside of it. Then I let go of 1, and then the others. I won't be notified of touchEnds for event 0 and 2, and I'd have to re-register 3 touches and let go properly just to get a touchend for 2 triggered!
Any tips on how I can detect all touchends, rather than have it stop on the first touchend? I've tried working with a setTimeout hack as well, but that really doesn't suit my use case.
Edit I've made a basic codepen to demonstrate how touchendoutside is only triggered once. https://codepen.io/Thomaswithaar/pen/EygRjM Do visit the pen on mobile, as it is about touches rather than mouse interactivity.
Holding two fingers on the red square and then moving them out and letting go will only trigger one touchendoutside event.
Looking at the PIXI source code, there is indeed a bug in the Interaction Manager. Here is the method that processes touch end events:
InteractionManager.prototype.processTouchEnd = function ( displayObject, hit )
{
if(hit)
{
this.dispatchEvent( displayObject, 'touchend', this.eventData );
if( displayObject._touchDown )
{
displayObject._touchDown = false;
this.dispatchEvent( displayObject, 'tap', this.eventData );
}
}
else
{
if( displayObject._touchDown )
{
displayObject._touchDown = false;
this.dispatchEvent( displayObject, 'touchendoutside', this.eventData );
}
}
};
You can see in the else statement that a touchendoutside event gets dispatched when displayObject._touchDown is true. But after you release your first finger, it sets the flag to false. That is why you only receive that event once.
I've opened an issue here:
https://github.com/pixijs/pixi.js/issues/2662
And provided a fix here:
https://github.com/karmacon/pixi.js/blob/master/src/interaction/InteractionManager.js
This solution removes the flag and uses a counter instead. I haven't tested it yet, so please let me know if it works.
I am trying to make a (new in 10.10) NSSplitViewItem collapse and uncollapse whilst moving its containing window so as to keep the whole thing "in place".
The problem is that I am getting a twitch in the animation (as seen here).
The code where I'm doing the collapsing is this:
func togglePanel(panelID: Int) {
if let splitViewItem = self.splitViewItems[panelID] as? NSSplitViewItem {
// Toggle the collapsed state
NSAnimationContext.runAnimationGroup({ context in
// special case for the left panel
if panelID == 0 {
var windowFrame = self.view.window.frame
let panelWidth = splitViewItem.viewController.view.frame.width
if splitViewItem.collapsed {
windowFrame.origin.x -= panelWidth
windowFrame.size.width += panelWidth
} else {
windowFrame.origin.x += panelWidth
windowFrame.size.width -= panelWidth
}
self.view.window.animator().setFrame(windowFrame, display: true)
}
splitViewItem.animator().collapsed = !splitViewItem.collapsed
}, completionHandler: nil)
}
}
I am aware of the "Don't cross the streams" issue (from session 213, WWDC'13) where a window resizing animation running on the main thread and a core animation collapse animation running on a separate thread interfere with each other. Putting the splitViewItem collapse animation onto the main thread seems like the wrong approach and I've got a nagging feeling there's a much better way of doing this that I'm missing.
Since I am not finding any documentation on the NSSplitViewItems anywhere (yet) I would appreciate any insights on this.
I have the little test project on GitHub here if anyone wants a look.
Update The project mentioned has now been updated with the solution.
Thanks,
Teo
The problem is similar to the "don't cross the streams" issue in that there are two drivers to the animation you've created: (1) the split view item (2) the window, and they're not in sync.
In the example from the '13 Cocoa Animations talk, constraints were setup to result in the correct within-window animation as only the window's frame was animated.
Something similar could be tried here -- only animating the window's frame and not the split view item, but since the item manages the constraints used to (un)collapse, the app can't control exactly how within-window content animates:
Instead the split view item animation could completely drive the animation and use NSWindow's -anchorAttributeForOrientation: to describe how the window's frame is affected.
if let splitViewItem = self.splitViewItems[panelID] as? NSSplitViewItem {
let window = self.view.window
if panelID == 0 {
// The Trailing edge of the window is "anchored", alternatively it could be the Right edge
window.setAnchorAttribute(.Trailing, forOrientation:.Horizontal)
}
splitViewItem.animator().collapsed = !splitViewItem.collapsed
}
For anyone using Objective C and targeting 10.11 El Capitan.
This did the trick for me, didn't need to set AnchorAttributes.
splitViewItem.collapsed = YES;
I have an MKMapView with an iAd banner on top. Due to this iAd Banner covering the MKMapView's legal label, it is moved. The following code is performed in viewDidLayoutSubviews:
NSLog(#"ViewDidLayoutSubviews called");
UILabel *attributionLabel = [self.mapView.subviews objectAtIndex:1];
if (adBannerViewFrame.origin.y < attributionLabel.frame.origin.y) {
NSLog(#"Banner Y coordinate: %f", adBannerViewFrame.origin.y);
CGRect legalFrame = attributionLabel.frame;
legalFrame.origin.y -= IAD_BANNER_HEIGHT;
attributionLabel.frame = legalFrame;
NSLog(#"Legal y: %f", legalFrame.origin.y);
NSLog(#"Legal x: %f", legalFrame.origin.x);
NSLog(#"Legal width: %f", legalFrame.size.width);
NSLog(#"Legal height: %f", legalFrame.size.height);
}
When I switch from the View Controller containing the map to another VC and back again, the code above works perfect, regardless of orientation; however, when rotating the devices without switching VCs, the legal label disappears on rotation and don't come back until I switch back and fourth between VCs again. The NSLogs print out the exact same for both rotation and VC switching:
ViewDidLayoutSubviews called
Banner Y coordinate: 902/646(rotation dependent)
Legal x: 882/626(rotation dependent)
Legal y: 11
Legal width: 48
Legal height 11
Despite everything looking the same, the label is gone after rotating (not sure if it is moved out of the view or if it actually disappears)
The iAd Banner is reloaded in ViewDidAppear (which is called when VCs are switched back and fourth). However, since the NSLogs print the same for both scenarios, I cannot see how it should make any difference. To be sure, I tried to reload the iAd Banner in ViewDidLayoutSubviews as well, with no different result than before. Anyone experienced anything similar or know how to fix this?
For anyone encountering the same issue, I found a solution. The workaround was to schedule a timer with 0 seconds and have the timer call the method executing the code above.
[NSTimer scheduledTimerWithTimeInterval:0 target:self selector:#selector(test) userInfo:nil repeats:NO];
I assume that something was not completely finished when viewDIDlayoutSubviews was called and that adding a timer (which is not 100 % accurate and will therefore not start immediately), allowed the unknown remaining stuff to complete before the code in the question was executed.
I'm a little new to using MFC and VC++ as such, but I'm doing this as part of a Course and i Have to stick to VC++.
http://www.cprogramming.com/tutorial/game_programming/same_game_part1.html
This is the tutorial I have been following to make a simple samegame. However when i try to display score, the score is getting displayed Underneath or outside my application window, even though I've displayed score before calling updateWindow(). I've tried various methods but I am kinda lost here.
Here is the code I'm using to Display the score:
void CSameGameView::updateScore()
{
CSameGameDoc* pDoc = GetDocument();
CRect rcClient, rcWindow;
GetClientRect(&rcClient);
GetParentFrame()->GetWindowRect(&rcWindow);
int nHeightDiff = rcWindow.Height() - rcClient.Height();
rcScore.top=rcWindow.top + pDoc->GetHeight() * pDoc->GetRows() + nHeightDiff;
rcScore.left=rcWindow.left + 50;
rcScore.right=rcWindow.left + pDoc->GetWidth() - 50;
rcScore.bottom=rcScore.top + 20;
CString str;
double points = Score::getScore();
str.Format(_T("Score: %0.2f"), points);
HDC hDC=CreateDC(TEXT("DISPLAY"),NULL,NULL,NULL);
COLORREF clr = pDoc->GetBoardSpace(-1, -1); //this return background colour
pDC->FillSolidRect(&rcScore, clr);
DrawText(hDC, (LPCTSTR) str, -1, (LPRECT) &rcScore, DT_CENTER);
}
Thank you for any help and I'm sorry if the question doesn't make sense or in ambiguous.
There are several problems with your code:
1. The hDC you are creating is going to have coordinates relative to the desktop window. To paint text in your window, use CClientDC like this: CClientDC dc(this); (see http://msdn.microsoft.com/en-US/library/s8kx4w44%28v=vs.80%29.aspx)
2. The code you have will leak a DC every time the function is called. The method in #1 will fix that.
3. Your paint code should be done in the CView::OnDraw. There you get a DC passed to you and you don't have to worry about creating one with CClientDC. Set the variables you want to draw (e.g. your points or score), store them as class members and draw them in CView::OnDraw.
Don't do the drawing in your updateScore method.
Make sense? Hang in there!
I have a method called startAnimation:
-(void)startAnimation{
1: self.animationTimer=[NSTimer scheduledTimerWithTimeInterval:1/60
target:self selector:#selector(gameLoop)
userInfo:nil repeats:YES];
2: //[self gameLoop]
{
The gameLoop method is like this:
-(void)gameLoop{
[self updateModel];
[self render]
{
Now, a very strange thing happens. If I comment the line 1 and uncomment the line 2 in startAnimation method I will not get the objects rendered to my screen. I thought rendering might require continuously calling the gameLoop metod. But even if I set the timer to not repeat (so repats:NO) objects are drawn. It means calling the gameLoop method just once, but from an NStimer object is enough. But if I call the gameLoop method manually I do not get the objects drawn. I tried calling the method inside a loop which executes 100 times. It did not help either. Is there something special with the timers in regards with OPENGL?Sorry for the question if it's too immature.
It´s been some time since you asked but the problem there is that the time interval is set to 1/60, which is 0 because it´s using the integer division. You should use 1.0/60.0 instead.
A timer is not needed for OpenGL ES. For my drawing app the render method is called everytime the user touches the screen.
However, for games most developers use CADisplayLink to call the render or gameloop method instead of NSTimer, as CADisplayLink will call the render method each time the screen refreshes.
Setting up a CADisplayLink is done like the example below.
- (void)setupDisplayLink {
//this sets up the game loop to be called once each time the screen refreshes
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(gameLoop:)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
Then the gameLoop should be setup as:
- (void)gameLoop:(CADisplayLink *)displayLink {
[self updateModel];
[self render];
}