I have a customView. It has some condition like this(only example):
customView(viewsNeed: Bool)
...
if viewsNeeded {
self.addSubView(newView)
self.addSubView(newView2)
} else {
self.addSubView(newView3)
self.addSubView(newView4)
self.addSubView(newView5)
}
and then I can add this View to in my ViewController:
self.view.addSubView(customView(viewsNeeded))
What I want to know is what should I do? Write conditions like this, or make separate Views for this purpose. Something like:
View1
...
self.addSubView(newView)
self.addSubView(newView2)
View2
...
self.addSubView(newView3)
self.addSubView(newView4)
self.addSubView(newView5)
And add one of them in the ViewController:
if viewsNeeded {
self.view.addSubView(view1)
} else {
self.view.addSubView(view2)
}
What kind of View creating is better in what situation, and how should i decide this kind of things? I need some really wide answers with explanations if it's real.
If a view can have different states, you would take care of those different states within the view that has a certain responsibility. The UINavigationBar is a good example. It has a clear purpose, giving navigational context to the user, but it's state (and context) can make it appear different.
func pushNavigationItem(...) {
...
if self.items.count > 1 {
// show backButton
} else {
// hide backButton
}
}
If the different views don't work together for a shared purpose, I wouldn't group them together in a container-view, but instead add them separately, dependent on your needs in a ViewController.
override func viewDidLoad() {
if userDidBuyContent() {
// add view with bought content
} else {
// add view to buy content
}
}
And in general it's a good practice to keep your view-hierachy as flat as possible. The less views you introduce, the better your app will perform. The decision is ultimately up to you, but just keep in mind what the purpose of a view is and whether subviews contribute to that purpose or are really serving some other purpose.
there is no conceptual difference between options you've described. from MVC pattern perspective they are both slightly wrong. you don't have to add views manually, view must create its structure itself.
Related
Let's say you have two classes conforming to a protocol and you want some logic to be shared between them. In languages like Java, you'd typically create an abstract class with the shared logic and make use of it in subclasses. In Swift, abstract classes aren't supported. What's the recommended approach for accomplishing this?
One answer is composition, but what if the common functionality can't be divided into smaller components in a clean and sensible way?
Another answer is to implement common functionality in the protocol itself, but what if it's heavily tied to state?
Some context:
I'm working on an iOS app in which two screens use the same view for different purposes. I'm using the MVP pattern and would like to share common logic among the two presenters. There is state involved, and there isn't really a clean way to pull shared logic into separate components since it's so closely tied to the view interface.
Here's a minimal example of this situation:
protocol View {
func doSomething()
}
class ViewController : UIViewController, View {
func doSomething() { }
}
protocol Presenter {
func tellViewToDoSomething()
}
struct Presenter1 : Presenter {
let view: View
init(withView view: View) {
self.view = view
// then do something unique to presenter 1
}
func tellViewToDoSomething() {
view.doSomething()
// then do something unique to presenter 1
}
}
struct Presenter2 : Presenter {
let view: View
init(withView view: View) {
self.view = view
// then do something unique to presenter 2
}
func tellViewToDoSomething() {
view.doSomething()
// then do something unique to presenter 2
}
}
I'm asking this as a general question rather than in terms of my current situation because I'd like to understand general approaches for sharing common logic in Swift.
I'm coming from an OOP background and it's likely that I'm fundamentally misunderstanding something, so maybe someone could enlighten me.
Could a protocol extension help here? The thing that makes protocol-oriented programming tricky from an OOP perspective, of course, is that there is no super. But nothing stops you from just calling the protocol's built-in functionality:
protocol View { func doSomething() }
protocol Presenter {
var view : View {get set}
}
extension Presenter {
func tellViewToDoSomething() {
self.view.doSomething()
}
}
struct Presenter1 : Presenter {
var view: View
func tellViewToDoSomethingAndThenSome() {
self.tellViewToDoSomething()
// and then some
}
}
I have a progress bar (with its own controller). This bar is supposed to be shown in different views depending on which view is visible. As the progress will be same, If possible I don't want to create many progress bar in many views rather I want to use same instance in all these views. Also in that way when I need to change any property of the progress bar it will be reflected commonly, which is required.
Please suggest me how can I use this common view. And also if my strategy is wrong, what would be the better design for such scenarios.
1) Well you have 2 options. You can declare a new Class ViewBox (or whatever name) and then use that inside your code
First View Controller
var box:ViewBox = ViewBox()
When you segue or transition to your next screen, you can have a predefined variable var box:ViewBox!. Then say when you press a button, the button has a function called transition.
//Now setup the transition inside the Storyboard and name the identifier "toThirdViewController"
override func prepareForSegue(segue:UIStoryboardSegue, sender:AnyObject?) {
if(segue.identifier == "toThirdViewController") {
var vc = segue.destinationViewController as! `nextViewController` //The class of your next viewcontroller goes here
vc.box = self.box
}
//Since The SecondViewController doesn't need ViewBox, we don't need it there.
}
where
nextViewController:UIViewController {
var box:ViewBox!
}
Or you could do a much simpler way and that is to look up a UIPageViewController :)
This seems like it should have a simple answer, and probably does, but it's proving harder to find than I expected. As a specific example, let's say that I'm programming a chess game.
It seems like this is something I should be able to do just using CoreGraphics. It seems like using OpenGL or SpriteKit shouldn't be necessary.
I want to have a Board class that models the state of the board. Where should I declare my Board object? My impression is that it should be in the ViewController.
I want to have a view (actually a subview) that displays the current state of the board (by overloading drawRect). It should do this at the beginning, and should be updated when players make moves. How does the view access the data model to display the board state? Does giving the view a reference to the data violate MVC? If not, how would the reference be passed to the view? (I seem to just find lots of links about passing data between two ViewControllers.)
Should it instead be the ViewController "pushing" the data to the view whenever it needs to be drawn? My understanding, though, is that drawRect should not be called directly, and that instead setNeedsDisplay should be called, which will indirectly result in drawRect being called. This being the case, it's hard to see how the data would be passed.
Your code; your design decision. Nothing to comment on here.
You should have your model declaration in ViewController. True. That is how MVC works.
Having a reference of the data in a UIView DOES break MVC. Your view instance will not be independent anymore. Decoupling view and model is one of the main points of MVC and you are probably breaking it with this design.
What can you do about it?
Extending #Paulw11's comment, in your view controller you can declare a method that looks something like this :
func movePiece(somePiece : Piece, toSquare : Square) {
let pieceID = somePiece.id //I am just assuming the models structures
let pieceImageView = self.pieceImageViewFromID(id) //Assume that this method returns the reference of the image view. Assume that I am just working UIKit here.
let fromPoint : CGPoint = somePiece.origin
let toPoint : CGPoint = toSquare.coordinates
self.animateView(pieceImageView, fromPoint:fromPoint, toPoint:toPoint)
}
Note that in this design, the view is not holding any model references; the view controller will take care of setting its state and bring upon relevant animations.
If you are overriding drawRect:, then yes, for it be called, you should call setNeedsDisplay to update the changes. The view controller might call or you can add property observers to redraw itself based on a property change. One example for this could be:
class SillyView : UIView {
var drawPonies : Bool = false {
didSet {
if oldValue != drawPonies {
self.setNeedsDisplay()
}
}
}
override func drawRect(rect: CGRect) {
if drawPonies {
self.drawGoodLookingPony()
} else {
self.drawSomeOtherAnimal()
}
}
func drawGoodLookingPony() {
//Draw a good looking pony here
}
func drawSomeOtherAnimal() {
//Draw Something else
}
}
If your view controller decides to draw ponies all you have to do is, get the reference of the SillyView and set drawPonies to true.
self.sillyView.drawPonies = true
You are not passing your data model here, but important pieces of configuration information that will help the view redraw itself.
I have a UIViewController (let's call it "EditViewController") which has a Container View on it (call it "ContainerView") where I switch in and out various subviews (call the one I'm most concerned with "EditDetailsView").
From the EditDetailsView I need to change the title in the navigation bar of the EditViewController. I can't seem to be able to figure out how to reference it.
From inside EditViewController I can simply make a statement like:
self.title = #"Some new title";
and it changes just fine.
But from the EditDetailsView view that is currently the subview of ContainerView nothing seems to work:
self.title = ... is obviously wrong.
super.title = ... doesn't work and seems wrong anyway.
super.super.title = ... errors out as super is not a property found on UIViewController.
Can someone please tell me how to reference the title? I'm kinda lost.
Thanks!
While digging through the parentViewController chain is possible, it is error prone and unrecommended. It is considered a bad design. Imagine you set up your view controller hierarchy in some manner, but after a few months change it a bit and now there is one level deeper. Or, you would like to use the same view controller in several different scenarios. A much better design would be to pass the new title to the container view controller using delegation. Create a delegate protocol, with a method for setting the title.
- (void)childViewController:(ChildViewController*)cvc didChangeToTitle:(NSString*)title;
I know this is an old thread, but in case someone else needs it: to avoid boilerplate code with delegation, and avoid digging into the parentViewController, I did it the other way around.
I've referenced the child view controller from the parent and got their title. So no matter which child you show, you will always get the right title.
This is in Swift 3.
So, basically, this is your parent:
class EditViewController: UIViewController {
override func viewDidLoad() {
if let child = self.childViewControllers.first {
self.title = child.title
}
}
}
And this is your child:
class ContainerView: UIViewController {
override func viewDidLoad() {
self.title = "Sbrubbles"
}
}
Another good way to avoid excess code with delegation is to use RxSwift, if you are familiar to Reactive programming.
I've been wondering about extending UIView with a sort of setNeedsRefresh method of my own - a method that behaves like setNeedsLayout and setNeedsDisplay but with the purpose of refreshing/reloading visible data, hiding views, etc. My goals would be to:
Be able to detach methods (particularly setters) from having to instantly refresh views.
Be able to flag a view for refresh rather than refreshing it manually (in order to optimize code execution and have a cleaner code architecture).
I was hoping to be able to extend UIView in such a way that you can call setNeedsRefresh on any view, and have it execute refreshView or some similar method before redrawing happens.
PS: is the best way to handle this through setNeedsLayout + a refresh flag?
Update #1: The following is an example of what I want to be able to do:
-(void)setAvailableForPurchase:(BOOL)availableForPurchase
{
if (_availableForPurchase != availableForPurchase)
{
_availableForPurchase = availableForPurchase;
[self setNeedsRefresh];
}
}
And then:
-(void)refreshView
{
if (self.isAvailableForPurchase)
{
self.someView.image = someImage;
self.someButton.enabled = yes;
// more code, changes several objects
}
else
{
// some other code...
}
}
This example is a very simple one on purpose to better express my requirement - the reasons why I need/want to do this are listed in my initial question.