Issue between override and non-override function in swift - xcode

Sorry I'm not very good at explaining this stuff. Basically I have the function below to handle remote control events.
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
override func remoteControlReceivedWithEvent(event: UIEvent) {
if event.type == UIEventType.RemoteControl {
if event.subtype == UIEventSubtype.RemoteControlPlay {
stream.play()
} else if event.subtype == UIEventSubtype.RemoteControlPause {
stream.stop()
} else if event.subtype == UIEventSubtype.RemoteControlTogglePlayPause {
toggle()
}
}
}
Essentially, when I use the term "override" (shown above), I get the error
"Method does not override any method from its superclass.
If I leave out the "override", I get the error:
"Method 'remoteControlReceivedWithEvent' with Objective-C selector 'remoteControlReceivedWithEvent:' conflicts with method "remoteControlReceivedWithEvent" from superclass "UIResponder" with the same Objective-C selector.
I'm pretty new to all of this so I don't really understand what the issue is. Can someone please explain how to remedy this issue? Let me know if you need some more code or something.
Is there more code I have to use to set this up?

There is a mismatch of UIResponder method signature and your function implementation. UIResponder has optional Event as following:
func remoteControlReceibedWithEvent(_ event: UIEvent?)
So it can not override as there is no function with non-optional argument, but if you remove override it will conflict with ObjC implementation, as selector names are the same.

Related

UICollectionViewDelegate in RxCocoa

I write an extension for UICollectionView which will listen the delegate's
shouldHighlightItemAt method,but it don't call.
public var shouldHighlightItem: ControlEvent<IndexPath> {
let source = self.delegate.methodInvoked(#selector(UICollectionViewDelegate.collectionView(_:shouldHighlightItemAt:)))
.map { a in
return try self.castOrThrow(IndexPath.self, a[1])
}
return ControlEvent(events: source)
}
}
how to write an extension for UICollectionView of rx shouldHighlightItemAt?
You cannot use methodInvoked(_:) with a delegate method that has a non void return type.
collectionView(_:shouldHighlightItemAt:) expects you to return a Bool value. So you cannot use methodInvoked(_:).
If you have a look at the implementation of methodInvoked(_:) it gives you an explanation why this does not work:
Delegate methods that have non void return value can't be observed
directly using this method
because:
those methods are not intended to be used as a notification mechanism, but as a behavior customization mechanism
there is no sensible automatic way to determine a default return value
There is however a suggestion how you could achieve what you are trying to do:
In case observing of delegate methods that have return type is
required, it can be done by
manually installing a PublishSubject or BehaviorSubject and implementing delegate method.
In your case it would work like this:
In RxCollectionViewDelegateProxy you add the 'PublishSubject' and implement the UICollectionViewDelegate method:
let shouldHighlightItemAtIndexPathSubject = PublishSubject<IndexPath>
public func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool {
shouldHighlightItemAtIndexPathSubject.on(.next(indexPath))
return self._forwardToDelegate?.collectionView(collectionView, shouldHighlightItemAt: indexPath) ?? true // default value
}
In your UICollectionView RxExtension you can expose the desired Observable like this:
public var property: Observable<IndexPath> {
let proxy = RxCollectionViewDelegateProxy.proxy(for: base)
return proxy.shouldHighlightItemAtIndexPathSubject.asObservable()
}
I have not tested this, I merely took it from the RxCocoa source code and modified it to fit your needs. So in theory this should work, but you might have to tweak it a little bit ;-)

'#selector' refers to a method that is not exposed to Objective-C

The new Xcode 7.3 passing the parameter via addTarget usually works for me but in this case it's throwing the error in the title. Any ideas? It throws another when I try to change it to #objc
Thank you!
cell.commentButton.addTarget(self, action: #selector(FeedViewController.didTapCommentButton(_:)), forControlEvents: UIControlEvents.TouchUpInside)
The selector it's calling
func didTapCommentButton(post: Post) {
}
In my case the function of the selector was private. Once I removed the private the error was gone. Same goes for fileprivate.
In Swift 4
You will need to add #objc to the function declaration. Until swift 4 this was implicitly inferred.
You need to use the #objc attribute on didTapCommentButton(_:) to use it with #selector.
You say you did that but you got another error. My guess is that the new error is that Post is not a type that is compatible with Objective-C. You can only expose a method to Objective-C if all of its argument types, and its return type, are compatible with Objective-C.
You could fix that by making Post a subclass of NSObject, but that's not going to matter, because the argument to didTapCommentButton(_:) will not be a Post anyway. The argument to an action function is the sender of the action, and that sender will be commentButton, which is presumably a UIButton. You should declare didTapCommentButton like this:
#objc func didTapCommentButton(sender: UIButton) {
// ...
}
You'll then face the problem of getting the Post corresponding to the tapped button. There are multiple ways to get it. Here's one.
I gather (since your code says cell.commentButton) that you're setting up a table view (or a collection view). And since your cell has a non-standard property named commentButton, I assume it's a custom UITableViewCell subclass. So let's assume your cell is a PostCell declared like this:
class PostCell: UITableViewCell {
#IBOutlet var commentButton: UIButton?
var post: Post?
// other stuff...
}
Then you can walk up the view hierarchy from the button to find the PostCell, and get the post from it:
#objc func didTapCommentButton(sender: UIButton) {
var ancestor = sender.superview
while ancestor != nil && !(ancestor! is PostCell) {
ancestor = view.superview
}
guard let cell = ancestor as? PostCell,
post = cell.post
else { return }
// Do something with post here
}
Try having the selector point to a wrapper function, which in turn calls your delegate function. That worked for me.
cell.commentButton.addTarget(self, action: #selector(wrapperForDidTapCommentButton(_:)), forControlEvents: UIControlEvents.TouchUpInside)
-
func wrapperForDidTapCommentButton(post: Post) {
FeedViewController.didTapCommentButton(post)
}
As you know selector[About] says that Objective-C runtime[About] should be used. Declarations that are marked as private or fileprivate are not exposed to the Objective-C runtime by default. That is why you have two variants:
Mark your private or fileprivate method declaration by #objc[About]
Use internal, public, open method access modifier[About]

Checking keyDown event.modifierFlags yields error

Im subclassing NSTextView and overriding keyDown. I want to detect command-key-combinations. Command-L, for example.
Apple's documentation indicates that you simply and the modifier flags (in the passed NSEvent) with NSEventModifierFlags.CommandKeyMask.
When I do so:
let ck = NSEventModifierFlags.CommandKeyMask
I receive an odd error:
Binary operator '&' cannot be applied to two 'NSEventModifierFlags' operands.
What's the deal? This is swift 2.0, xcode 7.
Thanks!
Apple's documentation indicates that you simply and the modifier flags
The documentation is still referring to C and Objective-C. Swift uses OptionSetType, which does not use bitwise operators for checking flags.
Instead, use the contains() method to check for one or more flags:
if theEvent.modifierFlags.contains(.CommandKeyMask) {
NSLog("command key down")
}
if theEvent.modifierFlags.contains(.AlternateKeyMask) {
NSLog("option key down")
}
if theEvent.modifierFlags.contains([.CommandKeyMask, .AlternateKeyMask]) {
NSLog("command and option keys down")
}
To check for a single key, use intersect to filter out any unwanted flags, then use == to check for a single flag:
let modifierkeys = theEvent.modifierFlags.intersect(.DeviceIndependentModifierFlagsMask)
if modifierkeys == .CommandKeyMask {
NSLog("Only command key down")
}
NSEventModifierFlags is an optionSet in Swift 2.0. You can use contain method to check it contains the command modifier key
override func keyDown(theEvent:NSEvent) {
if theEvent.characters == "l" && theEvent.modifierFlags.contains(.CommandKeyMask) {
print("command-L pressed")
}
}
I was going to put in a comment, but wasn't able to.
In case someone (like me) comes across this article in the future, Swift has changed a little since 2015.
Swift 4:
theEvent.modifierFlags.contains(.command)
theEvent.modifierFlags.contains(.option)
theEvent.modifierFlags.contains([.command, .option])
NSLog("command and option keys down")
also; (.control) is for CTRL.
This is my particular code:
override func viewDidLoad()
{
super.viewDidLoad()
NSEvent.addLocalMonitorForEvents(matching: NSEvent.EventTypeMask.keyDown, handler: myKeyDownEvent)
}
func myKeyDownEvent(event: NSEvent) -> NSEvent {
if (event.keyCode == 121) && event.modifierFlags.contains([.command, .option]) {
//Do what you want when PGDN + cmd + alt is pressed
}
return event
}

NSView with a KVC property in Swift

I have a custom NSView class defined as:
class MyView: NSView
{
var someText: NSString
override func didChangeValueForKey(key: String)
{
println( key )
super.didChangeValueForKey( key )
}
// other stuff
}
What I want to be able to do is from outside of this class change the value of someText and have didChangeValueForKey notice that someText has changed so I can, for example, set needsDisplay to true for the view and do some other work.
How an I do this?
Are you sure you need KVC for this? KVC works fine in Swift, but there’s an easier way:
var SomeText: NSString {
didSet {
// do some work every time SomeText is set
}
}
There is no KVC mechanism for this because this isn't what KVC is for.
In Objective-C, you would implement the setter explicitly (or override if the property is originally from a superclass) and do your work there.
In Swift, the proper approach is the didSet mechanism.
didChangeValueForKey() is not part of KVC, it's part of KVO (Key-Value Observing). It is not intended to be overridden. It's intended to be called when one is implementing manual change notification (as a pair with willChangeValueForKey()).
More importantly, though, there's no reason to believe that it will be called at all for a property which is not being observed by anything. KVO swizzles the class in order to hook into the setters and other mutating accessors for those properties which are actually being observed. When such a property is changed (and supports automatic change notification), KVO calls willChangeValueForKey() and didChangeValueForKey() automatically. But for non-observed properties, those methods are not called.
Finally, in some cases, such as the indexed collection mutation accessors, KVO will use different change notification methods, such as willChange(_:valuesAtIndexes:forKey:) and didChange(_:valuesAtIndexes:forKey:).
If you really don't want to use didSet for some reason, you would use KVO to observe self for changes in the someText property and handle changes in observeValueForKeyPath(_:ofObject:change:context:). But this is a bad, clumsy, error-prone, inefficient way of doing a simple thing.
KVO and didSet are not mutually exclusive:
import Foundation
class C: NSObject {
dynamic var someText: String = "" {
didSet {
print("changed to \(someText)")
}
}
}
let c = C()
c.someText = "hi" // prints "changed to hi"
class Observer: NSObject {
init(_ c: C) {
super.init()
c.addObserver(self, forKeyPath: "someText", options: [], context: nil)
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
print("observed change to \(object!.valueForKeyPath(keyPath!))")
}
}
let o = Observer(c)
c.someText = "test" // prints "changed to test" and "observed change to test"
I would add to Jaanus's answer that to make the property KVC compliant, you should declare it as dynamic var someText: NSString.
But if you don't need all the bells and whistles oh KVC, didSet is the way to go.
Update
As for didChangeValueForKey: – it is intended for the opposite, for you to notify value for key has changed (if it is not due to one of the cases covered by Foundation). You should use addObserver(_:forKeyPath:options:context:) and override observeValueForKeyPath(_:ofObject:change:context:) to be notified of changes.
Alternatively you can use one of many 3rd party solutions such as ReactiveCococa or Facebook's KVOController

Method 'setChecked' with Objective-C selector 'setChecked:' conflicts with setter for 'checked' with the same Objective-C selector

I have created custom class that inherits from UIButton.
In that class I have created a function defined as:
func setChecked(checked:Bool){
self.checked = checked
if checked {
buttonImageView.image = UIImage(named: "radioSelected.png")
} else {
buttonImageView.image = UIImage(named: "radioUnselected.png")
}
}
which was working fine until I updated my xCode to 6.1.3.
Now I keep getting the error message on the function definition line:
Method 'setChecked' with Objective-C selector 'setChecked:' conflicts
with setter for 'checked' with the same Objective-C selector
I already tried to make a override it but then I get a error saying that "Method does not override any method from its superclass".
Anyone know how can I correctly solve it?
(I don't want to change the name of my function.)
You have function name conflicting with your property. What about to implement it in a more elegant way with property observing? This explicitly shows how value changes, as well as a side effects for the value changes.
class RadioButton: UIButtom {
var checked: Bool = false {
didSet {
buttonImageView.image = UIImage(named: checked ? "radioSelected.png" : "radioUnselected.png")
}
}
}
It seems that there is a name collision happening. There a few more people with the same issue:
Compiler error: Method with Objective-C selector conflicts with previous declaration with the same Objective-C selector
https://stackoverflow.com/questions/30006724/method-setplayer-with-objective-c-selector-setplayer-conflicts-with-setter
Try renaming your function to something like:
func checkedSetter(checked:Bool){

Resources