Use delegate method written in Objective-C in Swift code - objective-c

I'd like to use a delegate method written in Objective-C in Swift. The method is included in the MGSwipeTableCell framework (MGSwipeTableCell.h).
Objective-C:
-(BOOL) swipeTableCell:(MGSwipeTableCell*) cell tappedButtonAtIndex:(NSInteger) index direction:(MGSwipeDirection)direction fromExpansion:(BOOL) fromExpansion;
I try to convert it into Swift to and use the method:
func swipeTableCell(cell:MGSwipeTableCell, index:Int, direction:MGSwipeDirection, fromExpansion:Bool) -> Bool {
return true
}
But I don't know why but the function isn't getting called. Did I something wrong? I just want to get the indexPath of the swiped cell with this function.

You should implement MGSwipeTableCellDelegate protocol in your table view controller first. So you can just write:
class TableViewController : UITableViewController, MGSwipeTableCellDelegate {
....
....
....
func swipeTableCell(cell:MGSwipeTableCell, index:Int, direction:MGSwipeDirection, fromExpansion:Bool) -> Bool {
return true
}
}
and then when creating cells in cellForRowAtIndexPath: method you should create it like this:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let reuseIdentifier = "cell"
var cell = self.table.dequeueReusableCellWithIdentifier(reuseIdentifier) as! MGSwipeTableCell!
if cell == nil {
cell = MGSwipeTableCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: reuseIdentifier)
}
cell.delegate = self
return cell
}
Then you'll be able to track when swipe method is called because you set the cell delegate property.

It appears that the library you're trying to use has not adopted Objective-C nullability annotations yet, as such, any return values or arguments which are objects will be translated into Swift as implicitly unwrapped optionals (with the exclamation mark).
So, the signature you're looking for is this:
func swipeTableCell(cell: MGSwipeTableCell!, tappedButtonAtIndex: Int, direction: MGSwipeDirection, fromExpansion: Bool) -> Bool
But with that said, you need to add the protocol conformance anyway. If you do that first then try to write this method out, it should autocomplete to exactly how Swift expects it to look.
And then just make sure you're actually setting the cell's delegate property to whatever object is implement this method.

Related

How to use RACObserve in swift

this new existing project is using Objective-C Code with RACObserve(Reactive cocoa ) to read api responses.
I want to convert obj-c to swift .
Current Obj-c Implementation is :
#interface ObjCTableViewCell : UITableViewCell
#property (nonatomic, strong) OfferPersonal *offer;
#end
.m class->
- (void)bindToModel {
[RACObserve(self, offer.lender.name) subscribeNext:^(id x) {
self.nameLabel.text = x;
}];
}
Controller ->
{
ObjCTableViewCell TableViewCell *myCell;
myCell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
[myCell setValue:self.offer forKey:#"offerPersonal"];
[myCell bindToModel];
}
Now how to use RACObserve in Swift. i tried to search it over other places.
(http://blog.scottlogic.com/2014/07/24/mvvm-reactivecocoa-swift.html)could not understand exactly .
Is the new project using ReactiveSwift with everything (model, cell, view controller) being rewritten in Swift? If not, you can use ReactiveObjcBridge to call the old ReactiveCocoa ObjC library from Swift.
If yes, you should use replace the name String property with a MutableProperty<String>, and you can bind to the MutableProperty - two options are:
a: the bind operator <~
nameLabel.reactive.text <~ offer.lender.name
or
b: by accessing its signal
offer.lender.name.signal.observeValues { (value: String) in
nameLabel.text = value
}
Also, depending on how many times the property is updated, you might want to limit the observation to only one value, like .signal.take(first: 1).observeValues { ... } or signal.take(until: reactive.prepareForReuse)

What is the swift equivalent to setting properties on `id`?

I wonder what's the Swift equivalent in calling a method on id in which the availability of the method is determined at runtime. Specifically I'm looking to do this pattern in Swift:
-(IBAction) handleEvent:(id) sender {
BOOL didDisable = NO;
if([sender respondsToSelector:#selector(setEnabled:)]) {
[sender setEnabled:NO];
didDisable = YES;
}
[self doSomethingAsyncWithCompletionHandler:^{
if(didDisable) {
[sender setEnabled:YES];
}
}];
}
The biggest problem is that setEnabled: is imported in Swift as a property (e.g. UIBarItem) and none of the following constructs compile
func handleEvent(sender: AnyObject) {
// Error: AnyObject does not have a member named "enabled"
sender.enabled? = false
// Error: (BooleanLiteralCompatible) -> _ is not identical to Bool
sender.setEnabled?(false)
}
You can in fact do it exactly the same way you were doing it before: by calling respondsToSelector:. Indeed, that is exactly what your proposed expression does:
sender.setEnabled?(false)
That expression is actually a shorthand - it calls respondsToSelector: first, and then calls setEnabled: only if the respondsToSelector: test passes. Unfortunately, as you say, you can't get that code to compile. That, however, is merely a quirk of Swift's known repertory of available methods. The fact is that, although it is a little tricky to get it to compile, it can be done - and once you get it to compile, it behaves just as you would expect.
However, I'm not going to explain how to make it compile, because I don't want to encourage this kind of trickery. This sort of dynamic messaging is discouraged in Swift. In general, dynamic messaging tricks such as key-value coding, introspection, and so forth are not needed in Swift and are not consonant with Swift's strong typing approach. It would be better to do things the Swift way, by casting optionally to something that you have reason to believe this thing might be and that has an enabled property. For example:
#IBAction func doButton(sender: AnyObject) {
switch sender {
case let c as UIControl: c.enabled = false
case let b as UIBarItem: b.enabled = false
default:break
}
}
Or:
#IBAction func doButton(sender: AnyObject) {
(sender as? UIControl)?.enabled = false
(sender as? UIBarItem)?.enabled = false
}
In Swift 2.0 beta 4, your prayers are answered; this code becomes legal:
#IBAction
func handleEvent(sender: AnyObject) {
if sender.respondsToSelector("setHidden:") {
sender.performSelector("setHidden:", withObject: true)
}
}
If you want to avoid using the respondsToSelector: method you could define a protocol instead. Then extend the classes you want to use that is already in conformance with this protocol's definition (enabled) and define the function with a generic variable conforming to your protocol.
protocol Enablable{
var enabled:Bool { get set }
}
extension UIButton : Enablable {}
extension UIBarButtonItem : Enablable {}
//....
func handleEvent<T:Enablable>(var sender: T) {
sender.enabled = false
}
If you need to use it with an IBAction method a little bit of a work around is required since you cannot use generics directly on them.
#IBAction func handleEventPressed(sender:AnyObject){
handleEvent(sender);
}
We also need a matching generic function without Enablable conformance so that we can call handleEvent without knowing wether or not sender is Enablable. Luckily the compiler is smart enough to figure out which of the two generic functions to use.
func handleEvent<T>(var sender: T) {
//Do Nothing case if T does not conform to Enablable
}
As a workaround/alternative, you can use Key-Value Coding:
#IBAction func handler(sender: AnyObject) {
if sender.respondsToSelector("setEnabled:") {
sender.setValue(false, forKey:"enabled")
}
}
This works with both Swift 1.2 (Xcode 6.4) and Swift 2.0 (Xcode 7 beta).

How to check if a view controller can perform a segue

This might be a very simple question but didn't yield any results when searching for it so here it is...
I am trying to work out a way to check if a certain view controller can perform a segue with identifier XYZ before calling the performSegueWithIdentifier: method.
Something along the lines of:
if ([self canPerformSegueWithIdentifier:#"SegueID"])
[self performSegueWithIdentifier:#"SegueID"];
Possible?
To check whether the segue existed or not, I simply surrounded the call with a try-and-catch block. Please see the code example below:
#try {
[self performSegueWithIdentifier:[dictionary valueForKey:#"segue"] sender:self];
}
#catch (NSException *exception) {
NSLog(#"Segue not found: %#", exception);
}
Hope this helps.
- (BOOL)canPerformSegueWithIdentifier:(NSString *)identifier
{
NSArray *segueTemplates = [self valueForKey:#"storyboardSegueTemplates"];
NSArray *filteredArray = [segueTemplates filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"identifier = %#", identifier]];
return filteredArray.count>0;
}
This post has been updated for Swift 4.
Here is a more correct Swift way to check if a segue exists:
extension UIViewController {
func canPerformSegue(withIdentifier id: String) -> Bool {
guard let segues = self.value(forKey: "storyboardSegueTemplates") as? [NSObject] else { return false }
return segues.first { $0.value(forKey: "identifier") as? String == id } != nil
}
/// Performs segue with passed identifier, if self can perform it.
func performSegueIfPossible(id: String?, sender: AnyObject? = nil) {
guard let id = id, canPerformSegue(withIdentifier: id) else { return }
self.performSegue(withIdentifier: id, sender: sender)
}
}
// 1
if canPerformSegue("test") {
performSegueIfPossible(id: "test") // or with sender: , sender: ...)
}
// 2
performSegueIfPossible(id: "test") // or with sender: , sender: ...)
As stated in the documentation:
Apps normally do not need to trigger segues directly.
Instead, you configure an object in Interface Builder associated with
the view controller, such as a control embedded in its view hierarchy,
to trigger the segue. However, you can call this method to trigger a
segue programmatically, perhaps in response to some action that cannot
be specified in the storyboard resource file. For example, you might
call it from a custom action handler used to process shake or
accelerometer events.
The view controller that receives this message must have been loaded
from a storyboard. If the view controller does not have an associated
storyboard, perhaps because you allocated and initialized it yourself,
this method throws an exception.
That being said, when you trigger the segue, normally it's because it's assumed that the UIViewController will be able to respond to it with a specific segue's identifier. I also agree with Dan F, you should try to avoid situations where an exception could be thrown. As the reason for you not to be able to do something like this:
if ([self canPerformSegueWithIdentifier:#"SegueID"])
[self performSegueWithIdentifier:#"SegueID"];
I am guessing that:
respondsToSelector: only checks if you are able to handle that message in runtime. In this case you can, because the class UIViewController is able to respond to performSegueWithIdentifier:sender:. To actually check if a method is able to handle a message with certain parameters, I guess it would be impossible, because in order to determine if it's possible it has to actually run it and when doing that the NSInvalidArgumentException will rise.
To actually create what you suggested, it would be helpful to receive a list of segue's id that the UIViewController is associated with. From the UIViewController documentation, I wasn't able to find anything that looks like that
As for now, I am guessing your best bet it's to keep going with the #try #catch #finally.
You can override the -(BOOL)shouldPerformSegueWithIdentifier:sender: method and do your logic there.
- (BOOL) shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if ([identifier isEqualToString:#"someSegue"]) {
if (!canIPerformSegue) {
return NO;
}
}
return YES;
}
Hope this helps.
Reference CanPerformSegue.swift
import UIKit
extension UIViewController{
func canPerformSegue(identifier: String) -> Bool {
guard let identifiers = value(forKey: "storyboardSegueTemplates") as? [NSObject] else {
return false
}
let canPerform = identifiers.contains { (object) -> Bool in
if let id = object.value(forKey: "_identifier") as? String {
return id == identifier
}else{
return false
}
}
return canPerform
}
}
Swift version of Evgeny Mikhaylov's answer, which worked for me:
I reuse a controller for two views. This helps me reuse code.
if(canPerformSegueWithIdentifier("segueFoo")) {
self.performSegueWithIdentifier("segueFoo", sender: nil)
}
else {
self.performSegueWithIdentifier("segueBar", sender: nil)
}
func canPerformSegueWithIdentifier(identifier: NSString) -> Bool {
let templates:NSArray = self.valueForKey("storyboardSegueTemplates") as! NSArray
let predicate:NSPredicate = NSPredicate(format: "identifier=%#", identifier)
let filteredtemplates = templates.filteredArrayUsingPredicate(predicate)
return (filteredtemplates.count>0)
}
It will be useful, before call performSegue, check native storyboard property on base UIViewController (for example screen was from StoryBoard or Manual Instance)
extension UIViewController {
func performSegueWithValidate(withIdentifier identifier: String, sender: Any?) {
if storyboard != nil {
performSegue(withIdentifier: identifier, sender: sender)
}
}
}
enter image description here
There is no way to check that using the standard functions, what you can do is subclass UIStoryboardSegue and store the information in the source view controller (which is passed to the constructor). In interface builder select "Custom" as the segue type as type the class name of your segue, then your constructor will be called for every segue instantiated and you can query the stored data if it exists.
You must also override the perform method to call [source presentModalViewController:destination animated:YES] or [source pushViewController:destination animated:YES] depending on what transition type you want.

NSTokenFieldCell in NSTableView crashes when tabbing, is it a bug?

I was having trouble with NSTokenFieldCell, so I proceeded to create a new project in Xcode to isolate the problem. Here is what I did:
Dropped a NSTableView into the main window;
selected the second column's text cell, and changed it's Class (via Identity Inspector) to NSTokenFieldCell;
implemented a minimum possible data source object, with the following code:
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return 1;
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
return #"aa, bb";
}
At first it seems to work fine, but if you double-click a cell to edit, then tab and shift+tab to switch cells back and forth, eventually the application crashes with a BAD ACCESS when the token field cell receive focus.
I'm using Xcode 4.2 in Lion 10.7.2, with all the default settings that come with a Mac OS X Cocoa Application template.
Looks like a bug in Cocoa. If you turn on zombies you'll see this:
2011-10-31 00:02:43.802 tokenfieldtest[35622:307] *** -[NSTokenFieldCell respondsToSelector:]: message sent to deallocated instance 0x1da761f10
I tried setting a delegate for the table and implementing - (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row, returning a new NSTokenFieldCell every time (for just the token column), but I got the same error.
The original solution brought a new problem.
When NSTokenFieldCell is not fully displayed in NSTableView, entering the editing state and then exiting will cause the table view to display abnormally.
So I tried repeatedly to get a better solution:
class MyTokenFieldCell: NSTokenFieldCell {
override func fieldEditor(for controlView: NSView) -> NSTextView? {
return nil;
}
}
It may be that NSTableView's editor reuse mechanism for NSTokenFieldCell has problems, which caused the program to crash.
fieldEditorForView: is overwritten here, returning nil, which should cause the editor to be recreated every time when editing, avoiding reuse, and thus solves the crash problem.
The following is the original answer.
⚠️ Because the solution causes other problems, please ignore it.
I also encountered this problem. My solution is to temporarily keep the cells used by the table view.
Custom NSTokenFieldCell: after each copy, temporarily save the copy.
class MyTokenFieldCell: NSTokenFieldCell {
static var cells = [NSUserInterfaceItemIdentifier: [MyTokenFieldCell]]()
override func copy(with zone: NSZone? = nil) -> Any {
let cell = super.copy(with: zone)
guard let tokenFieldCell = cell as? MyTokenFieldCell else { return cell }
tokenFieldCell.identifier = self.identifier
guard let identifier = tokenFieldCell.identifier else { return cell }
var cells = MyTokenFieldCell.cells[identifier] ?? []
cells.append(tokenFieldCell)
if cells.count > 4 {
cells.removeFirst()
}
MyTokenFieldCell.cells[identifier] = cells
return cell
}
}
Implement the tableView(_:dataCellFor:row:) method of NSTableViewDelegate, provide MyTokenFieldCell for the table view, and set the identifier to: <columnIdentifier>:<row>
extension ViewController: NSTableViewDelegate {
func tableView(_ tableView: NSTableView, dataCellFor tableColumn: NSTableColumn?, row: Int) -> NSCell? {
guard let columnIdentifier = tableColumn?.identifier, columnIdentifier.rawValue == "token" else {
return tableColumn?.dataCell(forRow: row) as? NSCell
}
let cell = MyTokenFieldCell()
cell.isEditable = true
cell.identifier = .init("\(columnIdentifier.rawValue):\(row)")
return cell
}
}

How to check if a UIViewController is of a particular sub-class in objective c?

I want to be able to check the type of a UIViewController to see if it is of a certain type like this
c code
if (typeof(instance1) == customUIViewController)
{
customUIViewController test = (customViewController)instance1;
// do more stuff
}
The isKindOfClass: method indicates whether an object is an instance of given class or an instance of a subclass of that class.
if ([instance1 isKindOfClass:[CustomUIViewController class]]) {
// code
}
If you want to check whether an object is an instance of a given class (but not an instance of a subclass of that class), use isMemberOfClass: instead.
var someVC: UIViewController
if someVC is MyCustomVC {
//code
}
Swift version:
var someVC: UIViewController
if someVC.isKindOfClass(MyCustomVC) {
//code
}
Try:
[vc isKindOfClass:[CustomViewController class]];
I just wanted to add in addition to this answer that if you're wanting to see if a view controller is of a certain type in a switch statement (in Swift) you can do it like this:
var someVC: UIViewController?
switch someVC {
case is ViewController01: break
case is ViewController02: break
case is ViewController03: break
default: break
}
Swift 3.0
in latest, we have to add a self along with the class name
or it will throw an error "Expected member name or constructor call after type name"
the below code u can use for Swift 3 and above
for viewController in viewControllers {
if viewController.isKind(of: OurViewController.self){
print("yes it is OurViewController")
self.navigationController?.popToViewController(viewController, animated: true)
}
}