nicklockwood / SwipeView

SwipeView is a class designed to simplify the implementation of horizontal, paged scrolling views on iOS. It is based on a UIScrollView, but adds convenient functionality such as a UITableView-style dataSource/delegate interface for loading views dynamically, and efficient view loading, unloading and recycling.
Other
2.65k stars 413 forks source link

How to modify controls inside a loaded XIB? #130

Open hafizrahman opened 10 years ago

hafizrahman commented 10 years ago

I am following your Controls Example project, because I want to emulate how the view for each SwipeView item is loaded from an XIB, instead of created programmatically.

Suppose I have added a label to itemView.xib, and I want to modify the text of this label. How and where do I go about to do this?

My XIB for the individual SwipeView item is called MTNNewsSingleSwipe and it has its own accompanying MTNNewsSingleSwipe.h and MTNNewsSingleSwipe.m files. The main view MTNNewsSingleSwipe.xib already uses 'MTNNewsSingleSwipe' as its custom class.

I suppose - (UIView *)swipeView:(SwipeView *)swipeView viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view is where I can modify the label's text, but I haven't managed to do it right yet. This is what I have so far:

- (UIView *)swipeView:(SwipeView *)swipeView viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view
{

    if (!view)
    {
        NSArray *nibViews = [[NSBundle mainBundle] loadNibNamed:@"MTNNewsSingleSwipe" owner:self options:nil];
        view = [nibViews objectAtIndex:0];
        [view.testLabel setText:@"modify label text"]; /* Not working */
    }
    return view;
}

To explain the [view.testLabel setText:@"modify label text"]; part, I already set an outlet named testLabel inside MTNNewsSingleSwipe.h. For some reason, it is not detected and the text can't be set.

I feel like I'm missing something very obvious here. I would really appreciate it if you can give any pointers about this. Thank you in advance.

myusuf3 commented 10 years ago

@hafizrahman have you found a work around for this using xibs? I hating laying out project programmatically.

nicklockwood commented 10 years ago

Sorry for not replying sooner. You were almost right; you would do it like this:

- (UIView *)swipeView:(SwipeView *)swipeView viewForItemAtIndex:(NSInteger)index reusingView:(MTNNewsSingleSwipe *)view
{

    if (!view)
    {
        //load the view if no view available for recycling
        view = [[[NSBundle mainBundle] loadNibNamed:@"MTNNewsSingleSwipe" owner:self options:nil] firstObject];
    }

    //set the label text (outside the "if (!view)" - very important)
    view.testLabel.text = @"modify label text";

    return view;
}

Note that I've specified the type of "reusingView" as MTNNewsSingleSwipe in the method declaration to avoid having to cast it to MTNNewsSingleSwipe when I set the testLabel.text.

myusuf3 commented 10 years ago

@nicklockwood what I am simply want to load a view controller with a xib interface with dynamic content?

myusuf3 commented 10 years ago

I am doing the following currently.

- (UIView *)swipeView:(SwipeView *)swipeView viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view
{
    TUSwipedViewController *viewController = [[TUSwipedViewController alloc] init];
    viewController.number.text = [_items[index] stringValue];
    return viewController.view;
}

I am sure I need to be loading in the xib some where but I am unsure as to where.

myusuf3 commented 10 years ago

This ended up doing the trick

- (UIView *)swipeView:(SwipeView *)swipeView viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view
{
    TUSwipedViewController *viewController = nil;

    if (!view || !viewController)
    {
        //load the view if no view available for recycling
        viewController = [[TUSwipedViewController alloc] init];
        view = [[[NSBundle mainBundle] loadNibNamed:@"TUSwipedViewController" owner:viewController options:nil] firstObject];
    }

    //set the label text (outside the "if (!view)" - very important)
    viewController.number.text = [_items[index] stringValue];

    return view;

}
myusuf3 commented 10 years ago

@nicklockwood you think it would be possible to stick a nav stack as each view in the swipe? that is what I am hoping to achieve. No luck so far.

So it would be something like swipe > new nav stack > push/pop viewcontrollers > swipe > new nav stack

nicklockwood commented 10 years ago

This will work but it's the wrong approach. You do not need a view controller to load a view from a nib.

Instead of subclassing UIViewController and setting it as the file's owner in the nib, subclass UIView and set it as the class for the root view. Add the outlets to the view and bind them as normal. Use nil as the file's owner.

nicklockwood commented 10 years ago

Similarly, putting a navigation controller inside the carousel is wrong. Put the carousel inside the navigation controller, and then use delegate pattern to send messages from the carousel views to the carousel's view controller to push new controllers onto the stack.

nicklockwood commented 10 years ago

Think of iCarousel as like a tableView - you wouldn't use a separate view controller for each cell in your table, right? It's the same pattern.

nicklockwood commented 10 years ago

(Sorry, I meant SwipeView, obviously, not iCarousel - but the principle is the same)

myusuf3 commented 10 years ago

@nicklockwood Thanks for the awesome feedback! I am going to proceed and see where I get.

myusuf3 commented 10 years ago

@nicklockwood I keep getting errors when i have the file owner set to nil.

Here is a gist describing what I have done.

https://gist.github.com/myusuf3/8aa14656ac23ed09be09

Right when it goes to get view I get this ''this class is not key value coding-compliant for the key''

nicklockwood commented 10 years ago

Looks correct. What errors are you getting?

Btw, on further reflection, in your case it will be easier to set the switchToggled method on your carousel view controller, and use that as your file's owner. That way each view's switch will call the same method and you can decide what to do from there.

Everything else should remain as you have it.

myusuf3 commented 10 years ago

@nicklockwood it had to deal with how I was hooking up UIView managed to fix it. I also added cast to the correct type since UIView doesn't have a label or switchToggled.

I am now looking into doing the navigation stack stuff, you got a second on IRC or something. I would totally appreciate it.