I have a custom UIWebView (EpubWebView), with a custom NSURLCache (EpubCache) for handling requests.
i created a custom delegate for handling request.
EpubCache.h
#protocol EpubCacheDelegate <NSObject>
#required
- (NSCachedURLResponse *)hadleRequest:(NSURLRequest *)request;
#end
#interface EpubCache : NSURLCache
#property (nonatomic, weak) id <EpubCacheDelegate> cacheDelegate;
#end
EpubCache.m
import "EpubCache.h"
#interface EpubCache ()
#end
#implementation EpubCache
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
{
return [self.cacheDelegate hadleRequest:request];
}
#end
EpubWebView .h
#interface EpubWebView : UIWebView <UIWebViewDelegate, EpubCacheDelegate>
#property (strong, nonatomic) EpubCache *mLocalCache;
#end
EpubWebView.m
- (void) localInit
{
self.mLocalCache = [[EpubCache alloc] init];
self.mLocalCache.cacheDelegate = self;
[NSURLCache setSharedURLCache:self.mLocalCache];
}
- (NSCachedURLResponse *)hadleRequest:(NSURLRequest *)request
{
// return handled request
}
on the other hand i have a navigationcontroller with a tableview and the destination view controller have this webview.
when i ran the app and click on an item in tableview, everything is fine and delegate works as expected.
if i click back and click on other item in tableview, things goes wrong, the cachedResponseForRequest getting called but the hadleRequest wont, i checked and findout that the delegate is null!
i can not figure out what is happening here.
any help would be appreciated.
UPDATE 1
EpubWebView.m
- (id)init
{
self = [super init];
if (self)
{
[self localInit];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self)
{
[self localInit];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if(self)
{
[self localInit];
}
return self;
}
UPDATE 2
the segue of the tableview that bring up the view controller that contain EpubWebView
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *indexPath = [self.collectionView indexPathForCell:sender];
BookViewController *bookController = segue.destinationViewController;
bookController.mBook = booksList[indexPath.row];
}
and BookViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
mWebView = [[EpubWebView alloc] initWithFrame:self.mainView.frame];
[self.mainView addSubView:mWebView];
[mWebView setBook:self.mBook];
}
You need to make a few modifications. First, remove your "localInit" method, then create a new function in EPubWebView:
- (void) setCache: (EpubCache *)localCache
{
localCache.cacheDelegate = self;
}
Now, you can create and hold onto your cache in your BookViewController via these lines in the .m file:
#interface BookViewController ()
#property (strong, nonatomic) EpubCache *mLocalCache;
#end
and change your BookViewController's viewDidLoad method to look like:
- (void) viewDidLoad {
self.mLocalCache = [[EpubCache alloc] init];
// only need to do this once, at viewDidLoad time
[NSURLCache setSharedURLCache:self.mLocalCache ];
[super viewDidLoad];
mWebView = [[EpubWebView alloc] initWithFrame:self.mainView.frame];
[mWebView setCache:self.mLocalCache];
[self.mainView addSubView:mWebView];
[mWebView setBook:self.mBook];
}
Related
Basically, I am now intend to use delegate to pass values between view controllers.
The flow of the view controllers is A -> B -> C using push segue.
When the user does some action in the "C" view controller, how to pass the value BACK to the first view controller, which is "A"?
In my code, "self.delegate" is always nil and the delegate method is never triggered.
Below is the code of the First VC and Third VC:
First VC .m:
#import "ViewController.h"
#import "ThirdViewController.h"
#interface ViewController ()<PassValueProtocal>
#property (weak, nonatomic) IBOutlet UILabel *myLabel;
#end
#implementation ViewController
{
ThirdViewController *thirdVC;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void) passValueBack:(NSString *)value
{
NSLog(#"HAHAH");
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)segueToSecondVC:(UIButton *)sender
{
thirdVC = [ThirdViewController sharedManager];
thirdVC.delegate = self;
}
#end
Third VC .h:
#import <UIKit/UIKit.h>
#protocol PassValueProtocal <NSObject>
- (void) passValueBack: (NSString *) value;
#end
#interface ThirdViewController : UIViewController
#property (weak, nonatomic) id<PassValueProtocal>delegate;
+ (id) sharedManager;
#end
Third VC .m:
#import "ThirdViewController.h"
#interface ThirdViewController ()
#property (weak, nonatomic) IBOutlet UITextField *myTextField;
#end
#implementation ThirdViewController
+ (id) sharedManager
{
NSLog(#"myDelegate sharedManager");
static ThirdViewController *sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ sharedManager = [[self alloc] init]; });
return sharedManager;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)passValueAction:(UIButton *)sender
{
NSLog(#"%#", self.delegate);
if ([self.delegate respondsToSelector:#selector(passValueBack:)])
{
[self.delegate passValueBack:self.myTextField.text];
}
}
#end
Your code
- (IBAction)segueToSecondVC:(UIButton *)sender
{
thirdVC = [ThirdViewController sharedManager];
thirdVC.delegate = self;
}
Should not be here. You should implement function prepareForSegue and move this code there. Something like
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
ThirdViewController *thirdVC = (ThirdViewController *)segue.destinationViewController;
thirdVC.delegate = self;
}
Sorry about the confusion.
What I want to do:
enter a string in a textfield in a view(EnterCommandViewController) and click save button, which will dismiss current view and go back to another view(DetectionViewController) and show the string in the UILabel in current view(DetectionViewController). So I have put define the delegate protocol in EnterCommandViewController, my question is that why the respondToSelector, which is used to check whether someone is listening does not work.
I am really a beginner in iOS, I am right now writing a delegate to send text got form UITextField to a UILabel, But I found that the respondToSelector cannot be called by using NSLog for testing.
Below is my code for reference:
EnterCommandViewController.h
#import <UIKit/UIKit.h>
#import "RscMgr.h"
#protocol EnterCommandDelegate <NSObject>
-(void) commandEntered:(NSString*)command;
#end
#interface EnterCommandViewController : UIViewController <RscMgrDelegate>
{
RscMgr* rscMgr;
__weak IBOutlet UITextField *inputTextField;
__unsafe_unretained id<EnterCommandDelegate> delegate;
}
-(void)sendMessage:(NSString*)message;
- (IBAction)cancelPressed;
- (IBAction)savePressed;
#property (nonatomic,assign)id delegate;
#end
EnterCommandViewController.m
#import "EnterCommandViewController.h"
#interface EnterCommandViewController () <UITextFieldDelegate>
{
#private
BOOL connected;
}
#end
#implementation EnterCommandViewController
#synthesize delegate;
- (void)viewDidLoad {
[super viewDidLoad];
rscMgr = [[RscMgr alloc] init];
[rscMgr setDelegate:self];
// Do any additional setup after loading the view, typically from a nib.
inputTextField.text=#"";
[inputTextField becomeFirstResponder];
}
-(void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
inputTextField.delegate = self;
}
-(void) viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
inputTextField.delegate = nil;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)cancelPressed {
[self dismissViewControllerAnimated:YES completion:^{}];
}
- (IBAction)savePressed {
//is anyone listening
NSLog(#"the command is %#",inputTextField.text);
NSLog(#"Checking -- SomeMethod is listening");
if([delegate respondsToSelector:#selector(commandEntered:)]){
NSLog(#"SomeMethod is listening");
//send delegate function with the command entered by the user
[delegate commandEntered:inputTextField.text];
}
[self dismissViewControllerAnimated:YES completion:^{}];
}
DetectionViewController.h
#import <UIKit/UIKit.h>
#import "EnterCommandViewController.h"
#interface DetectionViewController : UIViewController <EnterCommandDelegate>{
__weak IBOutlet UILabel *showCommand;
}
- (IBAction)showSettings:(UIBarButtonItem *)sender;
#end
DetectionViewController.m
#import <Foundation/Foundation.h>
#import "DetectionViewController.h"
#implementation DetectionViewController
- (IBAction)showSettings:(UIBarButtonItem *)sender {
}
-(void) viewDidLoad{
[super viewDidLoad];
showCommand.text=#"";
EnterCommandViewController* enterCVC = [[EnterCommandViewController alloc] init];
enterCVC.delegate = self;
}
#pragma mark - EnterCommandDelegate function(s)
-(void) commandEntered:(NSString *)command{
// showCommand.text = command;
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"command: %#",command);
[self->showCommand setText:command];
});
}
#end
AppDelegate.m
#import "AppDelegate.h"
#interface AppDelegate ()
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
return YES;
}
You are not setting the delegate properly.
EnterCommandViewController* enterCVC = [[EnterCommandViewController alloc] init];
enterCVC.delegate = self;
This is not the way of setting the delegate in your case, since you are not using the created instance of enterCVC, instead a new instance is created from the storyboard when you are transitioning toEnterCommandViewController `(From your comment its clear that you are using the storyboard for this).
So what you can do is you should the delegate from prepareForSegue in DetectionViewController like
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get reference to the destination view controller
EnterCommandViewController* enterCVC = [segue destinationViewController];
enterCVC.delegate = self;
}
So perhaps this is a beginner's mistake and super easy to you guys, but i really do not know how to solve it,really appreciate for any suggestions:
Right Now:
1: I have to ViewController: EnterCommandViewController and DetectionViewController
2: I wrote Delegate protocol in EnterCommandViewController and set DetectionViewController as its delegate.
3: About delegate: I have a inputTextField in the EnterCommandView and a "Save" bar button item on the top toolbar in this view. Once I click the save , current view will be dismissed and return back to DetectionView and show the NSString just entered in the UILabel in DetectionView.
Finally, My question is that Why After I alloc and init a EnterCommandViewController instance , that is enterCVS, the instance is still nil as show in end of my post.
Code:
EnterCommandViewController.h
#import <UIKit/UIKit.h>
#import "RscMgr.h"
#protocol EnterCommandDelegate <NSObject>
#optional
-(void) commandEntered:(NSString*)command;
#end
#interface EnterCommandViewController : UIViewController <RscMgrDelegate,EnterCommandDelegate>
{
RscMgr* rscMgr;
IBOutlet UITextField *inputTextField;
// DetectionViewController* detectionViewController;
// __unsafe_unretained id<EnterCommandDelegate> delegate;
}
-(void)sendMessage:(NSString*)message;
-(id)initWithDelegate:(id)delegateToBe;
- (IBAction)cancelPressed;
- (IBAction)savePressed;
#property (nonatomic,weak) id<EnterCommandDelegate> delegate; //assign replaced
#end
EnterCommandVIewController.m
#import "EnterCommandViewController.h"
#import "DetectionViewController.h"
#interface EnterCommandViewController () <UITextFieldDelegate>
{
#private
BOOL connected;
}
#end
#implementation EnterCommandViewController
#synthesize delegate;
- (void)viewDidLoad {
[super viewDidLoad];
rscMgr = [[RscMgr alloc] init];
[rscMgr setDelegate:self];
// Do any additional setup after loading the view, typically from a nib.
[inputTextField becomeFirstResponder];
}
-(id)initWithDelegate:(id)delegateToBe{
if(self = [super init]){
delegate = delegateToBe;
}
return self;
}
-(void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
inputTextField.delegate = self;
}
-(void) viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
inputTextField.delegate = nil;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - UITextFieldDelegate Methods
-(BOOL) textFieldShouldReturn:(UITextField *)textField{
[self sendMessage:textField.text];
textField.text = nil;
return NO;
}
#pragma mark - Serial Tx/Rx Methods Implementation
-(void) sendMessage:(NSString *)message{
if(connected == YES) {
[rscMgr writeString:message];
}
else{
NSLog(#"CableDisconnected!");
NSLog(#"Attempted To Send: %#",message);
}
}
- (IBAction)cancelPressed {
[self dismissViewControllerAnimated:YES completion:^{}];
}
- (IBAction)savePressed {
//is anyone listening
if([[[UIDevice currentDevice]systemVersion] compare:#"7.0" options:NSNumericSearch] != NSOrderedAscending){
NSLog(#"SYStem version > 7.0");
}
if(delegate&&[delegate respondsToSelector:#selector(commandEntered:)]){
NSLog(#"SomeMethod is listening");
[delegate commandEntered:inputTextField.text];
}
[self dismissViewControllerAnimated:YES completion:nil]; //commened: ^{}
}
#pragma mark - RscMgrDelegate Methods Implementation
-(void) cableConnected:(NSString *)protocol{
inputTextField.text = #"cableConnected";
[rscMgr setBaud:9600];
[rscMgr open];
connected = YES;
}
-(void) cableDisconnected{
inputTextField.text = #"cableDisconnected";
connected = NO;
}
-(void) readBytesAvailable:(UInt32)length{}
-(void) portStatusChanged{}
#end
DetectionViewController.h
#import <UIKit/UIKit.h>
#import "EnterCommandViewController.h"
#interface DetectionViewController : UIViewController <EnterCommandDelegate>{
}
- (IBAction)showSettings:(UIBarButtonItem *)sender;
#property (nonatomic, strong) EnterCommandViewController* enterCVC;
#property (nonatomic, strong) IBOutlet UILabel *showReceivedCommand;
#end
DetectionViewController.m
#import <Foundation/Foundation.h>
#import "DetectionViewController.h"
#import "EnterCommandViewController.h"
#implementation DetectionViewController
#synthesize showReceivedCommand;
#synthesize enterCVC;
- (IBAction)showSettings:(UIBarButtonItem *)sender {
}
-(void) viewDidLoad{
[super viewDidLoad];
if(showReceivedCommand){
showReceivedCommand.text=#"Initial text";
NSLog(#"UILAbel in ViewDidload is not nil");
}else {
NSLog(#"UILAbel in viewDidload is nil");
}
enterCVC = [[EnterCommandViewController alloc] init];
if(enterCVC.delegate) NSLog(#"X nil");
[enterCVC setDelegate:self];
}
#pragma mark - EnterCommandDelegate function(s)
-(void)commandEntered:(NSString *)command{
dispatch_async(dispatch_get_main_queue(), ^{
if(showReceivedCommand){
NSLog(#"UILabel is not nil");
}else{NSLog(#"UILabel is nil");}
showReceivedCommand = [[UILabel alloc] init];
NSLog(#"command received: %#",command);
showReceivedCommand.text = command;
[showReceivedCommand setNeedsDisplay];
NSLog(#"text in showReceivedCommand is %#",showReceivedCommand.text);
});
}
#end
I set a break point at DetectionViewController.n --> ViewDidLoad() --> [enterCVC setDelegate:self];
I got:
self DetectionViewController * 0x15c50e850 0x000000015c50e850
UIViewController UIViewController
showReceivedCommand UILabel * 0x15c510650 0x000000015c510650
enterCVC EnterCommandViewController * 0x15c611360 0x000000015c611360
showReceivedCommand UILabel * 0x15c510650 0x000000015c510650
enterCVC EnterCommandViewController * 0x15c611360 0x000000015c611360
UIViewController UIViewController
rscMgr RscMgr * nil 0x0000000000000000
inputTextField UITextField * nil 0x0000000000000000
connected BOOL NO false
delegate id 0x0 0x0000000000000000
enterCVC = [[EnterCommandViewController alloc] init]
Try changing that to....
enterCVC = [[EnterCommandViewController alloc] initWithDelegate:self];
Let's see if you guys can find the error here... because I'm really stuck right now.
DBManager.h
#protocol DBManagerDelegate <NSObject>
#optional
- (void) managerDidFinishUpdating:(id)controller;
#end
#interface DBManager : NSObject
#property (strong, nonatomic) id <DBManagerDelegate> delegate;
- (id) init;
#end
DBManager.m
#import "DBManager.h"
#implementation DBManager
- (id)init {
self = [super init];
if (self) {
[[self delegate] managerDidFinishUpdating:self];
}
return self;
}
UIViewController.h
#import <UIKit/UIKit.h>
#import "DBManager.h"
#interface DBViewController : UIViewController <DBManagerDelegate>
#property (nonatomic, retain) DBManager *manager;
#end
UIViewController.m
#import "DBViewController.h"
#implementation DBViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_manager = [[DBMataroManager alloc] init];
[_manager setDelegate:self];
}
- (void)managerDidFinishUpdating:(id)controller {
NSLog(#"Manager did finish");
}
#end
You should write like this,
- (id)initWithDelegate:(id)delegate {
self = [super init];
if (self) {
_delegate = delegate;
[_delegate managerDidFinishUpdating:self];
}
return self;
}
//Call it as follow
_manager = [[DBManager alloc] initWithDelegate:self];
[_manager setDelegate:self];
The delegate method managerDidFinishUpdating: is called within init. You set yourself as the delegate after calling init.
Maybe an initWithDelegate: method would help.
- (instancetype)initWithDelegate:(id< DBManagerDelegate>)delegate {
self = [super init];
if (self) {
self.delegate = delegate;
[self.delegate managerDidFinishUpdating:self];
}
return self;
}
You are trying to use the delegate in the init, but you have not set it until the next statement. The only way to do this would be to set the delegate as part of you init statement.
-(id)initWithDelegate:(id<DBManageDelegate>)delegate {
if ((self = [super init])) {
self.delegate = delegate;
if (delegate && [delegate respondsToSelector:#selector(managerDidFinishUpdating:)]) {
[delegate performSelector:#selector(managerDidFinishUpdating:) withObject:self];
}
}
return self;
}
But if you plan to do some additional things before the manager is updated I would suggest you move all of that outside of the init, perhaps into an updateManager function like this
-(void)updateManager {
// Do the stuff that updates your manager here
if (delegate && [delegate respondsToSelector:#selector(managerDidFinishUpdating:)]) {
[delegate performSelector:#selector(managerDidFinishUpdating:) withObject:self];
}
}
..later in your app
_manager = [[DBMataroManager alloc] init];
[_manager setDelegate:self];
[_manager updateManager];
pass data from FirstViewController to DetailViewController. i can not set the text of label in DetailViewController; FirstViewController is a tableview and it is good.
i use method updateRowNumber to set the rowNumber . and in DetailViewController, i can use debugger to see the rowNumber is correct. but the label's text is not showed on the view.
anyone can help me out?
in my FirstViewController
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (dvController == nil)
{
DetailViewController *aController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
self.dvController = aController;
[aController release];
}
[[self navigationController] pushViewController:dvController animated:YES];
[dvController updateRowNumber:indexPath.row];
}
in my DetailViewController.h
#import <UIKit/UIKit.h>
#interface DetailViewController : UIViewController
{
int rowNumber;
IBOutlet UILabel *message;
}
#property(readwrite) int rowNumber;
#property(nonatomic, retain) IBOutlet UILabel *message;
- (void) updateRowNumber:(int) theindex;
#end
in my DetailViewController.m
#import "DetailViewController.h"
#interface DetailViewController ()
#end
#implementation DetailViewController
#synthesize message, rowNumber;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void) updateRowNumber: (int) theindex
{
rowNumber = theindex + 1;
message.text = [NSString stringWithFormat:#"row %i was clicked", rowNumber];
}
- (void)dealloc
{
[message release];
[super dealloc];
}
- (void)viewDidLoad
{
message.text = [NSString stringWithFormat:#"row %i was clicked ", rowNumber];
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Do any additional setup after loading the view from its nib.
}
- (void)viewWillAppear:(BOOL)animated
{
//message.text = [NSString stringWithFormat:#"row %i was clicked ", rowNumber];
[super viewWillAppear: animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear: animated];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
You'll need to learn how the view is loaded, the process is well described in the documentation
What happens is that the view and all the outlets are nil until the view is loaded, you can make sure it is loaded by calling self.view; before configuring the outlet at updateRowNumber:
Please also note, you are better to call [super viewDidLoad] in the beginning of the overridden viewDidLoad, it makes sense as you need to let UIViewContorller to do it's staff before you do some customized logic, the dealloc is different as you need to do your logic before the standard NSObject -dealloc fires. Hope it's understandable.