Swift: Unable to set property value on mutable object paramter - ios

I have the following code:
func enableDelaysContentTouchesIncudlingSubviews(enable: Bool) {
self.setDelaysContentTouches(enable, forObject: self)
for obj: AnyObject in self.subviews {
self.setDelaysContentTouches(enable, forObject: obj)
}
}
private func setDelaysContentTouches(var value: Bool, var forObject obj: AnyObject) {
if obj.respondsToSelector("setDelaysContentTouches:") {
obj.delaysContentTouches = value
}
}
On the second function, the line obj.delaysContentTouches = value raises the following error: Cannot assign to property: 'obj' is immutable
I don't understand the reason since obj is declared as a var parameter. Therefor it should be mutable in my understanding.
Could somebody please explain me the reason and also provide a workaround.
Thanks in advance!

My guess is it's an issue of AnyObject technically being a protocol
Assuming you're working in a view, maybe try something along these lines:
func enableDelaysContentTouchesIncudlingSubviews(enable: Bool) {
self.setDelaysContentTouches(enable, view: self)
self.subviews.forEach({setDelaysContentTouches(enable, view: $0)})
}
private func setDelaysContentTouches(enable: Bool, view: UIView) {
if let viewAsScrollview = view as? UIScrollView {
viewAsScrollview.delaysContentTouches = enable;
}
}
This is a much swiftier way of doing things, more clear, and doesn't use "respondsToSelector"
More info, and possibly a more direct answer about respondsToSelector in swift here

Related

Value of type 'UIViewController' has no member 'newExerciseDelegate'

I've searched for a solution to this problem, even tried following a few tutorials to try to solve this, but for some reason I'm ending up with the same issue. I'm attempting to pass a custom object to a different view controller, but every time I try I get the error "Value of type 'UIViewController' has no member 'newExerciseDelegate'"
My delegate:
protocol exerciseDelegate {
func savedExercise(newExercise: Exercise)
}
my sending VC uses the following code:
var newExerciseDelegate: exerciseDelegate!
.
.
.
#IBAction func saveExerciseWasPressed(_ sender: UIButton) {
if (checkFields() == true){
newExercise = Exercise(name: exerciseNameField.text!, weight: Int32(weightField.text!)!, reps: Int32(numberOfRepsField.text!)!, sets: Int32(numberOfSetsField.text!)!, muscleGroupFocus: .cardio)
newExerciseDelegate.savedExercise(newExercise: newExercise)
dismiss(animated: false, completion: nil)
}
}
My receiving VC uses the following code:
#IBAction func addExerciseBtnWasPressed(_ sender: Any) {
guard let newExerciseVC = storyboard?.instantiateViewController(withIdentifier: "NewExerciseVC") else { return }
newExerciseVC.newExerciseDelegate = self // error present on this line
presentDetail(newExerciseVC)
}
I'm sure it's a stupid mistake, but I'm not seeing it. Any help is appreciated, thank you.
You should specify which class it is.After the code know which class actually it is, then you can access it's public objects, methods, variables etc.
#IBAction func addExerciseBtnWasPressed(_ sender: Any) {
guard let newExerciseVC = storyboard?.instantiateViewController(withIdentifier: "NewExerciseVC") as? NewExerciseViewController else { return }
newExerciseVC.newExerciseDelegate = self
presentDetail(newExerciseVC)
}
If you are accessing that delegate which is declared in your ViewController then you should call in the below way.
let childOne = self.storyboard?.instantiateViewController(withIdentifier:"WhatsNewViewController") as? WhatsNewViewController
You have to downcast the instantiated view controller to your custom view controller class:
guard let newExerciseVC = storyboard?.instantiateViewController(withIdentifier: "NewExerciseVC") as? YourViewControllerClass else { return }
newExerciseVC.newExerciseDelegate = self
Also you should use a capital E for your protocol's name.

Selectors in Swift 4.0

I was wondering if there is a more 'swift 4' way of creating a selector and calling a function? I am wanting to have the click of the Status Bar Button to call a simple print command from a function, but is this outdated or is there a more efficient 'swift' way of doing this?
button.action = #selector(myFunction)
#objc func myFunction (sender: NSStatusBarButton) {
print("Hi")
}
I am not sure if there's any good way to avoid using the target/action pattern under the hood, but you can definitely try to hide it.
Personally I use ReactiveSwift for all callbacks so I never have to use this awkward objc syntax. Another way to do it would be to hide this inside an extension. For instance, you can try something like:
extension UIButton {
private struct AssociatedKeys {
static var TouchUpClosure = "touchUpClosure"
}
internal var onTouchUpInside: ((UIButton) -> ())? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.TouchUpClosure) as? (UIButton) -> ()
}
set {
objc_setAssociatedObject(
self,
&AssociatedKeys.TouchUpClosure,
newValue as? (UIButton) -> (), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC
)
button.action = #selector(executeTouchUpInside:)
}
}
#objc func executeTouchUpInside(sender: UIButton) {
self.touchUpInside(sender)
}
}
Which allows you to use a "more swift" syntax (no #objc or #selector):
button.onTouchUpInside = { _ in print("Hi") }
Disclaimer - I haven't checked if this exact code compiles, this post is more about sharing an idea.

"Prepare for segue" for WatchKit

I am attempting to transfer a string from one view controller to another. The Problem that I having is that it is required to set the string 'as any object'.
The code I have below returns a nil in the console. Is there any way to send data across VC's as strings? Thanks in advance.
In MainVc-
func contextForSegueWithIdentifier(segueIdentifier: String) -> AnyObject? {
// You may want to set the context's identifier in Interface Builder and check it here to make sure you're returning data at the proper times
// Return data to be accessed in ResultsController
var currentValue = "Hellooo"
return currentValue as String as AnyObject
}
In receiving VC.
override func awake(withContext context: Any?) {
super.awake(withContext: context)
if let val: Any = context as? Any {
print(val)
} else {
print("An error occurred")
} // Configure interface objects here.
}
The output is simply nil.
First of all currentValue as String has no effect, currentValue is already a String.
Secondly, casting val to Any again makes no sense, since val is already of non-explicit type, Any. You should conditional cast it to String, since you are expecting a String variable.
For such a simple case, I wouldn't use 'contextForSegueWithIdentifier' at all, I would just simply use presentController(withName:context:) or pushController(withName:context:).
Using these you don't need to do any casting, you can simply do presentController(withName: "MyContoller", context: "Hello").
I came across with the same problem. The solution was as Larme suggested, used the appropriate function for swift 3 or 4, in this case, I assume you tried to use this code
override func contextForSegueWithIdentifier(segueIdentifier: String) -> AnyObject? {
but the compiler suggests that doesn't have to be overridden because Method does not override any method from its superclass
The easy solution is to remove the override. The app compiles but the contextForSegue function is never dispatched.
The correct structure is as follow:
override func contextForSegue(withIdentifier segueIdentifier: String) -> Any?
{

Isfirstresponder swift issue

I get a "Method does not override any method from its superclass" for a is first responder function. I looked this up online and theres no solution. could you please help me fix this. Here is the code.
public override func isFirstResponder() -> Bool {
// Return true if any of `self`'s subviews is the current first responder.
// Needs to unwrap the IBOutlets otherwise IBInspectable is crashing when using CardTextField because IBOutlets
// are not initialized yet when IBInspectable engine runs.
guard let numberInputTextField = numberInputTextField, let monthTextField = monthTextField, let yearTextField = yearTextField, let cvcTextField = cvcTextField else {
return false
}
return [numberInputTextField, monthTextField, yearTextField, cvcTextField]
.filter({$0.isFirstResponder})
.isEmpty == false
}
Here is an image of the error
Error of IsFirstResponderError
Try to replace this:
public override func isFirstResponder() -> Bool {
with this:
public override var isFirstResponder: Bool {
Apple doc

Cannot assign to immutable expression of type ' String?'

Working with text from a table view here. I am running into issues with the second bit of code.
This works:
var objects:CKRecord!
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
Notes.text = objects.objectForKey("content") as? String
}
Yet this doesn't work:
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
objects.objectForKey("content") as? String = Notes.text // problem line
}
Why not?
Found the solution:
#IBAction func Save(sender: AnyObject) {
objects["content"] = Notes.text as? String
}
Your original attempt does not work because objects.objectForKey("content") as? String is an R-value: both the objects.objectForKey(_:) method and the as? operator return read-only values that you cannot assign to.
Your posted solution works because the [] operator returns an inout value for dictionaries, making objects["content"] an L-value.
See this Wikipedia article for an explanation of L-values and R-values.

Resources