I have a custom view, let's call it ArticleView, that has three child views, which are also custom views, let's call them CustomLabel (they are a view with two labels each)
Each of my views can be either "light" or "dark" themed, depending on a boolean called "dark", that each custom view knows how to draw itself depending on it.
My questions is the following: I have a UITableViewController, and each UITableViewCell has an ArticleView inside of it. I do not want to expose the child subviews of the ArticleView to the cell, so in each cell (in cellForRowAtIndexPath) I pass the "dark" parameter to the ArticleView, which in turn passes the var to its children CustomLabels (via the didSet{} methods of swift), which in turn draw themselves accordingly.
Is this the correct pattern of passing data from my TableView's data all the way down to the custom child views of my custom view?
I do not want to let my cell have access to my ArticleView's children since that would make things less maintainable in the future. What is the best approach here? It just seems I'm passing the "dark" parameter too many times, so I'm asking if there is a better architectural approach for my case.
Thank you!
Related
I have a view model that contains my model and other properties used by my table view delegate methods in my view controller. For heightForRowAt delegate method, I’m passing a computed property from my viewmodel to dynamically calculate the height of the row. My question is does row height property belong to my viewmodel? What if it’s just a constant instead of computed property? If not, why? Should I put it inside another struct instead?
You can put it in either. There are different variations for MVVM.
Put it in view layer:
If you prefer view model to not have any knowledge of view, then you can put the height calculation in your view model, because it is pure rendering logic. In this case, the view model is decoupled from the view very well, and you can use different views to present the same view model. The properties in your view model should be view independent, for example, you have a name label in your view, the the view model property should be called name instead of nameLabelText. There are two drawbacks of this implementation. First, if you want to cache the calculated height, then you have to implement cache in your view layer. The second is that it is not easy to calculate cell heights before rendering.
Put it in view model:
In practice, I found it is acceptable to let your view model have some knowledge of the view, because in most cases, one view model is only used by one view. In this case you can call your view model property nameLabelText if you like, and you can also have cellHeight. It will also be easier to cache cell heights and do height calculations asynchronously.
If you want the pure MVVM implementation put it in the view layer because cell height belongs to the how the cell should display and it is a pure view logic, but it is acceptable to some extent you can put some view related code in the ViewModel.
To learn more about MVVM checkout this link about MVVM
I'm having a brain cramp for some reason on this. Some already posted questions/answers are outdated or objective-C. I'm not sure if delegate is the way to go?
I have a hierarchy of UIViews.
In phenotypeSunplotView:UIView some data is set, a string. I want to pass a string to another class of UIView which actually does the plotting, SunplotView:UIView
For example, I want to pass the string "Gorgonzola" to the class that plots the circle, SunplotView:UIView and plot the string next to the circle.
As a general rule, you don't. Views are not data storage objects. They are the "View" in the Model/View/Controller design pattern.
Instead, you set up the view controller to install the data in both places. Give your view controller outlets to all views that it needs to change.
If a view is an input field (like a UITextField for example) then you add an action or delegate method in your view controller that gets called when the user enters data. Then you collect that data and copy it to any other views that are appropriate. The view controller mediates as needed to implement your app's logic.
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.
I have a custom UITableViewCell class that I use to display quite a complex set of data.
Essentially the cell displays a Match object. But in doing so it displays information about the two Teams, the score, the time elapsed and so on.
Thinking about MVC and clean code.
Should I just pass in the Match object and let the cell do everything? Or is it better practise to expose the different elements of the cell (team1NameLabel, team1ScoreLabel, team2NameLabel, etc...) and set them all individually in the UITableViewController?
The first way makes the UITableViewController cleaner but then I'm relying on the UITableViewCell to "know" about the Match class, the Team class etc...
The second way makes more work for the UITableViewController but then makes the UITableViewCell a "dumb" display. All it does is then lay out the information within the cell. It doesn't know anything about the information it is displaying.
I would follow these rules:
The cell should just have the outlets for displaying the various bits of data. It is a view so it should not contain any logic.
The controller should get the Match data, parse and make calculations if necessary, and populate the cell. It is a controller, so that is its primary function in a MVC context.
IMO it is better and more MVC-like to pass the Match object to your table view cell.
Lot of the code you find on the internet(even Apple examples if I remember well) is not doing that. You can see many times a configureCell method within the view controller that is called in tableView:cellForRowAtIndexPath:.
I prefer to pass the model object object to the cell, it makes my view controller code simpler and also it is simpler to unit test: when I test my view controller I only verify that the model object is passed to the cell, and then in the table view cell tests I verify that the test of the labels is set to the expected values. Someone may say that this is making the view knowing about the model, but I don't see any big problem on that.
Both ways are fine, but personally I would go for second option, i.e. table view exposing #property and, if necessary, outlets.
However, if you really want to go for the first option, I would suggest to have any objects passed to the cell to implements a protocol exposing few methods:
#protocol tableViewCellProtocol
-(NSString*)titleForCell;
-(NSString*)descriptionForCell;
Then you can "pass the protocol" rather than the object.
[mytableCell renderObject:objectImplementingProtocol];
This way you slightly decouple the objects itself, and prepare cell for reuse with other objects.
Often, when I'm making my apps, I'm in this situation : I have a UINavigationController, handling the view stack, some UIViewControllers, controlling their respective views...
But when I want to add several custom UIViews in my mainView, I don't know how to manage my code.
Each UIViewController needs to handle one and only one view (wich normally occupy all the screen size), and a view should not control their content (update it a the extrême limit).
You can't neither do this :
[myViewController1.view addSubview:childViewController.view];
So if I want to achieve something like this, what should I do ?
The orange parts have to be 3 instances of the same UIView(Controller?), but with a content depending of a NSObject (User, obviously).
I think this very important to segment your content, this should be an easy problem, but I found a lot of contradictory answers so, what's the best practice to handle this common issue?
Theses orange views should be instances of UIViewControllers in order for it to handle their UITableViewDatasource? Is addChildViewController relevant in this case?
I already found a lot of things which work, but I don't know what should I do...
Also, I'm using xibs.
Thanks in advance if you can help me (and other people I think).
You can do it either way (view or view controller) depending on how you want to handle things. Certainly, you can have one object be the data source for multiple tables, so in that case, you would just add multiple views. If, however, you want to keep your code more compartmentalized, then add view controllers, and have each control its own view -- to do this, you do need to use addChildViewController, and use the methods that Apple describes for creating custom container controllers. Alternatively, you can use container views in a storyboard which makes the process of creating custom container controllers simpler.
You're on the right path... Create separate instances of your subviews, and add them to your view. If you will have more than 3 (for instance, imagine coverview for your music, and you could scroll indefinitely left and right), I'd take a look at UICollectionViewController ... That will help manage cell re-use.
But, if it's just 3, just create three instances with different frames and add them to your view.
Here's how I'd do it:
each orange box will be a custom view (inherits from UIView)
the view will have the label, image and the tableview.
since you are not sure of the number of instances of these views you'd be using, its better to use some kind of tagging, so that you can have one place for the datasource and delegate methods of the tables in these orange views.
in the datasource and the delegate methods, you can make use of the tableView.tag (same as the orangeView.tag property).
I personally dislike having more than one viewController in a view (except the splitVC), probably because I haven't had a such requirement.
I dont see how a uiviewcontroller for orange box would help, over a uiview.
as #James Boutcher mentioned in his answer, UICollectionViews will simplify this issue further.
Why not creating a UIView class and overriding the drawRect method and then adding subView for this class in your myViewController1.view