Swift generic function not working - swift

So, I made a generic function in a structure which has some static methods for helping to create customized UIButtons and so on. So I did this code:
static func createAlertPicker<T: UIViewController where T: UIPickerViewDelegate, T: UIPickerViewDataSource>(#title: String, inout forPicker picker: UIPickerView, viewController: T) -> UIAlertController {
let alert = UIAlertController(title: title, message: "\n\n\n\n\n\n\n\n\n\n", preferredStyle: .Alert)
alert.view.tintColor = data.backgroundColor
picker = createPickerViewWithFrame(CGRectZero, delegate: viewController, dataSource: viewController, backgroundColor: UIColor.clearColor(), addToView: alert.view)
picker.frame = CGRect(x: 2, y: 70, width: 266, height: 162)
return alert
}
I don't get an error doing this but when calling this method in a ViewController like so:
let alert = CreatorClass.createAlertPicker(title: "select sortage", forPicker: &self.pickerView!, viewController: self)
I get a crazy error telling me Type 'UIViewController' does not conform to protocol 'UIPickerViewDelegate'. Although it is implemented, that's the ViewController's declaration:
class PlayerDetails:UIViewController, UITableViewDataSource, UITableViewDelegate, UIPickerViewDelegate, UIPickerViewDataSource
By the way, this problem only occurs in one ViewController, I'm calling it several times. Maybe it should be mentioned that this line of code (let alert = ...) is not even compiled in the first place.
I really don't understand this. Thank for any help ! :]

Yeah that error is a total lie.
The problem is with the middle parameter: &self.pickerView!.
self.pickerView is an optional. Your optional contains a reference, but unwrapping that reference passes back you back a fresh copy of the reference by value. You don’t get access to the original reference inside the optional. So when you call !, you get an immutable value. You can’t change it or assign to it, and that means you can’t pass it as an inout parameter.
Here’s a simpler example:
let i: Int? = 5
func f(inout i: Int) { i = 6 }
f(&i!) // error: 'Int' is not convertible to '#lvalue inout $T3’
This is the compiler saving you from a potentially very confusing runtime bug – if the value were passed in and changed, it would make no difference to the value you actually intended to change. Only the temporary copy would have been changed.
It might be a bit confusing because classes are reference types so you’re not used to thinking about them in value terms. But references themselves are values. What you are getting out of the unwrap is a copy of the reference, not a copy of the thing referred to.
If you change your call to something like the following, it should work:
if var picker = self.pickerView {
let alert = CreatorClass.createAlertPicker(title: "select sortage", forPicker: &picker, viewController: self)
// don’t forget to assign the value back...
self.pickerView = picker
}
This version also has the benefit of not exploding in flames if you’ve ever forgotten to set forPicker to be a value before you force unwrap it.
But if all you are using the inout for is to return a new picker (doesn’t look in your createAlertPicker like you use the value passed in, only assign to it), then why not ditch the inout and make the function return a pair of values:
static func createAlertPicker
<T: UIViewController where T: UIPickerViewDelegate>
(#title: String, viewController: T)
// return a tuple
-> (UIPickerView,UIAlertController) {
// etc…
var picker = createPickerViewWithFrame(CGRectZero, delegate: viewController, dataSource: viewController, backgroundColor: UIColor.clearColor(), addToView: alert.view)
picker.frame = CGRect(x: 2, y: 70, width: 266, height: 162)
return (picker, alert)
}
let (picker, alert) = CreatorClass.createAlertPicker(title: "select sortage”, viewController: self)
self.pickerView = picker

Related

Protocol-Oriented Programming Extension variable init twice

Here is the situation. I have a protocol, and extension of it.
protocol CustomViewAddable {
var aView: UIView { get }
var bView: UIView { get }
func setupCustomView()
}
extension CustomViewAddable where Self: UIViewController {
var aView: UIView {
let _aView = UIView()
_aView.frame = self.view.bounds
_aView.backgroundColor = .grey
// this is for me to observe how many times this aView init.
print("aView: \(_aView)")
return _aView
}
var bView: UIView {
let _bView = UIView(frame: CGRect(x: 30, y: 30, width: 30, height: 30))
_bView.backgroundColor = .yellow
return _bView
}
func setupCustomView() {
view.addSubview(aView);
aView.addSubview(bView);
}
}
And I make a ViewController to conform this protocol then I add this custom 'aView' to my ViewController's view.
class MyVC: UIViewController, CustomViewAddable {
override func viewDidLoad() {
super.viewDidLoad()
setupCustomView()
}
}
I run it. In my console log it prints twice of init and I trying to do something in my custom 'aView' and it failed. (The code I paste above that I simplified so that it'll be very easy to show my intension)
Could anybody to explain why or make a fix to it that I'll be very appreciated.
Because your var aView: UIView is computed variable not store variable,
So every time you call aView, it will create a new UIView,
You can use Associated Objects in NSObject here is some tutorials:
swift-objc-runtime
associated-objects
Hope this may help.
Basically in the way you implemented the setupCustomView method nothing should work because as mentioned in another response you're using a computed property, so this implies that every time you access the property it's created again.
You don't need to use associated-objects or something like that to achieve what you want, you only need to keep the reference of the aView at the beginning avoiding calling it again, in this way:
func setupCustomView() {
let tView = aView // only is computed once
view.addSubview(tView)
tView.addSubview(bView)
}
I hope this help you.

Swift 3 - What does this mean?

I converted my project to Swift 3, I am having trouble with the following piece of code, it seems that this ( >>>-) is no longer used in Swift 3. What does >>>- actually mean? and how to use it in Swift 3?
fileprivate func addImageToView(_ view: UIView, image: UIImage?) -> UIImageView? {
guard let image = image else { return nil }
let imageView = Init(UIImageView(image: image)) {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.alpha = 0
}
view.addSubview(imageView)
// add constraints
[NSLayoutAttribute.left, .right, .top, .bottom].forEach { attribute in
(view, imageView) >>>- { $0.attribute = attribute }
}
imageView.layoutIfNeeded()
return imageView
}
The >>>- returns a value. Before Swift 3, the compiler did not warn you if you did not assign the return value of a function or method to anything. Starting in Swift 3, you will get an error if the return value of a function or method (including operators) is unused. A library author can fix this by adding the #discardableResult annotation, but in the meantime you will have to change that line of code to:
let _ = (view, imageView) >>>- { $0.attribute = attribute }
Despite the fact this answer can solve the issue in your side, there still a need to the author of the library to make some changes on his side.
The problem is because swift removed completely the var from function parameters and somehow that change is affecting the param you're receiving in the closure. (It seem a bug to me). You need to figure it out what is the type of the inout param the library is passing to the operator closure, and declare it as inout in your call. let's say that type is Constraint (this might not be the same as the library), then on you:
(view, imageView) >>>- { (i : inout Constraint) in
i.attribute = attribute
}
But again, the author might still need to make some changes in the operator implementation as well.

Execute code when pressing button created programmatically in function

I have created a custom Alert View with buttons that are created dynamically.
Everything works fine except I don't know how to make each button do something different.
What I mean by that is that I call a function addButton and I pass a block of code (like in UIAlertAction()). I want that code to execute after I press the button but it actually gets executed when the button is loaded.
Sorry if I'm unclear but I don't know the specific terms about my issue. Basically I tried to reproduce UIAlertView and I don't know how to fully remake UIAlertAction.
Here's the method (note: it's in a custom class):
func addButton(buttonNumber number: Int, title: String, color: UIColor, actions: () -> Void ) {
// Initialization
let button = UIButton(frame: CGRectMake(5, self.wrapper.bounds.height-CGFloat(55*number), self.wrapper.bounds.width-10, 50))
// Configuration
button.setTitle(title, forState: .Normal)
button.backgroundColor = color
button.layer.cornerRadius = 10
button.addTarget(self, action: #selector(myCustomView.buttonPressed(actions:)), forControlEvents: .TouchUpInside)
wrapper.addSubview(button)
}
This is the selector (I know it's not correct):
func buttonPressed(actions actions: () -> Void) {
actions() // How do I get the actions from addButton?
}
And this is when I call addButton:
let alertView = UIStoryboard(name: "Popups", bundle: nil).instantiateViewControllerWithIdentifier("HAlertView") as! HAlertViewVC
prepareAlert(alertView)
alertView.loadAlert(title: "Hold on", message: "You need to add at least the goal before saving", image: "Warning-Icon", numberOfActions: 1)
alertView.addButton(buttonNumber: 1, title: "Close", color: UIColor(red: 246.0/255.0, green: 71.0/255.0, blue: 71.0/255.0, alpha: 1), actions: {
// alertView.dismiss() // This is the problem. That's just a function that hides the alert view but the issue is that it gets executed right away.
})
What you are looking for are closures:
var some_code_block = {
print("my awesome code block!")
}
Put this at a higher / global scope, then you can run / change the block at will, simply by adding '()' at the end of the closure (variable name)
func addCode() {
// Wont execute:
some_code_block = { print("new code inserted") }
}
func buttonPressed() {
// Executes:
some_code_block()
}
Personally, I use arrays / dictionaries to store / update code blocks on the fly.. then you can iterate through / match indices/keys to get the code block you want.
https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html
If your problem is actually making different buttons, then you can store your newly created instance of a UI button into an array or dictionary
[UIButton]
An array / dictionary of buttons, matching with an array / dictionary of code blocks can certainly work... You just have to make sure they are in the right scope / and synchronize indices / keys.
Or, you could create a class / struct that holds a UIButton instance, and a code block, then put the class / struct into an array / dictionary
// Make your own initializer for your purposes
struct ButtonStuff {
var button: UIButton?
var code_block: ()?
}
// Make empty array that holds type ButtonStuff (global / outer scope)
var buttons: [ButtonStuff] = []
func addButton(pram1:Pram1, pram2:Pram2) {
// Make a new button (called locally inside of a func)
var new_button = ButtonStuff()
// Put stuff in here...
// new_button.button = pram1
// new_button.code_block = pram2
// Add our locally made new_button to the Global buttons array
buttons.append(new_buttons)
}
NOTE: The pram1, pram2 can be whatever you want, but to store to the code block (without running it) you want type (). If you want to automatically run the code block, you would use type ()->()
There are many ways of doing the above (you don't have to use optionals); just a quick example. You would then just have to make some logic / algo to find the right button, display it, then call the code block.
If you have any questions, need me to edit my answer, please leave a message in the comments with #fluidity

NSButtion action not called when target is different object

I've got an NSButton in a View Controller that, when clicked, should call a method in an instance of another class (I have that instance in the View Controller). However, the action method is never called.
My code is below (it's short and simple). Please can somebody explain why this is?
View Controller class with the button:
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let b:NSButton = NSButton(frame: NSRect(x: 150, y: 200, width: 30, height: 30))
self.view.addSubview(b)
let g = Global()
b.target = g
b.action = #selector(g.s)
}
}
Class called 'Global' that I create an instance of, that the button should then call a method within:
class Global:NSObject {
override init() {
super.init()
}
#objc dynamic func s() {
Swift.print("S ran")
}
}
Thanks
Update: For easy reproduction, I've created a GitHub repo showing the issue in its simplest form here.
The problem is that by the time you click the button, target has been set to nil. This is because g is stored as a local variable and target is a weak property, so after viewDidLoad is finished, g is released and the target becomes nil. So, by the time you click the button, there is no object on which to call the action.
You need to store a strong reference to the target somewhere. One way would be to store it as an instance variable on your view controller:
class ViewController: NSViewController {
let g = Global()
override func viewDidLoad() {
super.viewDidLoad()
let b:NSButton = NSButton(frame: NSRect(x: 150, y: 200, width: 30, height: 30))
self.view.addSubview(b)
b.target = g
b.action = #selector(g.s)
}
}

How to initialize a variable using another variable in an SKScene

I want to be able to make two variables available to the entire SKScene, and all functions inside of it. One of these variable using the other one to create its value. I understand why I cannot do this, but I don't know a fix for it. I have this code:
class GameScene: SKScene {
let num : CGFloat = 1.25
let reciprocal = 1 / num // <— This Line
override func sceneDidLoad() {
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
But I am obviously getting an error the line 4.
Cannot use instance member 'num' within property initializer; property
initializers run before 'self' is available
This means that I cannot use the variable because it is connected to the skscene, and the scene hasn't been implemented fully yet. Is there a way to declare this variable without throwing an error and making it assessable everywhere within this class?
Since reciprocal depends directly upon num, it could make sense to let the prior be a computed property based on the latter
class GameScene: SKScene {
let num: CGFloat = 1.5
var reciprocal: CGFloat { return 1/self.num }
// ...
}
Since num is an immutable property and will never change at runtime, another alternative is to let reciprocal be a lazy variable, computed upon its first use
class GameScene: SKScene {
let num: CGFloat = 1.5
lazy var reciprocal: CGFloat = { return 1/self.num }()
// ...
}
(Or, implement your own custom initializer for the GameScene, where you can initialize num and reciprocal to e.g. a given value and its reciprocal, respectively).