Add right margin for detailTextLabel in UITableViewCell - ios

What's the easiest way to add right margin to detailTextLabel inside an instance of UITableViewCell?

The detailTextLabel used by the standard UITableViewCellLabel is not a standard label but a special subclass, private, called UITableViewLabel. Customizing it it's not easy as you probably experienced as some methods are overridden by the subclass. You can take some control by de-queing the cell from a reusable pool (because in such case the cell is not recreated and so some of the overridden methods are not called again and in such case basic UILabel settings will apply) but the result couldn't be satisfactory.
So the best approach for custom views is not to try to tweak the existing on-the-shelf views but make a custom cell. This can be done programmatically in the tableView:cellForRow:atIndexPath: delegate method, or by creating a UITableView subclass or loading it from a nib file. Even for simple changes like this.

Related

UICollectionViewCell subclass that uses its parent's xib

I have a custom UICollectionViewCell subclass, lets call it CellClassOne, and I'm trying to create a subclass of that cell, called CellClassTwo so I can change a property and modify some constraints in its view in awakeFromNib.
However, when I register my cell in my collectionView it gets loaded form the xib, so it has the parent's class, CellClassOne. How can I create CellClassTwo that can be dequeued by my collectionView and have its class set to CellClassTwo (and any properties and ovverides with that)?
I'm trying to avoid setting my properties in cellForRow since I'm trying to reuse my cell in different parts of my app but need slightly different paddings for some views, and I don't want to create duplicate Xib files for this.
If I must go with the duplicate files, then it may be better to stick with configuring the views in cellForRow.
Iā€™m not sure that you can achieve what you are looking for via xib, but a viable way that should work is creating the CellClassOne programmatically ( including all the subviews and related layout constraints) following the cell lifecycles, and then subclassing it with CellClassTwo accordingly. That way you should be able to register the cell like ā€˜ collectionView.register(CellClassTwo.self, forCellReuseIdentifier: "myCell")ā€˜.

Subclassing existing custom UITableViewCell + XIB

I have a custom UITableViewCell called StandardTableViewCell. This is made in a XIB with autolayout.
The cell is registered with the method registerNib:forCellReuseIdentifier:
What I am trying to do now is subclass StandardTableViewCell without making a new XIB.
Is it possible to reuse the cell without making a new XIB while keeping the cell's File Owner?
Have you thought of creating a category instead of subclassing? Not sure what you're exactly doing, but that might work instead.
Unfortunately that's not possible, so you have to use a less than ideal solution, here's some options:
Implement the functionality in the same Standard cell and toggle it.
Duplicate the standard nib and replace the cell class.
Don't have a standard nib and just make one for each subclass (if they are different this is probably the best idea).

What is a proper way for handling actions (events) of the UITableView cell subviews?

I have a UITableView with custom cells. Each cell has a rather complex layout and a lot of child views all of which has event actions: UIImageView with gesture recogniser, several UIButtons with some actions, two UILabels with gesture recognizers.
So I'm interested are there some elegant ways to handle target-action for them?
Now I see three ways:
Handle actions with blocks (BlocksKit to the rescue) right in the cellForRowAtIndexPath:
Create custom class CellEventHandler, pass to it all needed dependencies (controller's navigation controller, data array, etc.) and place all the cell's views action selectors there.
Write all the selectors in the TableViewController (default way)
But I'm not satisfied with all this ways. Can someone describe some elegant way which will help to make controller thin, and also will be easy maintainble. I'm sure there should exist some pattern for this workflow.
Use delegation. When constructing a cell, set it's delegate attribute to some instance that conforms your delegation protocol. When actions are triggered, simply delegate the behavior to the delegate.
That way, you end up with well defined delegation protocol and functionality encapsulated in that class. It is up to you whether the delegate will be e.g. TableViewController or a custom class.
I think the most efficient way to go about handling a complex cells is to subclass UITableViewCell and have all of the events handled directly in the subclass. You can create IBOutlets and inactions directly in there.
In the viewForRowAtIndexPath you can simply call a custom function initWithObject that you can declare in the header of your class so even the initial setup of the cell is out of the viewController greatly simplifying the setup.
The Views and Actions should be in View of MVC pattern that is Custom cell in your case. You should write one method for gestureRecognizer(tapGestureRecognizer added on your cell) in your custom cell class. Method will identify the type of object which invoke it through introspection i.e. isKindOfClass UIButton/ Label / ImageView. Each object will further identify with its tag like if gestureRecognizer say it is label which invoke this method but which label label1 or label2 now you can compare its tag to detect it.

MVC pattern controller responsibilities and best practice when presenting a view and transitioning through states

I am familiar with the concept of MVC in which we divide the concerns of modeling, presentation and processing of information. But when it comes to a real life example it gets hazy.
Lets take for example Apples UITableView. Say we have a coresponding controller which takes care of suplementing the table (the presented UIView is custom and just has a UITableView, exposed as a property, at the moment).
The UITableView has a property backgroundView which I want to use for displaying some info when there are no elements to display. The real question is where should it be suplemented?
Make a method in the custom view which creates the view and shows it (showEmptyTableInfoViewWithMessage:).
As in first example but just expose an UILabel as a property and make a simple hide/show method and work on the labels text property from the controller.
Create a custom UIView which will expose the UILabel. Alocate it and assign it to the backgroundView property in the controller.
Create a UIView with an UILabel rigt in the controller and assign it to the backgroundView.
As all of these examples will work I am wondering which one is the proper way (the best seperate of concerns / maintable way). I belive No. 4 is the worst but that is just my feeling at the moment.
Thank you in advance for any information on the best architectural design/approach.
A UIView should be responsible for hiding or showing its subviews and managing its subviews. It shouldn't be responsible for whether or not itself is visible. That's why it exposes the hidden property--so outsiders can do this.
I don't like any of your four options personally.
I'd go with one of these two:
The backgroundView will already have a view object there. We can just create a UILabel object and add it as a subview to the table view's default backgroundView and add the appropriate auto-layout constraints. This would probably best be done by subclassing UITableView, and if so, I wouldn't expose a UILabel property, but rather expose an NSString property whose setter simply sets the label's text, and whose getter returns this text. You may expose a few other methods perhaps for setting the label's font and a few other things.
Create a subclass of UIView, perhaps called UIBackgroundView. This would be a UIView with a UILabel (and whatever else setup you want on it). It shouldn't expose the label directly, but like before, it should instead expose properties that let you set the things about the label you might like to set and does it via proxy. Then you instantiate it with a factory method something like this:
vwBackground = [UIBackgroundView backgroundViewWithText:#"Hello World];
And then simply:
tableview.backgroundView = vwBackground;
Between the two options, which you use depends on what level of reusability you want.
Option one is probably slightly better, and it saves some coding after you've created the subclass, but obviously, you can only do this with table views. If you want to do the same with a UICollectionView, or a UIScrollView, or a UIImageView, or whatever you can find that has a backgroundView property, you'd have to create similar subclasses for each... but in the end, it'd be easier and more intuitive to use, I feel.
But with option two, you can then automatically use your UIBackgroundView anywhere. It's just a UIView with a label, and you can do with it anything you'd do with any other UIView object.

Why I'm getting gesture recognizers cannot be used on prototype objects?

I'm getting the above error from storyboard when I dropped a UITapGestureRecognizer inside a UIView which is inside a UITableViewCell in my scene.
Any idea why I'm getting this error ?
I'm not sure why the restriction is in place but I know why you are getting it the error.
When you design a UITableViewCell in StoryBoard you are only designing a prototype object. i.e. the object may never actually exist. It only gets ACTUALLY created in tableView:cellForRowAtIndexPath:
What might be a better approach is to create the gestureRecognizer when you configure the cell in code. This way you won't have this restriction.
I'd also possibly look at whether you actually need it? If it is just for a single tap with one finger then you may be better off coding the touchesEnded or just using a UIButton instead.
Found the reason myself. storyboard only allows that when we have a UITableViewController or it's subclass and the tableview content should be 'static cells' instead of default 'Dynamic Prototypes'. In that configures I can add a gesture recognizer inside the cell subview.
But that is a limitation and will not work in my case as I have a very customized view controller subclass instead of table view controller subclass. Need to find other way around it seems :(

Resources