I have a iOS + Watch app with a UIPicker, and I keep getting logs related to it that I can't figure out related to the Watch app:
[default] -[SPRemoteInterface handlePlistDictionary:fromIdentifier:]:2977: ComF:->Plugin method .pickerFocus is not implemented by the controller (null)
[default] -[SPRemoteInterface handlePlistDictionary:fromIdentifier:]:2977: ComF:->Plugin method .pickerClearFocus is not implemented by the controller (null)
I checked the documentation and the closest thing I could find was just the pickerDidFocus() method, which seems to be talking about general problem area I'm having, but no clear implementation ideas for specifically .pickerClearFocus and .pickerFocus.
Same with ".pickerSettle" ...
"I'm having the same problem. Looks like a bug of the iOS or XCode, because the public API does not incude a method ".pickerSettle", and the existing method "pickerDidSettle" is actualy called just fine. Everything seems to be working as expected."
from:
https://forums.developer.apple.com/thread/68971
I did found a solution for my case (WKInterfaceButton). The button reference outlet and the button sent action outlet must be in the same class. The message is shown when selector outlet references to the view and the action references the view controller.
Cell :
class aCellView: NSObject {
var delegate: WKInterfaceController? = nil
// The action outlet
#IBAction func doSomeThing() {
self.delegate!.doSomeThing()
}
}
Controller :
class HomeInterfaceController: WKInterfaceController {
// ... where you implement your cell
cell.delegate = self
// ... where you implement your cell
}
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 am working on an open source tutorial using MVVM, Coordinators and RxSwift. I am constructing all the viewcontrollers and models in the coordinator. Controller has a strong reference to viewmodel and when a viewmodel is set, I would like to perform some UI related actions(using property observer didSet). The problem I am facing is that didSet is called before viewDidLoad causing a crash.
Stripped down version of ViewController:
class MessageVC: UIViewController {
var viewModel: MessageViewModel! {
didSet {
manipulateUI() // crashes
}
}
override func viewDidLoad() {
super.viewDidLoad()
manipulateUI() // works fine if setup is correct in coordinator
}
Coordinator stripped down version:
extension AppCoordinator {
convenience init() {
let rootVC = MessageVC() // actual construction from storyboard
let messages = Message.getMessages()
rootVC.viewModel = MessageViewModel(withMessage: messages)
}
My concern is that even though calling manipulateUI in viewDidLoad is working for me currently, the app will crash if I forget to set the viewModel from my co-ordinator making me think that I am using a fragile architecture. I really like updating userinterface from didSet but it is called before viewDidLoad.
I know it is a simple problem but from architecture standpoint it seems fragile. Any suggestions, improvements and comments are appreciated a lot.
I wont say that cases like this can define wether you are dealing with fragile architecture or not because view controllers has their own life cycle which differs a lot from other objects life cycle. Anyway you can easily avoid crashes here using different approaches. For example :
Approach 1:
Put a guard statement at the very beginning of your manipulateUI function so this function wont manipulate UI until both view is loaded and model is set. Then call this function on viewDidLoad method and when viewModel is set:
func manipulateUI(){
guard let viewModel = self.viewModel , isViewLoaded else {
return
}
//continue manipulation here
}
Approach 2:
Since you are not sure wether view is loaded when you set the model and don't know if views are initialized yet, you can access the views as optional properties in manipulateUI function:
func manipulateUI(){
self.someLabel?.text = self.viewModel.someText
//continue manipulation here
}
Approach 3:
Since you are using RxSwift you can always register an observer for view controller's isViewLoaded property and set the data source after you are sure that view is loaded
Crash happens because at this point
rootVC.viewModel = MessageViewModel(withMessage: messages)
view controller is not initialized.
It won't work the way you're trying to accomplish, you have to call manipulateUI() inside viewDidLoad.
I'm learning Swift and I'm studying the delegation pattern.
I think I understand exactly what is delegation and how it works, but I have a question.
I have a situation where Controller A is the delegate for Controller B.
In controller B I define a delegate protocol.
In controller B I set a variable delegate (optional)
In controller B I send message when something happens to the delegate
Controller A must adopt method of my protocol to become a delegate
I cannot understand if every delegate controller (in this case A) listens for messages sent by controller B or If I have to tell to controller B that A is now his delegate.
I notice that someone use this code (in controller A)
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Example" {
let navigationController = segue.destinationViewController as UINavigationController
let controller = navigationController.topViewController as AddItemViewController
controller.delegate = self
}
}
Is this the only way to tell a delegator who is his delegate?
I believe, you need to tell a deligator who is its delegate upon creation of that it. Now, the delegator can be created programatically or through storyboard. So, based on that you have two options, you can tell it who is its delegator programatically like you showed in the code or from IB.
The key here is upon creation. Let's me explain myself. Take the case of a UIView. Say, you want a Custom UIView object(CustomView). So, you drag and drop a UIView in your View Controller and in the identity inspector, you assign its class as of your CustomView's class. So, basically, as soon as the controller is created, your custom view will also be created. Now, you can either say it that the View Controller in which it is created is its delegate or You can go to the IB and connect the view's delegate to the View Controller.
Now, let's assume that you wanted the custom view to be created in your ViewController programatically. In that case, you would probably call the -initWithFrame: method to create the view and upon creation you tell that delegator that who is its delegate like-
myCustomView.delegate = self;
same goes with a View Controller.
controller.delegate = self;
So, basically to tell a delegator who is its delegate, you first need that delegator to be created. At least, that's what I think.
I think one of the best example of delegation is UITableView.
Whenever you want the control of various properties of a tableView e.g. rowHeight etc, you set your controller to be the delegate of your tableview. To set the delegate of your tableView you need to have tableView created obviously as pointed out by #natasha.
So in your case, you can set delegate of your delegator when you create it or when you find a need for the controller to be delegate of your delegator but you definitely need your delegator to be present to set its property.
You can set your controller as delegate at any time when you require control.
I'm sure you want your UIViewController to act like described, but here is a simpler example how to use the delegation pattern with custom classes:
protocol ControllerBDelegate: class {
func somethingHappendInControllerB(value: String)
/* not optional here and passes a value from B to A*/
/* forces you to implement the function */
}
class ControllerB {
var delegate: ControllerBDelegate?
private func someFunctionThatDoSomethingWhenThisControllerIsAlive() {
/* did some magic here and now I want to tell it to my delegate */
self.delegate?.somethingHappendInControllerB(value: "hey there, I'm a magician")
}
func doSomething() {
/* do something here */
self.someFunctionThatDoSomethingWhenThisControllerIsAlive()
/* call the function so the magic can really happen in this example */
}
}
class ControllerA: ControllerBDelegate {
let controllerB = ControllerB()
init() {
self.controllerB.delegate = self /* lets say we add here our delegate*/
self.controllerB.doSomething() /* tell your controller B to do something */
}
func somethingHappendInControllerB(value: String) {
print(value) /* should print "hey there, I'm a magician" */
}
}
I wrote the code from my mind and not testet it yet, but you should get the idea how to use such a pattern.
I tried to add a target to a UIButton and stumbled upon a weird behaviour
if i try:
//h = a collection view header
switch myVar {
case "none":
h.button.addTarget(self, action: "buttonTapped:", forControlEvents: .TouchUpInside)
func buttonTapped(sender:AnyObject) {
sendFriendRequest(self.targetUser,nil
}
}
I get SIGABRT - with "selector not found"
but if I move the function out of the switch case and make it a method of my ViewController, everything works as expected.
Anyone has an explanation for that? Is is just not allowed or are there technical reasons?
Selectors don't need to be class functions, but they must visible to the object call the selector (i.e. can't be marked as private or inside a method).
You can call any method in the project by using other class's instance instead of self.
Check this answer for the details.
https://stackoverflow.com/a/33068386/2125010
I have a custom UIView (called GridView) that I initialize and then add to a ViewController (DetailViewController). GridView contains several UIButtons and I would like to know in DetailViewController when those buttons are touched. I'm new to Swift and am wondering what is the best pattern to use to get those events?
If you want to do this with notifications, use 1:
func postNotificationName(_ notificationName: String,
object notificationSender: AnyObject?)
in the method that is triggered by your button. Then, in your DetailViewController, add a listener when it is initialized with 2:
func addObserver(_ notificationObserver: AnyObject,
selector notificationSelector: Selector,
name notificationName: String?,
object notificationSender: AnyObject?)
Both functions can be called from NSNotificationCenter.defaultCenter().
Another method would be to add callbacks which you connect once you initialize the GridView in your DetailViewController. A callback is essentially a closure:
var callback : (() -> Void)?
which you can instantiate when needed, e.g.
// In DetailViewController initialization
gridView = GridView()
gridView.callback = { self.doSomething() }
In GridView you can trigger the callback like this:
func onButton()
{
callback?()
}
The callback will only execute, if unwrapping succeeds. Please ensure, that you have read Automatic Reference Counting, because these constructs may lead to strong reference cycles.
What's the difference? You can connect the callback only once (at least with the method I've showed here), but when it triggers, the receiver immediately executes its code. For notifications, you can have multiple receivers but there is some delay in event delivery.
Lets assume your GridView implementation is like as follows:
class GridView : UIView {
// Initializing buttons
let button1:UIButton = UIButton(...)
let button2:UIButton = UIButton(...)
// ...
// Adding buttons to view
self.addSubview(button1)
self.addSubview(button2)
// ...
}
Now, we will add selector methods which will be called when a button is touched. Lets assume implementation of your view controller is like as follows:
class DetailViewController : UIViewController {
let myView:GridView = GridView(...)
myView.button1.addTarget(self, action: "actionForButton1:", forControlEvents: UIControlEvents.TouchUpInside)
myView.button2.addTarget(self, action: "actionForButton2:", forControlEvents: UIControlEvents.TouchUpInside)
// ...
func actionForButton1(sender: UIButton!) {
// Your actions when button 1 is pressed
}
// ... Selectors for other buttons
}
I have to say that my example approach is not a good example for encapsulation principles of Object-Oriented Programming, but I have written like this because you are new to Swift and this code is easy to understand. If you want to prevent duplicate codes such as writing different selectors for each button and if you want to set properties of your view as private to prevent access from "outside" like I just did in DetailViewController, there are much much better solutions. I hope it just helps you!
I think you better create a class called GridView that is inherited from the UIView. Then, you can connect all you UI element with you class as IBOutlet or whatever using tag something like that. Later on, you can ask the instance of GridView in DetailViewController so that you can connect as IBAction.
Encapsulation is one of the principles of OOP.