For my chart implementation, I want users to be able to select arbitrary data points as they see fit. The issue is that it seems like whenever another data point is selected, the previous one gets deselected. The only thing that seems to work is to have the selected BOOL as part of my data source object and then call reloadData and redrawChart on the chart with each selection.
-(void)sChart:(ShinobiChart *)chart toggledSelectionForPoint:(SChartDataPoint *)dataPoint inSeries:(SChartSeries *)series atPixelCoordinate:(CGPoint)pixelPoint {
...
myDataObject.selected = !myDataObject.selected;
dataPoint.selected = myDataObject.selected;
[self.chart reloadData];
[self.chart redrawChart];
}
And then the dataPointAtIndex will handle it.
-(id<SChartData>)sChart:(ShinobiChart *)chart dataPointAtIndex:(NSInteger)dataIndex forSeriesAtIndex:(NSInteger)seriesIndex {
...
datapoint.selected = myDataObject.selected;
}
But this seems like a wasteful, less efficient way of doing things, plus this does not have the benefit of persisting the zoom in between selections.
I think what you're looking for is series.togglePointSelection = YES. This will allow the series' points to have their selection toggled, instead of only one being set at a time.
Hope this helps!
Rob
Related
I'm an amateur at swift and I have been trying to make a tic-tac-toe game. So I'd like to know if there is any way to compare button images using button tags.
Thanks in advance.
I think there is a better way to approach this.
Do not try to compare buttons' images. Instead what you should do is to create a model. A model is basically something that represents a more complex system with simpler data structures.
In a tic-tac-toe game, instead of dealing with buttons, you can create such an enum:
enum SqaureContent {
case empty, nought, cross
}
Your model will then be a 2D array of SqaureContent:
var gameBoard: [[SquareContent]] = [
[.empty, .empty, .empty],
[.empty, .empty, .empty],
[.empty, .empty, .empty]
]
When the user taps on a button, the view controller can handle the tap by setting the button's image and also setting a value in the model to .cross. This way you can synchronise the model with the buttons (the "view").
Then, you can do all your operations on the model instead of the buttons.
You can then compare the values like this:
if gameBoard[0][0] == gameBoard[1][0] && gameBoard[1][0] == gameBoard[2][0] {
...
}
You know that this is going to be the same as comparing the button images because the model and the view is synchronised.
The benefit of writing programs this way is that you avoid a lot of complications. Buttons are complex objects. Their images can be anything, not just noughts and crosses. We should limit the values to just 3 - .empty, .nought, .cross using an enum.
yes you can compare Image like that:
if yourCell.ButtonOutlet.currentImage.isEqual(UIImage(named: "yourImageName")) {
} else {
}
I have created a subclass of a UICollectionViewCell that shows some information. I have one property in with type Weather. When an instance of that is set I want to update the cell. Is the approach below bad? I am thinking of that I may trigger the view to be created to early if I access the UI components before it is loaded. Or is that non sense and only applies to UIViewController (with regard to using view property to early)?
If this is bad, what would be the correct way?
var weather: Weather? {
didSet {
if let weather = weather {
dayLabel.text = dayFormatter.stringFromDate(weather.fromDate)
// ... more code like this
}
}
}
You may want an else clause, though, clearing the text field if weather was nil. Likewise, if you might update this from a background thread, you might want to dispatch that UI update back to the main thread.
Be aware that this observer is not called when you set weather in the cell's init (nor would be the #IBOutlet be configure at that point, anyway). So make sure that you're not relying upon that.
Also, if Weather is mutable, recognize that if you change the fromDate of the existing Weather object, this won't capture that. (If Weather was mutable, you'd really want to capture its changing properties via KVO, a delegate-protocol pattern, or what have you.) But if you make Weather immutable, you should be fine.
So, technically, that's the answer to the question, but this raises a few design considerations:
One generally should strive to have different types loosely coupled, namely that one type should not be too reliant on the internal behavior of another. But here we have an observer within the cell which is dependent upon the mutability of Weather.
This use of a stored property to store a model object within view is inadvisable. Cells are reused as they scroll offscreen, but you probably want a separate model that captures the relevant model objects, the controller then handles the providing of the appropriate model object to the view object (the cell) as needed.
Bottom line, it's not advisable to use a stored property for "model" information inside a "view".
You can tackle both of these considerations by writing code which makes it clear that you're only using this weather parameter solely for the purpose of updating UI controls, but not for the purposes of storing anything. So rather that a stored property, I would just use a method:
func updateWithWeather(weather: Weather?) {
if let weather = weather {
dayLabel.text = dayFormatter.stringFromDate(weather.fromDate)
// ... more code like this
} else {
dayLabel.text = nil
// ... more code like this
}
}
And this would probably only be called from within collectionView:cellForItemAtIndexPath:.
But, this makes it clear that you're just updating controls based upon the weather parameter, but not trying to do anything beyond that. And, coincidentally, the mutability of the weather object is now irrelevant, as it should be. And if the model changes, call reloadItemsAtIndexPaths:, which will trigger your collectionView:cellForItemAtIndexPath: to be called.
There are times where a stored property with didSet observer is a useful pattern. But this should be done only when the property is truly a property of view. For example, consider a custom view that draws some shape. You might have stored properties that specify, for example, the width and the color of the stroke to be used when drawing the path. Then, having stored properties for lineWidth and strokeColor might make sense, and then you might have a didSet that calls setNeedsDisplay() (which triggers the redrawing of the view).
So, the pattern you suggest does have practical applications, it's just that it should be limited to those situations where the property is truly a property of the view object.
I would use a property observer if I planned up updating the value during the users session. If this is a value that only gets updated when the user first loads, I would just simply call a method when my view is initially loaded.
If you use a property observer, you can give it an initial value when you define it so the data is there when the user needs it. Also, if you're updating the user interface, make sure you do it on the main queue.
var weather: Weather = data {
didSet {
dispatch_async(dispatch_get_main_queue(),{
if let weather = weather {
dayLabel.text = dayFormatter.stringFromDate(weather.fromDate)
// ... more code like this
}
})
}
}
I have to present a view like this in the picture :
I remember that iCarrousel project can do such things, Could Any one guide me to the right control that provide this animation?
I Tried and Got Something like this
Thanks.
You need to adjust the tilt of the item views, which can be done using the carousel:valueForOption:withDefault: delegate method, like this:
- (CGFloat)carousel:(iCarousel *)carousel valueForOption:(iCarouselOption)option withDefault:(CGFloat)value
{
if (option == iCarouselOptionSpacing)
{
return value * 1.5;
}
else if (option == iCarouselOptionTilt)
{
return 0.2;
}
return value;
}
The values included are just example. You'll need to tweak the title, and probably also the spacing, to avoid having the items appear to pass through each other.
The iCarousel repo includes several demo projects, one of which includes the option to display all the different types of Carousels it offers. It's been a while since I've used it, but I'm pretty sure one of the options is what you're after.
I suggest going through the demo programs and looking for the carousel type that matches what you want. Then there are a multitude of settings that you can adjust to customize the look of the carousel.
Working on a shopping store app. I've been going in circles for the past week and a half trying to figure out how to correctly do this. The problem lies in a UILabel that needs to be dynamically updated with text that corresponds to checked checkboxes. It also needs to know whether to apply selection when apply button has been tapped or return previous selection if customer decided to change their mind and not tap apply. This is where I'm running into issue.
I refer to 3 controllers as image 1, image 2 and image 3.
My model is a separate class that I pass copies of back and forth to keep selections made by users when they wish to refine a collection of clothing item results.
So for example the user taps the refine button in image 1
They are taken to image 2 where they decide what they want to refine results by
They are then taken to image 3 page where they make the selection
Where problems begin:
In short in image 3 a customer makes a selection and taps done, they are then taken back to image two where their selection is shown in a string separated by commas in a UILabel underneath the chosen refine by option e.g. Gender. If they are ok with their selection they tap apply and the refining is done and displayed like in image 1. A tick is also shown in the refine button to make the customer aware refining is active.
Now lets say a selection has already been made like in the images below and the customer goes back to image 3 to modify a selection. Let's say he unchecked "microsites". What should happen is when he goes back to image 2 the list underneath the refine by option should be updated.
This works fine but I'm actually updating the property that is updated when the apply button is tapped. So if the customer decides they don't want to refine anymore and don't click apply but the back button instead to take them to image 1 then I need the original selection string to be updated in the property.
The thing is this very property is updated whenever a selection is made in image 3. So when the customer does return to image 2 then because we unchecked "microsites" only "men" will show in the string underneath the refine by option.
I've tried creating a temp property to hold the previous selection but thing's just really get messy with all this going back and forth.
Further info:
I pass my model class back and forth between controllers using the prepareForSegue method and delegation/protocols.
Image 2 is aware when the done button in image 3 is tapped. This is where I pass the model over. Image 1 knows when the apply button in image 2 is tapped and again this is where I pass the model over. To pass the model from image 1 to 2 then 2 to 3 I use the prepareForSegue method.
Image 1:
Image 2:
Image 3:
How would you do this? I feel I've made a good start by moving all my model into its own class. This has made things easier but the other problem with the UILabel is holding me back.
Update:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[[sender titleLabel] text] isEqualToString:#"Gender"]) {
VAGRefineGenderViewController *vc = [segue destinationViewController];
[vc setDelegate:self];
[vc setRefineModel:[self refineModel]];
}
}
Working code:
#implementation VAGRefineModel
{
VAGRefineModel *_object;
}
-(id)copyWithZone:(NSZone *)zone
{
VAGRefineModel *modelCopy = [[VAGRefineModel alloc] init];
modelCopy->_object = [_object copyWithZone: zone];
return modelCopy;
}
Then in image 2/controller 2 I just set the model being passed to controller 3 as the copy of the existing model.
This seems fairly simple.
You should indeed create a copy of the model and pass this to the next viewController then you don't have to worry about the edits made to it.
If the user presses back then that copy is just discarded.
If you press done then you receive the copy and replace your current model then reload your views.
It might look as simple as this (no error handling to make it easy to follow)
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
{
Model *model = [self.model copy];
VC2 *viewController = segue.destinationViewController;
viewController.model = model;
viewController.delegate = self;
}
- (void)didTapDoneButtonWithModel:(Model *)model;
{
self.model = model;
[self reloadData];
}
I didn't read your post in detail, but did read enough to get the gist of it.
I'm gonna call your screen level 1 (master), level 2 (top level detail) and level 3 (fine detail) instead of image 1/2/3, because I'm talking about pictures, I'm talking about master/detail view controllers.
It sounds like you have 3 levels of view controllers that allow the user to edit finer levels of detail for a search.
I would suggest setting up your model so you can encapsulate the details handled by levels 2 and 3 into objects. When you get ready to drop to level 3, create a copy of the settings for gender and micro sites, and pass it to the level view controller. The user will interact with level 3, which has it's own copy of the settings.
Level 3 should set up level 2 as it's delegate, with a delegate message
-userChangedSettings: (Level3SettingsObject *) changedSettings
Or something similar.
If the clicks done, the level 3 VC would invoke that method on it's delegate, passing the changes to the Level3Settings object up to level 2, then pop itself/dismiss itself. (whichever is appropriate.)
If instead the user clicks cancel, just close pop/dismiss without calling the delegate method to tell the delegate about the change in settings. The settings in the level 2 object won't have changed.
The same would go for the communication between level 2 back up to level 1.
If it makes sense, you can make it so that the settings for level 1 contain level 2 data objects, and that the settings for level 2 contain level 3 data objects. That way changes to level 3 get passed back to level 2, and if the user cancels from level 2, all the changes made in level 2 and level 3 are discarded, and only if they tap save in level 2 do the changes from that level get passed up to level 1.
I am taking an online course and one of the problems has you build a simple number guessing game. That was easy enough. However, I want to modify it to limit the number of guesses. That would entail a loop for the number of guesses allowed.
When I try this, the code goes to the button press action and never returns to the loop. I have tried everything I know to get it to work, google searches have not helped, and the instructor has not answered my question posted 4 days ago!
So in short how does one get code to return to a loop after finishing the code for the button press?
In words here is what I want to do:
generate random number
for x = 1 to 6
get user guess in text field
press button to check if correct
if correct
do something
else
continue loop for another guess
x = x+1
You can't do what you want.
The user interface is an event-driven activity -- things happen as a result of the user typing in a textField or tapping a button. The code to handle the UI is distributed among the response routines that handle the events and you have to figure out where you can do each part of what you want to do. It can be maddening!
A general answer is that the code to handle events is distributed among multiple methods and you have to have some sort of shared state so that each event response can know what to do. Some code somewhere starts a round of the game and initialize the number of guesses. Your textField delegate routines get the user guess and store it in an instance variable or property. When the user taps a button, the button-response code can check the answer in that property and keep track of the number of guesses.
To be specific, here is some pseudocode that does what you proposed in your question:
#property (strong, nonatomic) NSString *currentGuess;
#property (nonatomic) int numberOfGuesses;
- (void) startNewRound {
...
// some code that gets ready for the user to guess
generate random number
// initialize the number of guesses where button code can get at it
self.numberOfGuesses = 0;
...
}
// This method is called when the user finishes typing
// a guess in the text field as a result of the user pressing Return
// (or however you manage the data entry). The parameter is whatever
// the user typed as a guess.
- (void) userEnteredAGuess:(NSString *)guess
// put the guess where the button-event code can get at it
self.currentGuess = guess;
}
// This is the method that gets run when the user taps the
// "Check the Guess" button
- (IBACTION) tapButton:(UIButton *)sender {
if (the guess in self.currentGuess is correct...) {
// Do whatever you do when the guess is correct
} else {
// Do whatever you do when the guess is wrong
}
self.numberOfGuesses += 1;
if (self.numberOfGuesses > guessLimit) {
// Exceeded guess limit
// Do whatever should happen --
// re-initialize the guesses, decrement score, ... whatever
// maybe...
[self startNewRound];
}
}