UITextFieldDelegate textFieldShouldReturn with ReactiveCocoa - ios

I am trying to implement UITextFieldDelegate textFieldShouldReturn handling with ReactiveCocoa. Unfortunately the subscribeNext block is run when I subscribe for the signal.
The implementation using delegation would be:
- (void)viewDidLoad
{
...
self.myTextField.delegate = self;
}
...
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if (textField == self.myTextField) {
NSLog(#"Let's go!");
}
return YES;
}
In ReactiveCocoa I have added a category for UITextField in a similar fashion like UITextView+RACSignalSupport.
#implementation UITextField (RACKeyboardSupport)
static void RACUseDelegateProxy(UITextField *self)
{
if (self.delegate == self.rac_delegateProxy) return;
self.rac_delegateProxy.rac_proxiedDelegate = self.delegate;
self.delegate = (id)self.rac_delegateProxy;
}
- (RACDelegateProxy *)rac_delegateProxy
{
RACDelegateProxy *proxy = objc_getAssociatedObject(self, _cmd);
if (proxy == nil) {
proxy = [[RACDelegateProxy alloc] initWithProtocol:#protocol(UITextFieldDelegate)];
objc_setAssociatedObject(self, _cmd, proxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return proxy;
}
- (RACSignal *)rac_keyboardReturnSignal
{
#weakify(self);
RACSignal *signal = [[[[RACSignal
defer:^{
#strongify(self);
return [RACSignal return:RACTuplePack(self)];
}]
concat:[self.rac_delegateProxy signalForSelector:#selector(textFieldShouldReturn:)]]
takeUntil:self.rac_willDeallocSignal]
setNameWithFormat:#"%# -rac_keyboardReturnSignal", [self rac_description]];
RACUseDelegateProxy(self);
return signal;
}
#end
Here subscribeNext block is executed even if the Return Key was never pressed:
- (void)viewDidLoad
{
...
[self.myTextField.rac_keyboardReturnSignal subscribeNext:^(id x) {
Log(#"Let's go with RAC!");
}];
}
I have to use skip:1 to avoid that problem:
- (void)viewDidLoad
{
...
[[self.myTextField.rac_keyboardReturnSignal skip:1] subscribeNext:^(id x) {
Log(#"Let's go with RAC!");
}];
}
Any idea why this happens?
Solution:
- (RACSignal *)rac_keyboardReturnSignal
{
RACSignal *signal = [[[self.rac_delegateProxy
signalForSelector:#selector(textFieldShouldReturn:)]
takeUntil:self.rac_willDeallocSignal]
setNameWithFormat:#"%# -rac_keyboardReturnSignal", [self rac_description]];
RACUseDelegateProxy(self);
return signal;
}

You are returning a signal that immediately returns a value in your defer block, then concat-ing new values onto the stream when textFieldShouldReturn is invoked.
The code in UITextView+RACSignalSupport.m is calling reduceEach in order to return a string value that is extracted from the UITextView instance. The defer is used to merely have an initial value generated upon subscription.
Basically, I don't think you want the defer at all for your use case.

Related

object of NSOperation doesn't removed from NSOperationQueue after executing

In my subclass of NSOperation I set 4 flags, and when an operation finishes its execution it is not removed to NSOperation queue, where it was added at the beginning, this thing cause a lot of issues in my app.
I suppose that the way I set these flags is not correct, could you please help with it. cause I really spend a lot of time on identifying this issue.
#property(assign, nonatomic) BOOL isCancelled;
#property(nonatomic, getter=isExecuting) BOOL executing;
#property(nonatomic, getter=isFinished) BOOL finished;
#property(readonly, getter=isAsynchronous) BOOL asynchronous;
//in initialisation
- (id)initWithURL:(NSURL*)url andRaw:(NSInteger)row
{
if (![super init])
return nil;
[self setTargetURL:url];
return self;
}
//the way I override KVO
- (BOOL)isExecuting
{
NSLog(#"Exec");
return (self.defaultSession != nil);//it doesn't work
}
- (BOOL)isFinished
{
NSLog(#"Finished");
return (self.defaultSession == nil); //it doesn't work, so I explicitly set the value
}
- (BOOL)isAsynchronous
{
return YES;
}
- (void)cancel
{
[super cancel];
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
self.isExecuting = NO;
self.isFinished = YES;
[self didChangeValueForKey:#"isFinished"];
[self didChangeValueForKey:#"isExecuting"];
if(self.downloadTask.state == NSURLSessionTaskStateRunning)
[self.downloadTask cancel];
[self finish];
}
- (void)finish
{
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
self.defaultSession = nil; //NSURLSession
self.isFinished = YES;
[self didChangeValueForKey:#"isFinished"];
[self didChangeValueForKey:#"isExecuting"];
}
Thank you in advance
EDIT:
finally I found the issue - it was NSURLSession inside the queue. It kept strong reference to the queue and didn't allow it to be deallocated and removed from NSOperationQueue.
I have done the exact same thing, albiet in Swift.
There are a couple of aspects that I have implemented differently and are listed below:
I have noticed that we do not have to override cancel() method in the
Asynchronous operation. The default behavior of NSOperation's cancel
method is to set the self.cancelled boolean to true.
self.executing should be set to true only inside the overriden start() method of operation, but not in the init ( Not sure if this wil cause any issues). Also before setting the self.executing = true in the start() method, I ensure that the boolean self.cancelled is false. If self.cancelled = true, we should set self.finished = true.
Also , I have created a "didSet" property observer for the isExecuting and isFinished properties where I call the willChangeValueForKey and didChangeValueForKey. I am not 100% sure how to replicate the didSet behavior in Obj C. ( This says to overrride setter)
Please refer to the Ray Wenderlich video tutorials about "Concurrency" where Sam DAvies explains the creation of NSoperation subclass for Asynchronous operation. Please note, that it is only for subscribers and it is explained in Swift. I believe if you fix points 1 and 2, you should see your issues being fixed.

No visible interface for selector - init method

That may sound odd, but please consider following:
-(instancetype)initWithModel:(FRPPhotoModel *)photoModel{
self = [super initWithModel:photoModel];
if (!self) return nil;
// a lot of code here
return self;
}
Actually it underline as error following line:
self = [super initWithModel:photoModel];
Error is: No visible #interface for 'RVMViewModel' declares the selector 'initWithModel:'
It actually reference to same method it declared is. Of course, superclass does not know about it. But, in a book im reading following work fine (that is the same method:
-(instancetype)initWithModel:(FRPPhotoModel *)photoModel {
self = [super initWithModel:photoModel];
if (!self) return nil;
#weakify(self);
[self.didBecomeActiveSignal subscribeNext:^(id x) {
#strongify(self);
[self downloadPhotoModelDetails];
}];
RAC(self, photoImage) = [RACObserve(self.model, fullsizedData) map:^id(id value) {
return [UIImage imageWithData:value];
}];
return self;
}
It work perfectly fine. How to fix that?

Calling of methods in ios

I am very new to ios and having a problem to call four different methods one after the other, as I would like to call them after comparison like if(number<6)
method2->method3->method4->method1-> such that it would make a loop until the comparison of (number)become false.
Also I want method1 to be activated in viewDidLoad.
Something like this, if I understood your question properly
[[[method2 method3] method4] method1]
You can use
[yourObject performSelector:#selector(method1:)]
You can call it on any NSObject (and subclasses of course). You may want to try before :
[yourObject respondToSelector:#selector(method1:)]
This will tell you if your object has this method or not (avoid exceptions and errors).
More informations on the official documentation of performSelector.
use [self method1]; somewhere else and use as follows:
-(void) method1
{
[self method2];
}
-(void) method2
{
[self method3];
}
-(void) method3
{
[self method4];
}
-(void) method4
{
// do if you need.
}
Or Try NSNotificationCenter
I hope i'm interpreting your question correctly. But here's a simple implementation...
- (void)viewDidLoad {
[super viewDidLoad];
[self method1];
}
- (void)method1 {
/* do stuff */
[self method2];
}
- (void)method2 {
/* do stuff */
[self method3];
}
- (void)method3 {
/* do stuff */
[self method4];
}
- (void)method4 {
/* do stuff */
}
-(void) method1
{
if (number<6)
{
[self method2];
}
}
-(void) method2
{
[self method3];
}
-(void) method3
{
[self method4];
}
-(void) method4
{
[self method1];
}

Call the my method some amount on cocos2d

I have a method that draws a one sprite on the screen with the animation effects,
if I call in init this method
if( (self=[super init]) ) {
// ....
[self myMethod];
// .....
}
Then he does it once on my project
When I call by schedule
-(void)schedulMyMethod:(ccTime)dt {
[self myMethod];
}
if( (self=[super init]) ) {
// ....
[self schedule:#selector(schedulMyMethod:) interval:0.5];
// .....
}
It runs for an unlimited times
I need so that I can call the my method some amount
You mean, you want to have it repeat N times? You'll need to keep state on times remaining for it to run.
#property (nonatomic, assign) NSInteger timesToRunMyMethod;
- (void)beginRunningMyMethod {
self.timesToRunMyMethod = 100; // N==100
[self myMethod];
}
- (void)myMethod {
self.timesToRunMyMethod--;
// do stuff
if (self.timesToRunMyMethod > 0) {
// i used native delayed execution, you can replace it with whatever cocos2d offers if you want
[self performSelector:#selector(myMethod) withObject:nil afterDelay:0.5];
}
}
And it's probably wrong to start this on init. Is it a view controller? Then you can use viewDidAppear or willAppear.

Intercept Objective-C delegate messages within a subclass

I have a subclass of UIScrollView in which I need to internally respond to scrolling behaviour. However, the viewcontroller will still need to listen to scrolling delegate callbacks, so I can't outright steal the delegate within my component.
Is there a way to keep the property named "delegate" and just listen to messages sent along it, or else somehow internally hijack the delegate property and forward messages outward after running some code?
To avoid overriding all of the delegate methods manually, you can use message forwarding. I just implemented the same thing using an intermediate proxy class as follows:
MessageInterceptor.h
#interface MessageInterceptor : NSObject {
id receiver;
id middleMan;
}
#property (nonatomic, assign) id receiver;
#property (nonatomic, assign) id middleMan;
#end
MessageInterceptor.m
#implementation MessageInterceptor
#synthesize receiver;
#synthesize middleMan;
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([middleMan respondsToSelector:aSelector]) { return middleMan; }
if ([receiver respondsToSelector:aSelector]) { return receiver; }
return [super forwardingTargetForSelector:aSelector];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
if ([middleMan respondsToSelector:aSelector]) { return YES; }
if ([receiver respondsToSelector:aSelector]) { return YES; }
return [super respondsToSelector:aSelector];
}
#end
MyScrollView.h
#import "MessageInterceptor.h"
#interface MyScrollView : UIScrollView {
MessageInterceptor * delegate_interceptor;
//...
}
//...
#end
MyScrollView.m (Edited, with thanks to jhabbott):
#implementation MyScrollView
- (id)delegate { return delegate_interceptor.receiver; }
- (void)setDelegate:(id)newDelegate {
[super setDelegate:nil];
[delegate_interceptor setReceiver:newDelegate];
[super setDelegate:(id)delegate_interceptor];
}
- (id)init* {
//...
delegate_interceptor = [[MessageInterceptor alloc] init];
[delegate_interceptor setMiddleMan:self];
[super setDelegate:(id)delegate_interceptor];
//...
}
- (void)dealloc {
//...
[delegate_interceptor release];
//...
}
// delegate method override:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// 1. your custom code goes here
// 2. forward to the delegate as usual
if ([self.delegate respondsToSelector:#selector(scrollViewDidScroll:)]) {
[self.delegate scrollViewDidScroll:scrollView];
}
}
#end
With this approach, the MessageInterceptor object will automatically forward all delegate messages to the regular delegate object, except for the ones that you override in your custom subclass.
The post from e.James gave an excellent solution for most views. But for keyboard dependent views like UITextField and UITextView, it often results in a situation of infinite loop. To get rid of it, I fixed it with some additional code what checks whether the selector is contained in specific protocol(s) or not.
WZProtocolInterceptor.h
#import <Foundation/Foundation.h>
#interface WZProtocolInterceptor : NSObject
#property (nonatomic, readonly, copy) NSArray * interceptedProtocols;
#property (nonatomic, weak) id receiver;
#property (nonatomic, weak) id middleMan;
- (instancetype)initWithInterceptedProtocol:(Protocol *)interceptedProtocol;
- (instancetype)initWithInterceptedProtocols:(Protocol *)firstInterceptedProtocol, ... NS_REQUIRES_NIL_TERMINATION;
- (instancetype)initWithArrayOfInterceptedProtocols:(NSArray *)arrayOfInterceptedProtocols;
#end
WZProtocolInterceptor.m
#import <objc/runtime.h>
#import "WZProtocolInterceptor.h"
static inline BOOL selector_belongsToProtocol(SEL selector, Protocol * protocol);
#implementation WZProtocolInterceptor
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if ([self.middleMan respondsToSelector:aSelector] &&
[self isSelectorContainedInInterceptedProtocols:aSelector])
return self.middleMan;
if ([self.receiver respondsToSelector:aSelector])
return self.receiver;
return [super forwardingTargetForSelector:aSelector];
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
if ([self.middleMan respondsToSelector:aSelector] &&
[self isSelectorContainedInInterceptedProtocols:aSelector])
return YES;
if ([self.receiver respondsToSelector:aSelector])
return YES;
return [super respondsToSelector:aSelector];
}
- (instancetype)initWithInterceptedProtocol:(Protocol *)interceptedProtocol
{
self = [super init];
if (self) {
_interceptedProtocols = #[interceptedProtocol];
}
return self;
}
- (instancetype)initWithInterceptedProtocols:(Protocol *)firstInterceptedProtocol, ...;
{
self = [super init];
if (self) {
NSMutableArray * mutableProtocols = [NSMutableArray array];
Protocol * eachInterceptedProtocol;
va_list argumentList;
if (firstInterceptedProtocol)
{
[mutableProtocols addObject:firstInterceptedProtocol];
va_start(argumentList, firstInterceptedProtocol);
while ((eachInterceptedProtocol = va_arg(argumentList, id))) {
[mutableProtocols addObject:eachInterceptedProtocol];
}
va_end(argumentList);
}
_interceptedProtocols = [mutableProtocols copy];
}
return self;
}
- (instancetype)initWithArrayOfInterceptedProtocols:(NSArray *)arrayOfInterceptedProtocols
{
self = [super init];
if (self) {
_interceptedProtocols = [arrayOfInterceptedProtocols copy];
}
return self;
}
- (void)dealloc
{
_interceptedProtocols = nil;
}
- (BOOL)isSelectorContainedInInterceptedProtocols:(SEL)aSelector
{
__block BOOL isSelectorContainedInInterceptedProtocols = NO;
[self.interceptedProtocols enumerateObjectsUsingBlock:^(Protocol * protocol, NSUInteger idx, BOOL *stop) {
isSelectorContainedInInterceptedProtocols = selector_belongsToProtocol(aSelector, protocol);
* stop = isSelectorContainedInInterceptedProtocols;
}];
return isSelectorContainedInInterceptedProtocols;
}
#end
BOOL selector_belongsToProtocol(SEL selector, Protocol * protocol)
{
// Reference: https://gist.github.com/numist/3838169
for (int optionbits = 0; optionbits < (1 << 2); optionbits++) {
BOOL required = optionbits & 1;
BOOL instance = !(optionbits & (1 << 1));
struct objc_method_description hasMethod = protocol_getMethodDescription(protocol, selector, required, instance);
if (hasMethod.name || hasMethod.types) {
return YES;
}
}
return NO;
}
And here is the Swift 2 version:
//
// NSProtocolInterpreter.swift
// Nest
//
// Created by Manfred Lau on 11/28/14.
// Copyright (c) 2014 WeZZard. All rights reserved.
//
import Foundation
/**
`NSProtocolInterceptor` is a proxy which intercepts messages to the middle man
which originally intended to send to the receiver.
- Discussion: `NSProtocolInterceptor` is a class cluster which dynamically
subclasses itself to conform to the intercepted protocols at the runtime.
*/
public final class NSProtocolInterceptor: NSObject {
/// Returns the intercepted protocols
public var interceptedProtocols: [Protocol] { return _interceptedProtocols }
private var _interceptedProtocols: [Protocol] = []
/// The receiver receives messages
public weak var receiver: NSObjectProtocol?
/// The middle man intercepts messages
public weak var middleMan: NSObjectProtocol?
private func doesSelectorBelongToAnyInterceptedProtocol(
aSelector: Selector) -> Bool
{
for aProtocol in _interceptedProtocols
where sel_belongsToProtocol(aSelector, aProtocol)
{
return true
}
return false
}
/// Returns the object to which unrecognized messages should first be
/// directed.
public override func forwardingTargetForSelector(aSelector: Selector)
-> AnyObject?
{
if middleMan?.respondsToSelector(aSelector) == true &&
doesSelectorBelongToAnyInterceptedProtocol(aSelector)
{
return middleMan
}
if receiver?.respondsToSelector(aSelector) == true {
return receiver
}
return super.forwardingTargetForSelector(aSelector)
}
/// Returns a Boolean value that indicates whether the receiver implements
/// or inherits a method that can respond to a specified message.
public override func respondsToSelector(aSelector: Selector) -> Bool {
if middleMan?.respondsToSelector(aSelector) == true &&
doesSelectorBelongToAnyInterceptedProtocol(aSelector)
{
return true
}
if receiver?.respondsToSelector(aSelector) == true {
return true
}
return super.respondsToSelector(aSelector)
}
/**
Create a protocol interceptor which intercepts a single Objecitve-C
protocol.
- Parameter protocols: An Objective-C protocol, such as
UITableViewDelegate.self.
*/
public class func forProtocol(aProtocol: Protocol)
-> NSProtocolInterceptor
{
return forProtocols([aProtocol])
}
/**
Create a protocol interceptor which intercepts a variable-length sort of
Objecitve-C protocols.
- Parameter protocols: A variable length sort of Objective-C protocol,
such as UITableViewDelegate.self.
*/
public class func forProtocols(protocols: Protocol ...)
-> NSProtocolInterceptor
{
return forProtocols(protocols)
}
/**
Create a protocol interceptor which intercepts an array of Objecitve-C
protocols.
- Parameter protocols: An array of Objective-C protocols, such as
[UITableViewDelegate.self].
*/
public class func forProtocols(protocols: [Protocol])
-> NSProtocolInterceptor
{
let protocolNames = protocols.map { NSStringFromProtocol($0) }
let sortedProtocolNames = protocolNames.sort()
let concatenatedName = sortedProtocolNames.joinWithSeparator(",")
let theConcreteClass = concreteClassWithProtocols(protocols,
concatenatedName: concatenatedName,
salt: nil)
let protocolInterceptor = theConcreteClass.init()
as! NSProtocolInterceptor
protocolInterceptor._interceptedProtocols = protocols
return protocolInterceptor
}
/**
Return a subclass of `NSProtocolInterceptor` which conforms to specified
protocols.
- Parameter protocols: An array of Objective-C protocols. The
subclass returned from this function will conform to these protocols.
- Parameter concatenatedName: A string which came from concatenating
names of `protocols`.
- Parameter salt: A UInt number appended to the class name
which used for distinguishing the class name itself from the duplicated.
- Discussion: The return value type of this function can only be
`NSObject.Type`, because if you return with `NSProtocolInterceptor.Type`,
you can only init the returned class to be a `NSProtocolInterceptor` but not
its subclass.
*/
private class func concreteClassWithProtocols(protocols: [Protocol],
concatenatedName: String,
salt: UInt?)
-> NSObject.Type
{
let className: String = {
let basicClassName = "_" +
NSStringFromClass(NSProtocolInterceptor.self) +
"_" + concatenatedName
if let salt = salt { return basicClassName + "_\(salt)" }
else { return basicClassName }
}()
let nextSalt = salt.map {$0 + 1}
if let theClass = NSClassFromString(className) {
switch theClass {
case let anInterceptorClass as NSProtocolInterceptor.Type:
let isClassConformsToAllProtocols: Bool = {
// Check if the found class conforms to the protocols
for eachProtocol in protocols
where !class_conformsToProtocol(anInterceptorClass,
eachProtocol)
{
return false
}
return true
}()
if isClassConformsToAllProtocols {
return anInterceptorClass
} else {
return concreteClassWithProtocols(protocols,
concatenatedName: concatenatedName,
salt: nextSalt)
}
default:
return concreteClassWithProtocols(protocols,
concatenatedName: concatenatedName,
salt: nextSalt)
}
} else {
let subclass = objc_allocateClassPair(NSProtocolInterceptor.self,
className,
0)
as! NSObject.Type
for eachProtocol in protocols {
class_addProtocol(subclass, eachProtocol)
}
objc_registerClassPair(subclass)
return subclass
}
}
}
/**
Returns true when the given selector belongs to the given protocol.
*/
public func sel_belongsToProtocol(aSelector: Selector,
_ aProtocol: Protocol) -> Bool
{
for optionBits: UInt in 0..<(1 << 2) {
let isRequired = optionBits & 1 != 0
let isInstance = !(optionBits & (1 << 1) != 0)
let methodDescription = protocol_getMethodDescription(aProtocol,
aSelector, isRequired, isInstance)
if !objc_method_description_isEmpty(methodDescription)
{
return true
}
}
return false
}
public func objc_method_description_isEmpty(
var methodDescription: objc_method_description)
-> Bool
{
let ptr = withUnsafePointer(&methodDescription) { UnsafePointer<Int8>($0) }
for offset in 0..<sizeof(objc_method_description) {
if ptr[offset] != 0 {
return false
}
}
return true
}
Actually, this worked for me:
#implementation MySubclass {
id _actualDelegate;
}
// There is no need to set the value of _actualDelegate in an init* method
- (void)setDelegate:(id)newDelegate {
[super setDelegate:nil];
_actualDelegate = newDelegate;
[super setDelegate:(id)self];
}
- (id)delegate {
return self;
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([_actualDelegate respondsToSelector:aSelector]) { return _actualDelegate; }
return [super forwardingTargetForSelector:aSelector];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
return [super respondsToSelector:aSelector] || [_actualDelegate respondsToSelector:aSelector];
}
#end
...making the subclass to be the message interceptor in the awesome answer given by e.James.
Yes, but you'll have to override every delegate method in the docs. Basically, make a second delegate property and implement the delegate protocol. When your delegate methods are called, take care of your business and then call the same method on your second delegate from the delegate method that was just run. E.g.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// Do stuff here
if ([self.delegate2 respondsToSelector:#selector(scrollViewDidScroll:)]) {
[self.delegate2 scrollViewDidScroll:scrollView];
}
}

Resources