I have a storyboard, and it needs to be used in few places. Is there any other option than just duplicate storyboard and assign it to new view controller? I want to avoid boilerplate code and writing 3x same outlets and others. I need exactly same layout and almost all functions in all three controllers, but I want to only change model for all of them.
I have tried to do it like this:
class FirstViewController: UIViewController {
//outlets
// code
// tableView configuration
}
class SecondViewController: FirstViewController {
// and here I have just overrided some function to modify them
}
Related
I'm new to Swift and I'm sure this question is pretty basic and has been asked and answered before.
I am not using storyBoard. My main viewController is created from AppDelegate via code.
I have:
a custom class defined in a model.swift file
a main viewController (from AppDelegate) that I am using as a container
3 additional viewcontrollers as subviews of the main (not each other)
all 3 subviews are displayed simultaneously each covering 1/3 of the screen (no segues)
each viewcontroller is in a separate .swift file
I want to create an instance of my custom class in the main viewController and have all 3 of the subviews be able to reference that instance.
Each of the subview view controllers need to be able to get/set instance variables and the other subviews need to be made aware of those changes.
I think I will need to use notifications to communicate the changes to the multiple subviews - but I haven't even begun to try and figure that out yet.
If this has been asked and answered before - could someone please either provide a link - or provide me with the right search terms so that I'm able to find the answer? The only found answers I've found that come close are to use segues to pass the data back and forth.
You can use delegate pattern. Below code is assuming that you are using MVVM pattern. (It is very similar for VIPER/ReSwift patterns also)
protocol DataChangedDelegate {
func refreshData()
}
// ViewModel for FirstViewController
class FirstViewModel {
var delegate: DataChangedDelegate?
var data: Any {
didSet {
delegate?.refreshData()
}
}
//rest of the things
}
//similarly other two view models will have a delegate and on data change will call the refresh method
And your view controllers should adopt this protocol
class FirstViewController: UIViewController, DataChangedDelegate {
//view controller code
//delegate code
func refreshDate() {
//tableView.reloadDate()
//collectionView.reloadDate()
//lableView.text = viewModel.data()
}
}
And where ever you create a viewControllers and add as subView, you have to set the delegate of viewModel.
let firstViewController: FirstViewController = createFirstViewController()
let firstViewModel = FirstViewModel()
firstViewModel.delegate = firstViewController
firstViewController.viewModel = firstViewModel
mainViewController.addSubView(firstViewController.view)
Similarly for all other view controllers.
Here's how I would do it:
Create a singleton class.
Configure the singleton's properties in the the main ViewController.
Use didSet to post a Notification.
Add a listener for that Notification in your additional ViewControllers.
i've create TabBar App with 5 tabs, all 5 views are the same and each view include buttons to control one set of equipment, switch input to output on receiver and control volume, so 5 views correspond to 5 rooms.
Now i have 5 different UIViewCOntrollers with nerly the same code, inherited from UIViewController.
But i think it should be one RoomViewController, inhereted from UIViewController, and RoomViewCOntroller shold be used for each room, something like this
class RoomViewController: UIViewController {
#IBOutlet weak var videoSat: UIButton!
func lockAudioLabel(sender: UIButton) {
if sender.isSelected {
return
}
videoSat.isSelected = false
}
#IBAction func videoSatSelect(_ sender: UIButton) {
//send command to equipment
//make some actions
lockVideoLabel(sender: sender)
}
}
And Room1ViewController is
class Room1ViewController: RoomViewController {
}
All rooms have scene in storyboard
So the question is there any way to use one set of iboulets and ibactions in RoomViewController class and inherit them for example in Room1ViewContoller? And how to access them?
So the question is there any way to use one set of iboulets and ibactions in RoomViewController class and inherit them for example in Room1ViewContoller?
The #IBOutlet properties and #IBAction methods you declare in RoomViewController are inherited by subclasses such as Room1ViewController.
But what is not inherited is the design in the nib. When you instantiate a subclass like Room1ViewController, it loads its own view, not some other view controller's view. This loading takes place on a per-instance basis. Therefore, you still need to hook up the interface objects in the nib to the properties and methods in the class declaration. In other words, you still need to make outlets and actions in the nib, because this is a different view.
I am developing an app with 10 view controllers, including 2 webview controllers. Every view controller contains a 'send feedback' button on top right with exactly same style and functionality.
Right now, I am writing exact same code in each view controller for the buttons.
I was wondering if there is a way to write the method only at one place and use it for all the buttons, since the function is same.
I am using swift 3.
Create new subclass of view controller:
class SendFeedbackViewController: UIViewController {
#IBOutlet weak var sendFeedbackButton: UIButton!
#IBAction func sendFeedback() {
/* do whatever you need */
}
}
Then subclass all your view controllers from this new view controller:
class YourViewController: SendFeedbackViewController {
/* your entire logic */
}
In storyboard set class type:
Now you can connect your send feedback button outlet and action:
Use extension of UIViewController.
extension UIViewController {
func sendFeedBack() {
//write your code here
}
}
And call from any ViewController.
I'm, relatively, a beginner, so this may be an entirely common practice—or an entirely impossible one—but I've been wondering if it's possible to modify a view controller added in a storyboard so that instead of (or in addition to?) being an instance of UIViewConroller, it's also an instance of (blahblah)ViewController, e.g. ABUnknownPersonViewController.
That way, instead of doing something like this:
class ViewController : UIViewController {
override func viewDidLoad() {
let test = ABUnknownPersonViewController()
...
self.presentViewController(test, animated: false, completion: nil)
}
}
This could be done:
class ViewController : ABUnknownPersonViewController {
override func viewDidLoad() {
//ViewController already is an ABUnknownPersonViewController, so you can treat it as one
//example below (displayedPerson is a property of ABUnkownPersonViewControllers)
self.displayedPerson...
}
}
EDIT: ABUnknownPersonViewController is a class supplied by Apple, which does not support subclassing (here). With that said, and the understanding that I would obviously like as simple a solution as possible (avoidance of protocols and whatnot), what are my options?
I tried class FourthViewController: UIViewController, ABUnknownPersonViewController, ABUnknownPersonViewController, ABUnknownPersonViewControllerDelegate only to get an error about multiple inheritance.
It sounds like what you actually want to do is to subclass UIViewController, and in your storyboard, set the custom class to your subclass. When the view controller is loaded from the storyboard, it will be an instance of your subclass.
So your subclass would look like this:
class ABUnknownPersonViewController : UIViewController {
override func viewDidLoad() {
self.displayedPerson...
}
}
In the storyboard, highlight the view controller you want to use a custom class for, and in the Custom Class field, type the name of your subclass. If you've done it correctly, it should autocomplete for you.
I have some problems to use subclasses in Swift, hope someone can help me.
What I have
Two view controllers:
VC1 with just some UIButtons
EffectVC that do some animation depending on the button pressed on VC1
import UIKit
protocol viewAnimation {
func initialStateSet()
func finalStateSet()
}
class EffectVC: UIViewController {
#IBOutlet weak var mainImage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
self.initialStateSet()
}
override func viewDidAppear(animated: Bool) {
self.finalStateSet()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func initialStateSet() {
}
func finalStateSet() {
}
}
class GrowingEffect : EffectVC, viewAnimation {
override func initialStateSet() {
// some stuff
}
override func finalStateSet() {
// other stuff
}
}
The problem
Maybe a simple question but I can't do what I want in Swift: I need to set a subclass according to the button that is pressed.
In other words I need to present subclassed view controller from my VC1 according to which button is pressed on VC1.
If I press the first button for example I want to show the VC 2 with the class GrowingEffect for use some custom stuff (this stuff must change according to the selected button).
What I tried
use IBAction for create my subclassed VC2 and show it
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destinationViewController : UIViewController = storyboard.instantiateViewControllerWithIdentifier("EffectVC") as! GrowingEffect
self.presentViewController(destinationViewController, animated: true, completion: nil)
but I got
Could not cast value of type 'ViewAnimationDemo.EffectVC'
(0x109948570) to 'ViewAnimationDemo.GrowingEffect' (0x109948650).
use PrepareForSegue
but I can't set any subclass
What I really want to do
I know there are some other solution, like not using storyboard, but now I describe exactly what I want to do, hoping this is possibile:
have only one view controller in IB (EffectVC) associate with the class EffectVC. The class EffectVC has some subclasses like GrowingEffect.
In my code I want to instantiate the view controller EffectVC with the subclass that I need: for example instantiate the view controller in IB EffectVC with the class GrowingEffect.
I know that if I have one view controller for every subclass of EffectVC I can do what I want but I don't want so many view controller in IB because they are equal, the only things that I want to change are 2 methods.
I think there are some things mixed up in your setup. You should have 2 view controllers, each set up in its file, and each present in the storyboard with its identifier. It is ok if GrowingEffect inherits from EffectVC.
What you currently do with as! GrowingEffect is actually trying to cast the UIViewController instance you get from calling instantiateViewControllerWithIdentifier("EffectVC") to GrowingEffect. This will not work, because it is of type EffectVC.
Rather, you need to call instantiateViewControllerWithIdentifier("EffectVC") if button X is pressed, and instantiateViewControllerWithIdentifier("GrowingEffect") if button Y is pressed.
EDIT
If you use storyboard, you have to instantiate view controllers using instantiateViewControllerWithIdentifier. But you can only get an instance of GrowingEffect, if it is present on the storyboard.
It is not possible to "cast" an instance of EffectVC to GrowingEffect once created.
So, you have two possibilities here:
Use storyboard and put both view controllers on it. Use instantiateViewControllerWithIdentifier to instantiate the view controller you need, depending on the button pressed.
Do not use storyboard. Then you can create the needed view controller manually and use your UINavigationController's pushViewController method to present it.
You can't cast from parent class to child class, parent class just doesn't have the capacity to know what the child is doing. You can however cast from a child to parent, so you would want to set your view controller as GrowingEffect, then cast it to Effect, but again there is no strong suit to doing this either unless some method needs the parent class and you are using the child class. It looks like you need a redesign of how you want your view controllers laid out. Now I am assuming you have 2 children, lets call GrowingEffect and ShrinkingEffect. In your designer, you set your 1 to GrowingEffect and the other to ShrinkingEffect and make sure they have unique identifiers. Then you can use your view to present an Effect, and pass in either of those objects.