I have to set up a project that has to contain several targets. The goal is to have a code base to share it with all the targets that will be created later. I make an example : I will have a UIViewController for the login, but this will be different for every target. My idea is to have a code base to share with every specific LoginViewController. What is the best approach to structure the code? I started writing a protocol, here it is :
protocol LoginProtocol {
weak var txtUsername: UITextField? { get set }
weak var txtPassword: UITextField? { get set }
weak var btnLogin: UIButton? { get set }
func login()
}
The idea is to implement this protocol in every controller.
The other approach is to write a sort of base view controller that every other controller will inherit from. Like this :
// MARK: - Instance vars and IBOutlets
class LoginViewController: UIViewController {
#IBOutlet weak var txtUsername: UITextField?
#IBOutlet weak var txtPassword: UITextField?
#IBOutlet weak var btnLogin: UIButton?
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension LoginViewController {
func login() {
print("Hello")
}
}
What do you think? What is the best choice, architecturally?
Tough to "answer" this since it is to some extent opinion...but here's my input:
I think it boils down to common behavior: If you use a protocol, but then feel like you are writing almost the same code every time you implement it...then you probably want a base class. I love protocols and use them heavily, but I think people are too quick to dismiss base classes these days. (There ARE pros and cons, but so many people blindly make protocols without thinking about it.)
I will sometimes use a hybrid of a base class that has a delegate to "configure" certain parts.
Related
I understand it’s rather basic, but I’m only trying to get a grasp on basic functions.
I have produced some code by partially my own knowledge and partial bits from different guides.
I am not getting any errors, but the label is not displaying itself as “Text”. I believe it’s to do with the order/place my code is put.
Please help explain how I can fix this!
Please note as well:
I have just a single label called myLabel (named under the document section of my the identity inspector
It is has the text “Loaded” put into it already when I put it in.
I have no other code anywhere, only the default new project code.
I renamed the ViewController to ViewManager to avoid a class error.
First image: This is the image just so you know the location and other bits. I’ll attach the code too:
Second image: What I get, with no errors:
Third image: My main storyboard file:
And now it in code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel!
#IBAction func labelSet() {
myLabel.text = "Text"
}
}
Make sure that the IBAction is connected to Touch Up Inside in Interface Builder.
Change the signature of the IBAction to
#IBAction func labelSet(_ sender: UIButton) {
Your function func labelSet() isn't called anywhere. Neither in the Storyboard nor elsewhere.
You can call it in viewDidLoad() like this:
override func viewDidLoad() {
super.viewDidLoad()
labelSet()
}
Alternatively call it after the label has loaded.
#IBOutlet weak var myLabel: UILabel! {
didSet {
labelSet()
}
}
maybe I am missing something really fundamental here, but after staring at the code for an hour or so, my brain is going trough cycles and I would appreciate a fresh glance at this problem.
I have the following UIView:
import UIKit
protocol DetailViewWillShowUpDelegate {
func sendDetailOpened(_ openedBool: Bool)
}
class locationXIBController: UIView {
#IBOutlet weak var loationLabel: UILabel!
#IBOutlet weak var vsedniOteviraciDobaLabel: UILabel!
#IBOutlet weak var prijmajiKartyLabel: UILabel!
#IBOutlet weak var detailViewButtonOutlet: UIButton!
#IBOutlet weak var backgroundViewButton: UIButton!
let openedBool = true
var detailViewWillShowUpDelegate: DetailViewWillShowUpDelegate?
override func awakeFromNib() {
super.awakeFromNib()
}
#IBAction func vecerkaDetailButtonPressed(_ sender: UIButton) {
detailViewWillShowUpDelegate?.sendDetailOpened(openedBool)
print("pressed")
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if let result = detailViewButtonOutlet.hitTest(convert(point, to: detailViewButtonOutlet), with: event) {
return result
}
return backgroundViewButton.hitTest(convert(point, to: backgroundViewButton), with: event)
}
}
Now the problem is, that when I call/press the vecerkaDetailButtonPressed function I get "pressed" output in the console but the protocol for some reason doesn't go trough.
The other side looks like this (stripped for simplicity):
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
let locationXIB = locationXIBController()
let isVecerkaDetailOpened = false
override func viewDidLoad() {
locationXIB.detailViewWillShowUpDelegate = self
}
extension MapViewController: DetailViewWillShowUpDelegate {
func sendDetailOpened(_ openedBool: Bool) {
isVecerkaDetailOpened = openedBool
print("success")
}
}
I know the protocol value at the moment of execution is nil. As I said, any help is appreciated, thanks!
First, a couple of naming convention issues:
The name locationXIBController is a bad choice for a UIView object. It is a view object, not a controller object.
Second, class names in Swift should start with an upper-case letter. So LocationXIBView would be a much better name for that view class.
Next, your code
let locationXIB = locationXIBController()
...is wrong. That creates a brand-new instance of your locationXIBController class that you never install in your view hierarchy. You should make that line an IBOutlet:
#IBOutlet weak var locationXIB: locationXIBController!
And then you should control-drag from the locationXIBController in your StoryBoard onto the outlet in your view controller. That will cause Interface Builder to connect the outlet.
Now when you run your program the variable locationXIB will be connected to the locationXIBController view from your storyboard/XIB when it's loaded.
In addition to the answer of #Duncan C, you might check whether you need super.viewDidLoad() at the top of the viewDidLoad() method in the MapViewController class? Not doing that can lead to quirky things in your app.
I asked:
So does detailViewWillShowUpDelegate actually point at anything, or is it nil?
And you replied:
I just tried debugging and it is actually nil
So that's the problem... you need to set detailViewWillShowUpDelegate to point to a valid delegate object. This is often done in the .xib file or storyboard, and sometimes people forget to make that connection, so check there if it makes sense. Else you'll just need to get a reference to the delegate at some point before the code in question can run and set it up.
Answer to the credit of #Paulw11
I finally managed to get it working by communicating like so:
step 1) 1:1 communication via protocol between MKAnnotation and MKAnnotationView
step 2) 1:1 communication via protocol between MKAnnotationView and MapViewController passing the same data
Finally works like a charm, thanks!
I am building one framework. I need to get the IBOutels list from UIViewcontroller through code. I have written InterfaceOutletsReadable protocol. If the framework user conforms this protocol I have to read the list of IBOutlets from ViewController.
protocol InterfaceOutletsReadable {
///Read the outlets objects
func readOutlets()
}
extension InterfaceOutletsReadable {
//TODO:- Stuck at this stage. Here I have to read the viewcontroller IBOutlets
}
class HomeViewController: InterfaceOutletsReadable {
#IBOutlet weak var userNameTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var errorLabel: UILabel!
}
Edit: I don't want to get the list from IBOutlet Collection. Is there any way to get all outlets programmatically?
you can take IBOutletCollection of one textField and connect to all others.It maintain Array of Outlet and You Can access through index.
#IBOutlets exist only at design time, there are no ones even at compile time. So it’s imposible to get all outlets in the way you want. Outlets have no difference with other variables.
If you need list of the outlets you may implement readOutlets() in your HomeViewController and return Array consist any variables you want.
I want the user to be able to select a range of years on a WatchKit App, since there's no UIPicker in WatchKit, can I use a slider to make the user click + or - to increment the year, to be displayed on my label above? I read the documentation but still can't figure out how to implement the setNumberOfSteps method....alternatively, is there a better way to do this than a slider?
class InterfaceController: WKInterfaceController {
#IBOutlet weak var label: WKInterfaceLabel!
#IBOutlet weak var sliderOutlet: WKInterfaceSlider!
#IBAction func slider(value: Float) {
func setNumberOfSteps(numberOfSteps: Int){
label.setText("\(numberOfSteps)")
}
}
You can definitely do this with a slider. To me there isn't probably a better alternative. You could also present a table view modally with all the possible years you want to be able to select from, but I think the slider is probably a better option if you don't have too many years to pick from. Here's a quick example of connecting a WKInterfaceSlider with a WKInterfaceLabel.
Storyboard
Add a WKInterfaceSlider and WKInterfaceLabel to your interface controller
Add the slider and label IBOutlets
Add a sliderValueChanged IBAction to your InterfaceController class
Set the properties to match the screenshots
Code
import WatchKit
class InterfaceController: WKInterfaceController {
// MARK: - Properties
#IBOutlet weak var label: WKInterfaceLabel!
#IBOutlet weak var slider: WKInterfaceSlider!
// MARK: - Interface Callback Methods
#IBAction func sliderValueChanged(value: Float) {
let roundedValue = Int(round(value))
self.label.setText("\(roundedValue)")
}
}
Hopefully that helps shed some light.
Maybe it is the wrong idea completely to use the same xib multiple times in one viewcontroller, but it looked in some way better than creating an x amount of views with the same labels and the same buttons..
And of course some fun with xibs:)
To give you a quick impression of what I achieved so far:
In the class belonging to the xib, I created a delegate so I could catch a button press in my main view controller.
import UIKit
protocol TimerViewDelegate : class {
func timerButtonTapped(buttonState : NSInteger)
}
class TimerView: UIView {
var delegate : TimerViewDelegate?
var currentButtonState = 0
#IBOutlet var view: UIView!
#IBOutlet var timerLabel: UILabel!
#IBOutlet var button: UIButton!
required init(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
NSBundle.mainBundle().loadNibNamed("TimerView", owner: self, options: nil)
self.addSubview(self.view)
}
#IBAction func buttonTapped(sender:UIButton) {
if currentButtonState == 0{
currentButtonState = 1
} else{
currentButtonState = 0
}
self.delegate?.timerButtonTapped(currentButtonState)
}
}
I know it is not super fancy stuff, but at the moment I'm only evaluating if its any use to do this at all.
In my main view controller I registered outlets for the xibs in a way like:
#IBOutlet weak var timerView1 : TimerView!
#IBOutlet weak var timerView2 : TimerView!
#IBOutlet ...
And in viewDidLoad():
self.timerView1.delegate = self
self.timerView2.delegate = self
self...
Later I can catch the button presses in the following method:
func timerButtonTapped(buttonState: NSInteger) {
println("button press from nib, with state \(buttonState)")
}
From here it does make a difference if I press the button from the top xib or another one, since they keep track of their own buttonstate.
But how can I distinguish the different xibs from each other like this?
I can give the xibs themselves a tag, but I don't know if that has any use. Also talking to their labels from my main view, will have a simular problem..
Even if this is a completely wrong approach of using xibs, I'm still interested how to solve this.
Thank you for your time!
You pretty much have your solution already, you just need to improve your protocol method specification, basically by adding the TimerView that is passing on the button press.
(compare to a delegate protocol like UITableViewDelegate, where the table view always passes itself...)
So, something like:
protocol TimerViewDelegate : class {
func timerButtonTapped(sender: TimerView, buttonState : NSInteger)
}
and then the delegate can find out which TimerView is associated and do something with it.
Incidentally, it's likely best to store the TimerView instances in an array, sorted in some way, so that you can easily access the details.
You can use the tag property safely. Apple documentation says:
An integer that you can use to identify view objects in your application.