Alternative to pickerView.dataSource = self;

Discussion in 'iOS Programming' started by 1458279, Mar 3, 2015.

  1. 1458279 Suspended

    1458279

    Joined:
    May 1, 2010
    Location:
    California
    #1
    I'm trying to gain an advanced understanding of .dataSource. All the examples I see, set it to self. Yet I read that it's a design pattern and can be changed to alter the behavior of the view.

    Example:
    Code:
       UIPickerView *myPickerView = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 200, 320, 200)];
        myPickerView.delegate = self;
        myPickerView.dataSource = self;
        myPickerView.showsSelectionIndicator = YES;
        [self.view addSubview:myPickerView];
    
    I've been woking on creating an instance during runtime that will have it's own unique set of methods like

    Code:
    - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    
    I was told that the key to doing this is in using the UIPickerViewDataSource protocol.

    If so, I guess that means that I need to use a different .dataSource other than self. However, all the example I'm finding, use .dataSource = self;.

    Q. Does anyone have a link to an example of using a different dataSource other than self and what effect it has?

    I'm guessing the protocol is the format to use (methods that need to be addressed) but I see no examples of this.

    I'm also thinking of using a block and passing the block into the method and running the block inside of the method.

    I suppose another option would be to have a block as a var in a subclass and then access the block thru the class, but I'd still like to see some example of a dataSource other than self and what change it could make.

    Thanks.
     
  2. Punkjumper macrumors member

    Joined:
    Jan 12, 2013
    #2
    I think I answered that question in a different thread where you asked basically the same thing about a month or so ago.
     
  3. 1458279 thread starter Suspended

    1458279

    Joined:
    May 1, 2010
    Location:
    California
    #3
    The questions are similar in that they are for the same part of a project, but this is a very different approach. The answer that you gave does work, but I'm trying to learn the use of design patterns to see what options that gives me.

    This approach to the problem would be more object oriented and will use design patterns instead of a switch inside of a method.

    At least one issue is that there could be tons of these and the parameters won't be know at compile time.

    These instances will be created in a loop on an as needed bases and each one will have unique usage. The number needed and the number active at any one time will not be known at compile time and each can have unique usage.
     
  4. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #4
    That seems like a partial description of the problem and your solution. It would help us to help you if you fleshed it out more.

    To summarize what you've given so far, you need multiple different data-sources, only one of which is selected and instantiated at runtime. This need for multiple different data sources is what prohibits the use of a single data-source.


    The first thing you'll need to understand is what a protocol is and how you use it. If you already know that, then the next step should be obvious: implement the protocol in a class of your own design.

    Since you said you need multiple different data-sources, one obvious design for that is a single base class that declares the protocol, and provides a concrete implementation of any common features used by all the actual resulting data-sources. For example, if they have similar uses of URLs or rendering, you can add those methods in the base class for every subclass to use.

    Then you start making subclasses with implementations for every method of the protocol. You'll have one such subclass for each different data-source. If you see a lot of repetition, that's a sign you can factor out a common method (or methods) and use it across multiple subclasses.


    As to how to select and instantiate one of the data-sources, please explain what criteria (conditions) you're using to select between data-sources. You should also state what the upper limit on the number of different data-source classes is. Is it 100? 1000? A million?

    Post the code of the 'switch' statement you intend to eliminate, and explain how many cases it would have.


    Since an Objective-C class is an object, you could build an NSDictionary where the key is the class name string and the value is the class object. You could also make an NSArray or any other collection that holds the class objects, depending on what criteria and mechanism you use to locate a specific class.

    Once you have the class object, you then invoke either the new method or alloc followed by a suitable init method on the class. Voila, there's your data-source instance.

    You can also get a class by building its name as a string. You'll have to dig into lower-level Objective-C, but it's there. The reference doc to read is "Objective-C Runtime Reference". Once you have the class object, you do the same thing: invoke its new or alloc/init methods.

    Finally, once you have the data-source instance, do this:
    Code:
    myPickerView.dataSource = chosenDataSourceInstance;
    
     
  5. 1458279 thread starter Suspended

    1458279

    Joined:
    May 1, 2010
    Location:
    California
    #5
    Thanks for the reply. I started looking into protocols and I'm not sure I fully understand.

    This is what I think so far:
    a protocol is a group of methods and/or properties that can be attached to any other class. The net result of this is an object that contains the stock methods/properties plus whatever you've defined in the protocol.

    The example give was here: http://rypress.com/tutorials/objective-c/protocols

    Where you end up with 3 methods from the protocol being added to another object.

    However, it seem that these methods would be the same methods added to each instance of the object.

    So, if you make 10 objects, they all have the same methods with the same behavior.

    What if I want each of the 10 objects to have a dataSource method that would have behavior that I could define at runtime?

    I suppose if you added a property via the protocol, you could assign a value to that property that would control it's behavior and that could be done at runtime.

    So it sounds as if I could add a protocol to UIpickerView that has a property, set that property for each instance but that would still leave me with all the objects calling the stock method 'dataSource'.

    On the other hand, if I can figure out how to assign dataSource at runtime that would not change the dataSource behavior for another instance.

    The problem I'm having is finding an example where someone has changed the dataSource for an instance.

    Consider this:

    Example:
    Code:
       UIPickerView *pickerView1 = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 200, 320, 200)];
        pickerView1.delegate = self;
        pickerView1.dataSource = self;
        pickerView1.showsSelectionIndicator = YES;
        [self.view addSubview:pickerView1];
    
       UIPickerView *pickerView2 = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 200, 320, 200)];
        pickerView2.delegate = self;
        pickerView2.dataSource = self;
        pickerView2.showsSelectionIndicator = YES;
        [self.view addSubview:pickerView2];
    
       UIPickerView *pickerView3 = [[UIPickerView alloc] 
    initWithFrame:CGRectMake(0, 200, 320, 200)];
        pickerView3.delegate = self;
        pickerView3.dataSource = self;
        pickerView3.showsSelectionIndicator = YES;
        [self.view addSubview:pickerView3];
    
    Now I have 3 instances but they are all calling the same method:

    Code:
    - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    
    I want something like this:
    Code:
    - (NSInteger)pickerView1:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    
    - (NSInteger)pickerView2:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    
    - (NSInteger)pickerView3:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    
    But I don't know how to connect 1 instance to a given method.

    One workaround might be to define a block in the object, create the block at runtime, look for block inside the method, if the block is there, execute the block, otherwise continue to run the stock method.

    Something like this:
    Code:
    - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    // check for a block
    If (block){
    // run the block
    //return = return value of the block
    } else {
    // do what you would have done for every other instance
    // assign return value
    }
    return (return value)
    
    This would allow me to control the behavior per instance, but something makes me think that I'm doing this because I don't understand ObjC well enough.

    My guess is that dataSouce is assignable per instance to address this. I just can't seem to find an example of how to do that.

    I noticed at the end, you mentioned:
    "myPickerView.dataSource = chosenDataSourceInstance;"
    I'm having trouble finding an example of the "chosenDataSourceInstance"
    What datatype is it? What structure, return value, etc... because that seems to be what I need.
     
  6. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #6
    You're confused about what a protocol is. You should study it again, and actually write some code that uses what you learned, so you know how it works in a practical way.

    A protocol doesn't declare methods for an object, but for a class. Since every object from the same class has the same methods, then every object instantiated from that class then has the same methods, as defined by the protocol. A near equivalent is 'interface' in Java, as distinct from 'class' in that language.

    There is no way to define a protocol only for an object. You must define it for the class of the object, not for an individual object. Objective-C doesn't define behaviors by attaching methods to objects; JavaScript does, but Objective-C isn't JavaScript.


    This part is right. Protocol attaches to class.
    This part is wrong. The net result is NOT an object that contains methods and properties, it's a class. To get an object, you must instantiate the class.

    Here you are mistakenly referring to objects (I've underlined them) when the correct reference is to classes. That is, to make 10 objects with different behavior you first define 10 classes that implement the protocol and have different code in their methods. Then you instantiate one object from each class. You now have 10 objects with different behavior, each of which implements the protocol.

    There may be other ways of encapsulating the differences in behavior between the objects, but you'd first have to describe in detail what those behaviors and differences are.

    Or maybe I'm not understanding what you mean by "behavior that I could define at runtime". Since you haven't given any examples of the behavior you want, or differences between objects, I have no way of knowing how to design anything to produce it.

    You also still haven't said how many different kinds of behavior you want (100's, thusands, millions?), nor did you post the 'switch' code I asked for.


    Its type is one of the classes you wrote that implements the protocol. For an example, see Punkjumper's earlier post in another thread.
    http://forums.macrumors.com/showpost.php?p=20650101&postcount=3
     
  7. kage207 macrumors 6502a

    Joined:
    Jul 23, 2008
    #7
    Yes, you are correct here but there is some slight confusion. You are wrong in saying: 'The net result of this is an object...' It is not an object, just a way of telling a class what methods/properties MUST implement. This standard (protocol) can be applied to any class.

    So protocols tell classes all the properties and methods that MUST be defined for it.

    So this is where your confusion lies. The protocol tells a class HOW to conform (what methods/properties it must have) and not the IMPLEMENTATION (how the class defines the methods/properties). This means that your class is free to implement the required methods/properties in their own way. Note that the required methods/properties must all be of the same type.

    Since you've followed a tutorial to begin understanding how to use a protocol, you should refine your knowledge. I suggest reading Apple's documentation about protocols to see where and how to use them. This will help you understand how Apple has designed protocols and apply to your case.

    Working with Protocols

    This is where some more confusion kicks in. Are the 10 objects of the same type (class) or they objects from different classes that implement the protocol? If you are looking at the same type, you need to look into 'Interacting Directly with Objective-C Runtime'.

    Read through that chapter and really understand try to understand the design process of runtime decisions for Objective-C.

    A protocol should not be added to UIPickerView. It should be added to something like UIPickerViewController (where I'm guess you are doing the myPickerView.delegate = self). Protocols are added to classes to make them conform and implement the required methods.

    So this is where you are vague. Are you trying to put multiple UIPickerViews into your view? If you just to have one UIPickerView in your view, where does the input data come from to decide which PickerView to use?
     
  8. 1458279 thread starter Suspended

    1458279

    Joined:
    May 1, 2010
    Location:
    California
    #8
    Ok, I screwed up the term usage and probably caused more confusion.

    Let me ask a different way:

    Right now I can create 3 buttons like this:
    Code:
        CGRect buttonFrame = CGRectMake( 10, 80, 100, 30 );
        UIButton *button1 = [[UIButton alloc] initWithFrame: buttonFrame];
        [button2 setTitle: @"My Button 1" forState: UIControlStateNormal];
        [button2 setTitleColor: [UIColor redColor] forState: UIControlStateNormal];
        [self.view addSubview: button1];
    
       CGRect buttonFrame = CGRectMake( 10, 80, 100, 30 );
        UIButton *button2 = [[UIButton alloc] initWithFrame: buttonFrame];
        [button2 setTitle: @"My Button2" forState: UIControlStateNormal];
        [button2 setTitleColor: [UIColor redColor] forState: UIControlStateNormal];
        [self.view addSubview: button2];
    
       CGRect buttonFrame = CGRectMake( 10, 80, 100, 30 );
        UIButton *button3 = [[UIButton alloc] initWithFrame: buttonFrame];
        [button3 setTitle: @"My Button3" forState: UIControlStateNormal];
        [button3 setTitleColor: [UIColor redColor] forState: UIControlStateNormal];
        [self.view addSubview: button3];
    
    Each button can have it's own color, title, and behavior. The behavior can be controlled by different methods.

    Example of one button that calls it's own method:
    Code:
        UIBarButtonItem *customItem = [[UIBarButtonItem alloc] initWithTitle:@"Clear"
             style:UIBarButtonItemStyleBordered
             target:self
             action:@selector(clearSQLText:)];
    
    This button will call "clearSQLText" method.

    When it's done, I can have 3 buttons that call 3 different methods, all done in code.

    However, If I want to do this with pickerViews instead of buttons, I don't know how.

    Each instance of the 3 buttons has it's own method via "action:mad:selector(_____)"

    If I create 3 pickerViews, they all call the same methods, when I create 3 buttons I can make them call their own methods.

    I've found examples with buttons and other things, but can't seem to find any examples with pickerView.

    The solution that was presented before, was to use a an if statement in the one method to find out which instance was calling the method, yet with the buttons I don't have to do that, I just say at runtime what method controls the action of the given button.

    I hope this makes the question a bit clearer, I just want the level of control in pickerView as I have with the buttons. I just can't seem to find any examples of how to do it.
     
  9. kage207 macrumors 6502a

    Joined:
    Jul 23, 2008
    #9
    I'm guessing you do something along the lines of below for each button.
    Code:
    *.m
    -(IBAction)buttonAction:(id)sender{
     //DO STUFF!
    }
    
    Now this is where you need to look at:

    Code:
    - pickerView:didSelectRow:inComponent:
    This is what is called when you select an item from the UIPickerView. You know what row was selected. You should look at http://nscookbook.com/2013/01/ios-programming-recipe-7-using-the-uipickerview/

    Same with UIPickerView, look at the above tutorial with the code:

    Code:
    - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row   inComponent:(NSInteger)component{
    
        NSLog(@"Selected Row %d", row);
        switch(row)
        {
    
            case 0:
                //Call your method!
                break;
            case 1:
                //Call another method!
                break;
            case 2:
              ...
        }
    
    }
    
    Why is there 3 pickerViews? Just have one. Each row in a pickerView is a "button" in your case.
     
  10. 1458279 thread starter Suspended

    1458279

    Joined:
    May 1, 2010
    Location:
    California
    #10
    Ok, it looks like Punkjumper was giving me the answer in my other thread and I didn't see it.

    I'm working on wrapping my head around the code now, but it does have a unique datasource which is what I was looking for.
     
  11. 1458279 thread starter Suspended

    1458279

    Joined:
    May 1, 2010
    Location:
    California
    #11
    The code you posted does work, but I'm trying to find a different way of doing it. At least one problem is that I don't know how many pickerViews will be there at runtime. It's more about gaining the most flexibility of the pickerView. The usage will be more dependent on how the user uses it and what data if finds. Much like the button can do anything I want and I can determine how many buttons I want and what they'll do at runtime.
     
  12. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #12
    Seems like chown has given up on trying to teach you to fish.

    Let's say you want to have picker views that let a user choose a number between two dynamically chosen integers and there are a variable number of picker views. You need a data model and delegate for each picker.

    Let's say the data model is two integers representing the lower bound and the upper bound of the values displayed in the picker. So you create a class that has as @properties the lowerBound and upperBound and most importantly this class adopts the UIPickerViewDelegate @protocol. The class implements all the required methods of the delegate protocol and returns values based on the lowerBound and upperBound. Lets call the class MyPickerViewDelegate.

    When you create one of these picker views your controller also creates one of these MyPickerViewDelegate objects, sets its lowerBound and upperBound @properties, and sets the pickerView.delegate property to the MyPickerViewDelegate object.

    That's it.
     

Share This Page