I am trying to make a picker appear to spin around multiple times before landing on a particular row. I have tried using for and do while loops. I have even tried calling multiple methods in succession, but each time, the picker only appears to reload once even though I am telling it to reload multiple times before finally landing on a selected row like this:
int newValue = random() % [self.inputNames count];
[payPicker selectRow:newValue inComponent:0 animated:YES];
[payPicker reloadComponent:0];
NSDate *future = [NSDate dateWithTimeIntervalSinceNow: 2.0 ];
[NSThread sleepUntilDate:future];
newValue = random() % [self.inputNames count];
[payPicker selectRow:newValue inComponent:0 animated:YES];
[payPicker reloadComponent:0];
[NSThread sleepUntilDate:future];
I do the above about three or four times and each time the picker only appears to reload once and goes to the final selected row. Is there something special I have to do or is it possible at all to make the picker appear to spin around and around? If I could even get it to jump randomly between the row choices before landing on one, I would be fine with that.
Thanks!!
Don't sleep the UI thread. No animation will start until you exit this method, after all your sleeping. So return from the method with the first request for the picker to animate. Do subsequent picker animation requests in timer callbacks after the previous picker animation has finished. Rinse and repeat as necessary.
The way other apps do this is by not using a real picker at all, but overlaying the picker control with an animated image sequence which just happens to look like a picker spinning. Then remove the fake animation to show an identical looking but real picker underneath. The advantage of your own animated cartoon is that you can make the fake picker spin in ways that a real picker can't.
Perhaps you could try selecting random rows before selecting your actual row. For example, if you want to select row 0, select rows in the following order - 9, 2, 15, 0. I don't think picker view will rotate if it is already on the row it is supposed to go to.
Also, you should select the rows every time after your pickerView:didSelectRow:inComponent: method hits.
Related
I have a UICollectionView which shows images retrieved from the web. They are downloaded asynchronous.
When user scrolls fast, they see placeholders until the cell loads. It seems UICollectionView only loads what is visible.
Is there a way to say "collection view, load 20 cells more above and below" so chance is higher that it loaded more cells while user was looking at content without scrolling?
The idea is to have the VC recognize when a remote load might be required and start it. The only tricky part is keeping the right state so you don't trigger too much.
Let's say your collection is vertical, the condition you want to know about is when:
BOOL topLoad = scrollView.contentOffset.y < M * scrollView.bounds.size.height
or when
BOOL bottomLoad = scrollView.contentOffset.y > scrollView.contentSize.height - M * scrollView.bounds.size.height
in other words, when we are M "pages" from the edge of the content. In practice though, this condition will be over-triggered, like when you're first loading, or if you're testing it on scrollViewDidScroll, you don't want to generate web requests for every pixel of user scrolling.
Getting it right, therefore, requires additional state in the view controller. The vc can have a pair of BOOLs, like topLoadEnabled, bottomLoadEnabled, that are NO until the view is ready. Then, scroll delegate code looks like this:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// compute topLoad and bottomLoad conditions
if (topLoad && self.topLoadEnabled) [self startTopLoad];
similarly for bottom. The load code looks like this, abstractly:
self.topLoadEnabled = NO; // don't trigger more loading until we're done
[self.model getMoreTopStuff:^(NSArray *newStuff, NSError *error) {
// do view inserts, e.g. tableView.beginUpdates
self.topLoadEnabled = YES;
}];
Same idea for bottom. We expect the model to fetch for itself (maybe the model has image urls) and cache the result (then the model has images). As the datasource for the view, the view controller gets called upon to configure view cells. I can just naively ask the model for images. The model should answer either fetched images or placeholders.
Hope that makes sense.
In my opinion you are making the wrong assumption: cells are just views so you shouldn't treat them as model objects. UICollectionView and UITableView are very efficient because they constantly recycle cells so you should think in therms of pre loading content in the business side of things. Create interactor or viewmodel objects and populate your data source with those, then you'll be able to ask those objects to preload images, if you still wish to do so.
A BOOL flag seldom is the answer. I'd rather go for estimating a reasonable page size and fetching images as needed from the cellForItemAtIndePath method.
When a user adds an item to my list, I want to scroll to the new row, highlight it, and select it (which will push a new controller). The key part is waiting for the scroll animation to complete before pushing the new controller.
In this answer, I learned how to use the animation delegate to wait until the scroll is complete.
However, if the insertion row is already on scree, the table view will not scroll and the method will not fire.
How can I wait to push the new controller until the end of the scroll, and deal with the case where no scroll will be initiated - and how might I tell the difference between each case?
The easiest way to check whether a given row is visible in your table view is something like this:
if (!CGRectContainsRect([self.tableView bounds], [self.tableView rectForRowAtIndexPath:indexPath])
{
// the row is partially outside the table view’s boundaries and needs to be scrolled for full visibility
}
else
{
// the row is within the boundaries and does not need to be scrolled
}
Try creating a method to see if scrolling is needed. If no scrolling is needed, call the push right away, otherwise wait for the delegate call and push.
- (BOOL)isSrollingingNeededForIndexPath:(NSIndexPath *)indexPath {
NSArray *visibleIndices = [self.tableView indexPathsForVisibleRows];
for (NSIndexPath *visibleIndexPath in visibleIndices)
if ([indexPath compare:visibleIndexPath] == NSOrderedSame)
return NO;
return YES;
}
Edit: Good point. Since indexPathsForVisibleRows is used for data rendering.
You could do essentially the same thing with indexPathsForRowsInRect where you use the content.offset.y and the tableview.frame.size.height to determine your "visible rect".
Then to account for partially visible rows at the top and bottom you could add rowHeight-1 to the top of the rect and subtract rowHeight - 1 from the bottom of the rect. Code shouldn't be too gnarly if you have static height rows. If you have varying height rows it would still work, but it would be a bit more involved.
All said though, it seems like a lot of code for something which you'd think would have a simple answer.
i have a functionility for show count down timer in format, but when uidatepicker in count down timer mode , it show only HH:MM.
how i can add a extra field for second in uidatepickerview.
how i can add a extra field for second in uidatepickerview.
You can't. UIDatePicker only displays hours and minutes in countdown mode. If you need to display seconds as well, you'll have to recreate it yourself using a UIPickerView.
As always, you should file an enhancement request to request this functionality.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView;
{
return 2;
}
it will create section in your picker view.
Ok so I have a segmentedControl which I need to dynamically remove and add segments to. This piece of code will remove the first segment but then doesnt set the correct segment to be selected. The strange thing is when I print it out all the numbers are correct it is just the highlighted segement on the screen which is always out by -1.
if ([outControl numberOfSegments]==4) {
int previous = [outControl selectedSegmentIndex];
if (previous>0) {
previous--;
}
[outControl removeSegmentAtIndex:0 animated:YES];
NSLog(#"setting to %d with %d segments", previous ,[outControl numberOfSegments]);
[outControl setSelectedSegmentIndex:previous];
}
Here is the log setting to 1 with 3 segments. With this log segment 0 is actually selected in the control and I cannot press segment 1 as the control thinks it is selected?
I can press segment 0 though even though that is the one being displayed as selected.
Here is a screen grab to try and clarify my issue.
image
The text in the cell is correct as cell 1 is actually selected as you can see by the log. But the highlighted cell is 0!? Its driving me crazy. It all works when adding segments its only when removing I get the issue.
Can anyone see where its going wrong??
Many thanks
Jules
I have noticed that even if i do not set the new selected segment I get the same results ie the wrong segment is selected.....?? Also this is all coded with cellForRowAtIndexPath if that helps anyone?
The code looks right yet behaves (wrongly) just as you described. It seems that the control gets in an awkward state when changing the selection in the same turn of the run loop as the segment deletion. But this works:
-(void)setSegmentedSelection:(NSNumber *)indexNum {
NSInteger index = [indexNum intValue];
[outControl setSelectedSegmentIndex:index];
}
// then instead of setting the selected index after the segment is removed,
// let the run loop return...
[self performSelector:#selector(setSegmentedSelection:) withObject:[NSNumber numberWithInt:previous] afterDelay:0.0];
I'm surprised that it doesn't work as you coded, but at least here's a workaround until we figure it out.
In my delegate i have an NSInteger healthInt; and NSMutableString * healthString. In appDelegate.m i have set healthString to hold the changing value of healthInt. Then in a different view i have UILabel * healthLabel. And I have set healthLabel to display healthString with the following code.
healthLabel.text = [NSString stringWithFormat:#"%#", appDelegate.healthString];
This works and displays the number 100, which is what i have set healthInt to in the appDelegate. But when a UIImageView mainSprite collides with a different ImageView healthInt should decrease by two. Which it does, i can tell because i can see it happen in the log. The log may change and display the values of the slowly decreasing healthInt, but the healthLabel doesn't update as healthInt decreases. My question is how can i get this healthLabel to update as healthInt decreases? I have tried just putting in that code for the collision detection between mainSprite and the other ImageView that causes damage but that doesn't seem to work. Thanks!
If you want to get your UILabel to update every time healthInt or healthString changes, there's a couple ways to do this.
One way is to go into the setter method for healthInt (or create one instead of using #synthesize) and broadcast a NSNotification for each time your healthInt number changes. And then when the view with that label is visible, have an observer registered pick up on that notification and make a change to your label.
Another way is to use Key Value Coding & bind the UILabel's text field to healthString. Here is a related question that may help you use this possible solution.