I am trying to call another method with Delegate and protocols in my app. I need to send the variable "myData" to another view but for some reasons it doesn't works. I don't know what I am doing wrong with the implementation of delegates and protocols. The delegate never call the action of the protocol.
Sorry I'm new with this.
BLEViewController.h
#import <UIKit/UIKit.h>
#import "BLE.h"
#protocol EnviarDatos <NSObject>
//Metodo que se manda llamar pero se implementa en otra clase
-(void) actualizaDatos:(NSData*)Data;
#end
#interface BLEViewController : UITableViewController <BLEDelegate>
{
//id <EnviarDatos> delegate;
}
#property (nonatomic,assign)id delegate;
#property (nonatomic, retain) NSData *myData;
+ (BLE*) theBLEObject;
- (void) scanForPeripherals;
- (IBAction)connect:(id)sender;
-(void) activaProtocolo;
#end
BLEViewController.m
//
// BLEViewController.m
// DL_RemoteBLE_02
//
// Created by Dave Lichtenstein on 3/16/14.
// Copyright (c) 2014 Dave Lichtenstein. All rights reserved.
//
#import "BLEViewController.h"
static BLE* ble;
static UILabel *statusLabel;
static NSString* connectionStatus = #"Not connected!";
#interface BLEViewController ()
#end
#implementation BLEViewController
#synthesize delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
if(ble==nil)
{
// Create our Bluetooth Low Energy object
//
ble = [[BLE alloc] init];
[ble controlSetup];
ble.delegate = self;
}
// Create a toolbar at the bottom of the screen to show status text, etc.
//
// get screen size
//
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
CGFloat screenHeight = screenRect.size.height;
CGFloat toolbarHeight = 50.0;
CGFloat labelHeight = 50.0;
if(statusLabel==nil) // only create once
{
// create our status label object
//
statusLabel = [[UILabel alloc] initWithFrame:CGRectMake(5, screenHeight-toolbarHeight-labelHeight, screenWidth, labelHeight)];
statusLabel.backgroundColor = [UIColor clearColor];
statusLabel.textColor = [UIColor blackColor];
statusLabel.font = [UIFont boldSystemFontOfSize:15];
statusLabel.text = #"Connection Status:";
}
// create a toolbar
//
UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0,screenHeight-toolbarHeight,screenWidth,toolbarHeight)];
toolbar.tintColor = [UIColor blackColor];
/*UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(5, 5, 150, 20)];
label.backgroundColor = [UIColor clearColor];
label.textColor = [UIColor blackColor];
label.font = [UIFont boldSystemFontOfSize:15];
label.text = #"Status:";
UIBarButtonItem *labeltext = [[UIBarButtonItem alloc] initWithCustomView:label];
UIBarButtonItem *button = [[UIBarButtonItem alloc]initWithTitle:#"" style:UIBarButtonItemStyleDone target:self action:nil];
NSArray *items = [NSArray arrayWithObjects:statusLabel, nil];
toolbar.items = items;
*/
[self.view addSubview:statusLabel];
[self.view addSubview:toolbar];
// Update our status label
statusLabel.text = connectionStatus;
_myData = [[NSData alloc]init];
delegate = self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//-------------------------------------------------------
// methods
/////////////////////////////////////////////////////////
+ (BLE*) theBLEObject
{
return ble;
}
-(void) connectionTimer:(NSTimer *)timer
{
if (ble.peripherals.count > 0)
{
[ble connectPeripheral:[ble.peripherals objectAtIndex:0]];
}
NSLog(#"connectionTimer"); // diag
}
// We call this when the view loads to try to connect to our bluetooth perepheral
//
- (void) scanForPeripherals
{
if (ble.activePeripheral)
if(ble.activePeripheral.state == CBPeripheralStateConnected)
{
statusLabel.text = #"Disconnectng from peripheral...";
[[ble CM] cancelPeripheralConnection:[ble activePeripheral]];
return;
}
if (ble.peripherals)
ble.peripherals = nil;
NSLog(#"scanning...");
statusLabel.text = #"Scanning for peripherals...";
[ble findBLEPeripherals:2];
[NSTimer scheduledTimerWithTimeInterval:(float)2.0 target:self selector:#selector(connectionTimer:) userInfo:nil repeats:NO];
//[indConnecting startAnimating];
}
- (IBAction)connect:(id)sender {
[self scanForPeripherals];
}
///////////////////////////////////////////////////////////
#pragma mark - BLE delegate
///////////////////////////////////////////////////////////
NSTimer *rssiTimer;
// When Connected, this will be called
-(void) bleDidConnect
{
NSLog(#"->Connected");
statusLabel.text = #"Connected!";
connectionStatus = #"Connected!";
// Schedule to read RSSI every 1 sec.
rssiTimer = [NSTimer scheduledTimerWithTimeInterval:(float)1.0 target:self selector:#selector(readRSSITimer:) userInfo:nil repeats:YES];
}
// When RSSI is changed, this will be called
-(void) bleDidUpdateRSSI:(NSNumber *) rssi
{
// Append the rssi value to our status label
//
NSString *temp = [NSString stringWithFormat:#"%# (%#)", connectionStatus, rssi];
statusLabel.text = temp;
}
-(void) readRSSITimer:(NSTimer *)timer
{
[ble readRSSI];
}
// When data is comming, this will be called
-(void) bleDidReceiveData:(unsigned char *)data length:(int)length
{
NSData *d = [NSData dataWithBytes:data length:length];
_myData = [NSData dataWithBytes:data length:length];
NSString *s = [[NSString alloc] initWithData:d encoding:NSUTF8StringEncoding];
NSLog(#"Datos en String %#",s);
//_datosdelegate = self;
//Is anyone listening
if([delegate respondsToSelector:#selector(actualizaDatos:)])
{
//send the delegate function with the amount entered by the user
[delegate actualizaDatos:_myData];
NSLog(#"Entro delegado");
}
}
- (void)bleDidDisconnect
{
NSLog(#"->Disconnected");
connectionStatus = #"Disconnected!";
statusLabel.text = #"Disconnected!";
[rssiTimer invalidate];
}
#end
sevenSegmentsViewController.h
#import <UIKit/UIKit.h>
#import "BLEViewController.h"
#import "BLE.h"
#interface sevenSegmentsViewController : UIViewController<EnviarDatos>{
UIImage *unoON;
UIImage *dosON;
UIImage *tresON;
UIImage *cuatroON;
UIImage *cincoON;
UIImage *seisON;
UIImage *sieteON;
UIImage *unoOFF;
UIImage *dosOFF;
UIImage *tresOFF;
UIImage *cuatroOFF;
UIImage *cincoOFF;
UIImage *seisOFF;
UIImage *sieteOFF;
}
#property (strong, nonatomic) IBOutlet UIImageView *uno;
#property (strong, nonatomic) IBOutlet UIImageView *dos;
#property (strong, nonatomic) IBOutlet UIImageView *tres;
#property (strong, nonatomic) IBOutlet UIImageView *cuatro;
#property (strong, nonatomic) IBOutlet UIImageView *cinco;
#property (strong, nonatomic) IBOutlet UIImageView *seis;
#property (strong, nonatomic) IBOutlet UIImageView *siete;
#end
sevenSegmentsViewController.m
//
// sevenSegmentsViewController.m
// iShield
//
// Created by Victor CarreƱo on 29/03/14.
// Copyright (c) 2014 RedBearLab. All rights reserved.
//
#import "sevenSegmentsViewController.h"
#interface sevenSegmentsViewController ()
#end
#implementation sevenSegmentsViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
unoOFF = [UIImage imageNamed:#"7segnh.png"];
dosOFF = [UIImage imageNamed:#"7segnv.png"];
tresOFF = [UIImage imageNamed:#"7segnv.png"];
cuatroOFF =[UIImage imageNamed:#"7segnh.png"];
cincoOFF = [UIImage imageNamed:#"7segnh.png"];
seisOFF = [UIImage imageNamed:#"7segnv.png"];
sieteOFF = [UIImage imageNamed:#"7segnh.png"];
unoON = [UIImage imageNamed:#"7segvh.png"];
dosON = [UIImage imageNamed:#"7segvv.png"];
tresON = [UIImage imageNamed:#"7segvv.png"];
cuatroON =[UIImage imageNamed:#"7segvh.png"];
cincoON = [UIImage imageNamed:#"7segvh.png"];
seisON = [UIImage imageNamed:#"7segvv.png"];
sieteON = [UIImage imageNamed:#"7segvh.png"];
_uno = [[UIImageView alloc]initWithImage:unoOFF];
_dos = [[UIImageView alloc]initWithImage:dosOFF];
_tres = [[UIImageView alloc]initWithImage:tresOFF];
_cuatro = [[UIImageView alloc]initWithImage:cuatroOFF];
_cinco = [[UIImageView alloc]initWithImage:cincoOFF];
_seis = [[UIImageView alloc]initWithImage:seisOFF];
_siete = [[UIImageView alloc]initWithImage:sieteOFF];
//BLEViewController *myBLE = [[BLEViewController alloc]init];
//BLE *myBLE = [BLEViewController theBLEObject];
//NSLog(#"%#", myBLE.myData);
BLEViewController *myBLE = [[BLEViewController alloc]init];
myBLE.delegate = self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#pragma mark Delegado de Actualizar datos
-(void) actualizaDatos :(NSData *)myData{
NSLog(#"Datos recividos");
NSLog(#"Imprimio mi data con exitos %#", myData);
}
#end
In a nutshell, your BLEViewController is setting its delegate property to "self" when I think you want it to get set to an instance of "sevenSegmentsViewController". As a result the "if([delegate respondsToSelector:..." test is failing and you are never hitting the call to actualizaDatos. If you are using protocols correctly, you don't really need to test for "respondsToSelector" because by definition, the delegate must support the protocol.
The compiler and IDE are not showing you the error because you declared the property of the BLEViewController as just type "id" instead of
id<EnviarDatos>
If you fix the property declaration to say that your delegate must support the right protocol, you'll immediately see the errors highlighted.
Related
I created a NavigationBar and added it to the UIViewController. But after init, the reference turns to nil. I'm new to iOS and OC, I don't know why. Anyone can help? Thank you.
code summary:
#interface ContainerViewController()
#property (nonatomic, retain) UINavigationBar *nav;
#property (nonatomic, retain) UINavigationItem *navItem;
#end
#implementation ContainerViewController
- (instancetype) initWithParams:(NSDictionary *)params {
self = [super init];
if (self) {//...}
return self;
}
- setNavTitle:(NSDictionary *) params {
NSString *title = params[#"title"];
/////////////////////////////////
// here goes wrong
// self.navItem == nil here, why?
/////////////////////////////////
self.navItem.title = title;
}
- (void) viewWillAppear:(Bool)animated {
[super viewWillAppear:NO];
static float navHeight = 64.0;
UIViewController *wvController = [WebView init here];
UINavigationBar *nav = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), navHeight)];
UINavigationItem *navItem = [[UINavigationItem alloc] initWithTitle:title];
nav.items = [NSArray arrayWithObjects: navItem, nil];
///////////////////////////////
// I saved the reference here
//////////////////////////////
[self setNav:nav];
[self setNavItem:navItem];
[self.view addSubview:nav];
[self addChildViewController:wvController];
wvController.view.bounds = CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds) - navHeight);
[self.view addSubview:wvController.view];
[wvController didMoveToParentViewController:self];
}
#end
This will be useful for you, kindly check and do
Tutorial point site is very easy to learn some important UI basics if you are working in Objective C
I am attempting to create a view to show a list of items and prices using two UILabels in a UIView.
In my UIViewController I call my subview LineItemView and pass data, and return the UIView to the main view that will hold the subviews.
Though my view is returning empty. The strings are null.
ViewController.m
#import "LineItemView.h"
//...
#property (weak, nonatomic) IBOutlet UIView *viewCharges;
//...
- (void)viewDidLoad {
[super viewDidLoad];
[self loadData];
}
- (void) loadData {
//Here we will fill in the viewCharges view
LineItemView * view = [[LineItemView alloc]init];
view.linePrice = #"1.00";
view.lineItem = #"Something";
[self.viewCharges addSubview:view];
}
LineItemView.h
#interface LineItemView : UIView {
UILabel * lblLineItem, *lblLinePrice;
}
#property (nonatomic,strong) NSString* lineItem;
#property (nonatomic,strong) NSString* linePrice;
LineItemView.m
#define LABEL_MINIMUM_HEIGHT 32
#define VERTICAL_MARGIN 5
#define HORIZONTAL_MARGIN 10
#implementation LineItemView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self createUI];
[self updateData];
NSLog(#"\n\n\n Line Item: %# Price: %# \n\n\n", self.lineItem, self.linePrice);
}
return self;
}
-(void) createUI {
lblLineItem = [[UILabel alloc] initWithFrame:CGRectZero];
lblLineItem.backgroundColor = [UIColor greenColor];
[self addSubview:lblLineItem];
lblLinePrice = [[UILabel alloc] initWithFrame:CGRectZero];
lblLinePrice.backgroundColor = [UIColor yellowColor];
[self addSubview:lblLinePrice];
}
- (void) updateLayout {
lblLineItem.frame = CGRectMake(HORIZONTAL_MARGIN, VERTICAL_MARGIN, 300, 35);
lblLinePrice.frame = CGRectMake(lblLineItem.frame.origin.x + lblLineItem.frame.size.width + HORIZONTAL_MARGIN, VERTICAL_MARGIN, 80, 35);
}
- (void) updateData {
lblLineItem.text = self.lineItem;
lblLinePrice.text = [NSString stringWithFormat:#"%0.2f", [self.linePrice floatValue]];
}
- (void) layoutSubviews {
[super layoutSubviews];
[self updateLayout];
}
What am I doing wrong?
If I want to continue calling LineItemView, how do I ensure that it is added below the previous one and ensure the viewCharges size is readjusted to fit all the subviews?
Solution using setters:
#implementation LineItemView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self createUI];
}
return self;
}
-(void) createUI {
lblLineItem = [[UILabel alloc] initWithFrame:CGRectZero];
lblLineItem.backgroundColor = [UIColor greenColor];
[self addSubview:lblLineItem];
lblLinePrice = [[UILabel alloc] initWithFrame:CGRectZero];
lblLinePrice.backgroundColor = [UIColor yellowColor];
[self addSubview:lblLinePrice];
}
- (void) updateLayout {
lblLineItem.frame = CGRectMake(HORIZONTAL_MARGIN, VERTICAL_MARGIN, 300, 35);
lblLinePrice.frame = CGRectMake(lblLineItem.frame.origin.x + lblLineItem.frame.size.width + HORIZONTAL_MARGIN, VERTICAL_MARGIN, 80, 35);
}
- (void) layoutSubviews {
[super layoutSubviews];
[self updateLayout];
}
- (void)setLineItem:(NSSTring *)lineItem {
_lineItem = lineItem;
lblLineItem.text = self.lineItem;
}
- (void)setLinePrice:(NSNumber *)linePrice {
_linePrice = linePrice;
lblLinePrice.text = [NSString stringWithFormat:#"%0.2f", [self.linePrice floatValue]];
}
I'm struggling with a retaining issue between two of my UIViewControllers. The view controllers are never deleted causing my app memory to keep growing memory consumption.
UITitleScreenViewController is my initial view controller. When I go from it to UIChooseAntViewController (a choose player screen) I want to relinquish ownership of UITitleViewController but as you can see in the instruments below the controller is still retained after the transition:
The second image is the retain/release history. All entries prior to #133 were issued on the app startup. I believe #133 and #140 are pairs created by the storyboard segue. So whose responsibility is to issue that extra release to destroy the controller? I tried to set self.view = nil on my willDidDisappear method but no deal.
Not only it is not releasing the controllers but it is creating new instances of them each time a transition. For instance, when I come back from ChooseAnt to Title it creates another instance of UITitleViewController!
Things that are important to say:
1) NSZombies flag is not ticked in the target scheme
2) There are no blocks in my UITitleViewController, and I commented out all blocks in UIChooseAntController. In fact these controllers are very simple. UITitle is entirely defined via storyboard (just a view with a background and two buttons performing segues)
while UIChooseAnt is a control that presents a background and a swipe interface to display available characters and radio buttons. The segue is performed programatically by calling [self performSegueWithIdentifier];
3) I don't know if this matters but the segues are defined as modal and have no animation.
EDIT: 4) None of the the controllers reference each other.
Below is the source code for the TitleViewController
This problem is driving me crazy. If anyone could shed some light on it. Anything would be of great help! Thanks!
#interface SMTitleScreenViewController ()
#property (weak, nonatomic) IBOutlet UIButton *buttonPlay;
#property (weak, nonatomic) IBOutlet UIButton *buttonCamera;
- (IBAction)onButtonPlay:(id)sender;
- (IBAction)onButtonCamera:(id)sender;
#end
#implementation SMTitleScreenViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
UIColor* color = [UIColor colorWithRed:0.2509f green:0.1176f blue:0.0745f alpha:1.0f];
UIFont* font = [UIFont fontWithName:#"Jungle Roar" size:BUTTON_FONT_SIZE];
NSString* playString = NSLocalizedString(#"Play", #"");
NSString* cameraString = NSLocalizedString(#"Camera", #"");
[self.buttonPlay setTitle:playString forState:UIControlStateNormal];
[self.buttonPlay setTitle:playString forState:UIControlStateHighlighted];
[self.buttonPlay setTitleColor:color forState:UIControlStateNormal];
[self.buttonPlay setTitleColor:color forState:UIControlStateHighlighted];
self.buttonPlay.titleLabel.font = font;
[self.buttonCamera setTitle:cameraString forState:UIControlStateNormal];
[self.buttonCamera setTitle:cameraString forState:UIControlStateHighlighted];
[self.buttonCamera setTitleColor:color forState:UIControlStateNormal];
[self.buttonCamera setTitleColor:color forState:UIControlStateHighlighted];
self.buttonCamera.titleLabel.font = font;
}
- (void) viewDidDisappear:(BOOL)animated
{
if ([self.view window] == nil)
{
self.view = nil;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
if ([self.view window] == nil)
{
self.view = nil;
}
}
- (IBAction)onButtonPlay:(id)sender
{
}
- (IBAction)onButtonCamera:(id)sender
{
}
EDIT: UIChooseAntViewController (as requested)
#interface SMChooseAntViewController ()
#property (strong, nonatomic) UIImageView* rope;
#property (strong, nonatomic) UIImageView* antFrontLayer;
#property (strong, nonatomic) UIImageView* antBackLayer;
#property (strong, nonatomic) NSArray* antFrontImages;
#property (strong, nonatomic) NSArray* antBackImages;
#property (strong, nonatomic) NSArray* antNameImages;
#property (strong, nonatomic) UIButton* leftButton;
#property (strong, nonatomic) UIButton* rightButton;
#property (strong, nonatomic) UIButton* confirmButton;
#property (nonatomic) NSUInteger selectedAntID;
#property (strong, nonatomic) UIImage* radioImageHighlighted;
#property (strong, nonatomic) UIImage* radioImage;
#property (strong, nonatomic) NSMutableArray* radioViews;
#property (weak, nonatomic) IBOutlet UILabel *antDescriptionLabel;
#property (weak, nonatomic) IBOutlet UIImageView *antDescriptionBG;
#property (strong, nonatomic) UIImageView* antNameView;
#property (strong, nonatomic) UISwipeGestureRecognizer* leftSwipeRecognizer;
#property (strong, nonatomic) UISwipeGestureRecognizer* rightSwipeRecognizer;
- (void) onArrowButton:(id)sender;
- (void) onConfirmButton:(id)sender;
- (void) respondToSwipe:(UISwipeGestureRecognizer*)recognizer;
#end
#implementation SMChooseAntViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
// Needed to come in between front and back player image layers
UIImage* ropeImage = [UIImage imageNamed:ROPE_IMAGE_PATH];
self.rope = [[UIImageView alloc] initWithImage:ropeImage];
self.rope.center = CGPointMake(screenSize.width / 2.0f, ropeImage.size.height / 2.0f);
UIColor* brownColor = [UIColor colorWithRed:0.2509f green:0.1176f blue:0.0745f alpha:1.0f];
self.antDescriptionLabel.textColor = brownColor;
self.antDescriptionLabel.numberOfLines = 0;
NSArray* antNames = [SMProfile antNames];
// Cache available Player Views in a NSArray
UIImage* frontImages[MAX_AVAILABLE_ANTS];
UIImage* backImages[MAX_AVAILABLE_ANTS];
UIImage* nameImages[MAX_AVAILABLE_ANTS];
for (NSUInteger i = 0; i < MAX_AVAILABLE_ANTS; ++i)
{
NSString* antName = [antNames objectAtIndex:i];
frontImages[i] = [SMImage imageNamed:[NSString stringWithFormat:#"%#_title_front.png", antName]];
backImages[i] = [SMImage imageNamed:[NSString stringWithFormat:#"%#_title_back.png", antName]];
nameImages[i] = [SMImage imageNamed:[NSString stringWithFormat:#"%#_name.png", antName]];
}
self.antFrontImages = [NSArray arrayWithObjects:frontImages[0], frontImages[1], frontImages[2], nil];
self.antBackImages = [NSArray arrayWithObjects:backImages[0], backImages[1], backImages[2], nil];
self.antNameImages = [NSArray arrayWithObjects:nameImages[0], nameImages[1], nameImages[2], nil];
// Load Selected player from profile
SMProfile* profile = [SMProfile mainProfile];
self.selectedAntID = profile.antID.unsignedIntegerValue;
self.antFrontLayer = [[UIImageView alloc] initWithImage:[self.antFrontImages objectAtIndex:self.selectedAntID]];
self.antBackLayer = [[UIImageView alloc] initWithImage:[self.antBackImages objectAtIndex:self.selectedAntID]];
self.antNameView = [[UIImageView alloc] initWithImage:[self.antNameImages objectAtIndex:self.selectedAntID]];
self.antNameView.center = CGPointMake(screenSize.width / 2.0f, self.antDescriptionBG.frame.origin.y);
NSString* antDescriptionKey = [NSString stringWithFormat:#"AntDescription%lu", (unsigned long)self.selectedAntID];
self.antDescriptionLabel.text = NSLocalizedString(antDescriptionKey, #"");
self.antDescriptionLabel.numberOfLines = 0;
self.antDescriptionLabel.adjustsFontSizeToFitWidth = YES;
self.antFrontLayer.center = CGPointMake(screenSize.width / 2.0f, ropeImage.size.height * 0.75f);
self.antBackLayer.center = self.antFrontLayer.center;
// Here a perform button creation, loading and positioning
// No blocks are being called
// add Target to buttons
[self.leftButton addTarget:self action:#selector(onArrowButton:) forControlEvents:UIControlEventTouchUpInside];
[self.rightButton addTarget:self action:#selector(onArrowButton:) forControlEvents:UIControlEventTouchUpInside];
[self.confirmButton addTarget:self action:#selector(onConfirmButton:) forControlEvents:UIControlEventTouchUpInside];
// Create and configure SwipeRecognizers
self.leftSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(respondToSwipe:)];
self.leftSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:self.leftSwipeRecognizer];
self.rightSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(respondToSwipe:)];
self.rightSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:self.rightSwipeRecognizer];
// Here a create a custom page control scheme. I load two radio button images
// create views and add them to the root view node.
// Add remaining view to the hierarchy
[self.view addSubview:self.antBackLayer];
[self.view addSubview:self.rope];
[self.view addSubview:self.antFrontLayer];
[self.view addSubview:self.confirmButton];
[self.view bringSubviewToFront:self.antDescriptionBG];
[self.view bringSubviewToFront:self.antDescriptionLabel];
[self.view addSubview:self.leftButton];
[self.view addSubview:self.rightButton];
[self.view addSubview:self.antNameView];
[self.view bringSubviewToFront:[self.radioViews objectAtIndex:0]];
}
- (void) viewDidDisappear:(BOOL)animated
{
if ([self.view window] == nil)
{
self.rope = nil;
self.antFrontLayer = nil;
self.antBackLayer = nil;
self.antFrontImages = nil;
self.antBackImages = nil;
self.antNameImages = nil;
self.leftButton = nil;
self.rightButton = nil;
self.confirmButton = nil;
self.radioImageHighlighted = nil;
self.radioImage = nil;
self.radioViews = nil;
self.antNameView = nil;
self.leftSwipeRecognizer = nil;
self.rightSwipeRecognizer = nil;
self.view = nil;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
if ([self.view window] == nil)
{
self.view = nil;
}
}
- (void)onArrowButton:(id)sender
{
UIButton* button = (UIButton*)sender;
NSInteger direction = button.tag;
// if on boundaries do nothing (first ant selected and swipe left or last ant selected and swipe right)
if ((self.selectedAntID == 0 && direction == -1) || (self.selectedAntID == (MAX_AVAILABLE_ANTS - 1) && direction == 1))
{
return;
}
// Update Radio Buttons. Unselect previous and select next.
UIImageView* currRadio = [self.radioViews objectAtIndex:self.selectedAntID];
currRadio.image = self.radioImage;
self.selectedAntID = (self.selectedAntID + MAX_AVAILABLE_ANTS + direction) % MAX_AVAILABLE_ANTS;
UIImageView* nextRadio = [self.radioViews objectAtIndex:self.selectedAntID];
nextRadio.image = self.radioImageHighlighted;
self.antFrontLayer.image = [self.antFrontImages objectAtIndex:self.selectedAntID];
self.antBackLayer.image = [self.antBackImages objectAtIndex:self.selectedAntID];
self.antNameView.image = [self.antNameImages objectAtIndex:self.selectedAntID];
// here I was issuing some block to perform the swipe animation for the ant image views. I commented them and I'm just replacing the images now (3 lines above)
}
- (void)onConfirmButton:(id)sender
{
// Save player choice to profile and perform segue
SMProfile* profile = [SMProfile mainProfile];
profile.antID = [NSNumber numberWithUnsignedInt:self.selectedAntID];
[profile save];
[self performSegueWithIdentifier:#"chooseAntToStageSelect" sender:self];
}
- (void) respondToSwipe:(UISwipeGestureRecognizer *)recognizer
{
// forward swipe to onArrowButton message
if (recognizer.direction == UISwipeGestureRecognizerDirectionLeft)
{
[self onArrowButton:self.rightButton];
}
else if (recognizer.direction == UISwipeGestureRecognizerDirectionRight)
{
[self onArrowButton:self.leftButton];
}
}
#end
When presenting B view controller from A, A will not release as A is the presentingViewController (please refer to the sdk doc).
Or if A,B are sub view controller of a navigation controller, A is store int he push stack which is not removed when pushing to B.
You are pushing a view controller on to a stack hence until the last one is not popped, the controller will not be released.
To go deep into dependencies on childs read the the article below.
Greatly explained, i am sure it'll help. :)
http://www.cocoawithlove.com/2009/07/rules-to-avoid-retain-cycles.html
I'm making a news reading app. I have a ArticleDetailPagingVC which functions as a paging controller. This has a UIScrollView with multiple ArticleDetailViewController's.
Inside the ArticleDetailViewController is a UIWebView which handles the articleText.
After changing some code I got a EXC_BAD_ACCESS when trying to inject a HTML string in the UIWebView. I eventually ended up looking for NSZombie's, which I found:
As seen in the screenshot the NSZombie points to setting the frame of the ArticleDetailViewController, which is not correct in my opinion.
If I comment out the line of code which injects the HTMLString to my UIWebView, the view is shown as it should, without any data in the UIWebView.
The WebView is created as an IBOutlet:
#property (nonatomic) IBOutlet UIWebView *webView;
Delegate is set to self (ArticleDetailViewController)
Also, its crashing before any of the UIWebView Delegate Methods are called.
I'm sure the problem is not:
The HTML String (it was working before & if I load a 'Hello world' string its crashing too)
MultiThreading (everything is handled on the mainthread for testing purposes)
I have no weak properties
I have 0 autoreleasepool's / CFRelease(object) in my code
I have no idea what could have been released too soon to create the crash
So my question is, how do you debug such a NSZombie? Or any other pointers are much appreciated.
PagingVC.h
#import <UIKit/UIKit.h>
#import "DDScrollViewController.h"
#import "ThumbArticle.h"
#import "NewsArticle.h"
#import "MBProgressHUD.h"
#import "DDScrollViewDelegate.h"
#interface ArticleDetailPagingVC : UIViewController <UIScrollViewDelegate,MBProgressHUDDelegate>
//View
#property (nonatomic) IBOutlet UIScrollView *scrollView;
//Data
#property (nonatomic) ThumbArticle *selectedThumbArticle;
#property (nonatomic) NewsArticle *selectedNewsArticle;
#property (nonatomic) int indexOfSelectedArticle;
#property (nonatomic) NSMutableArray *dataList;
#property (nonatomic) NSInteger selectedPage;
#property (nonatomic) BOOL dataSet;
#property (nonatomic) MBProgressHUD *mbProcess;
-(id)initWithDataList:(NSMutableArray*)dataList;
#end
PagingVC.m
#import "ArticleDetailPagingVC.h"
#import "ArticleDetailViewController.h"
#interface ArticleDetailPagingVC ()
-(void)setupView;
-(void)setupViewWithThumbArticles;
-(void)setupViewWithNewsArticles;
#end
#implementation ArticleDetailPagingVC
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
self.dataSet = NO;
}
return self;
}
-(id)initWithDataList:(NSMutableArray*)dataList
{
self = [super init];
if (self) {
self.dataSet = NO;
self.dataList = [NSMutableArray arrayWithArray:dataList];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (self.selectedThumbArticle) {
self.indexOfSelectedArticle = [self.dataList indexOfObject:self.selectedThumbArticle];
} else if (self.selectedNewsArticle) {
self.indexOfSelectedArticle = [self.dataList indexOfObject:self.selectedNewsArticle];
}
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self setupView];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark -
#pragma mark Custom Methods
-(void)setupView
{
if (self.dataList.count > 0) {
id object = [self.dataList objectAtIndex:0];
if ([object isKindOfClass:[NewsArticle class]]) {
} else if ([object isKindOfClass:[ThumbArticle class]]) {
ArticleDetailViewController *articleDetailVC = [[ArticleDetailViewController alloc] init];
articleDetailVC.selectedThumbArticle = [self.dataList objectAtIndex:self.indexOfSelectedArticle];
articleDetailVC.view.frame = CGRectMake(self.indexOfSelectedArticle * self.scrollView.frame.size.width, 0, self.scrollView.frame.size.width, self.scrollView.frame.size.height);
[self.scrollView addSubview:articleDetailVC.view];
//[articleDetailVC layoutViewWithThumbArticle:[self.dataList objectAtIndex:self.indexOfSelectedArticle]];
}
self.scrollView.contentSize = CGSizeMake(self.dataList.count * self.scrollView.frame.size.width, self.scrollView.frame.size.height);
[self.scrollView setContentOffset:CGPointMake(self.indexOfSelectedArticle * self.scrollView.frame.size.width, 0) animated:NO];
self.dataSet = YES;
}
}
-(void)setupViewWithThumbArticles
{
//Set the selected article first
/*
ArticleDetailViewController *articleDetailVC = [[ArticleDetailViewController alloc] init];
dispatch_async(dispatch_get_main_queue(), ^{
articleDetailVC.view.frame = CGRectMake(indexOfSelectedArticle * self.scrollView.frame.size.width, 0, self.scrollView.frame.size.width, self.scrollView.frame.size.height);
[self.scrollView addSubview:articleDetailVC.view];
});
[self.viewControllers replaceObjectAtIndex:indexOfSelectedArticle withObject:articleDetailVC];
[articleDetailVC layoutViewWithThumbArticle:[self.dataList objectAtIndex:indexOfSelectedArticle]];
//Then loop through the rest to add them to the scrollview
*/
int i = 0;
for (ThumbArticle *article in self.dataList) {
if (i != self.indexOfSelectedArticle) {
ArticleDetailViewController *articleDetailVC = [[ArticleDetailViewController alloc] init];
dispatch_async(dispatch_get_main_queue(), ^{
articleDetailVC.view.frame = CGRectMake(i * self.scrollView.frame.size.width, 0, self.scrollView.frame.size.width, self.scrollView.frame.size.height);
[self.scrollView addSubview:articleDetailVC.view];
});
//[self.viewControllers replaceObjectAtIndex:i withObject:articleDetailVC];
}
i++;
}
dispatch_async(dispatch_get_main_queue(), ^{
self.scrollView.contentSize = CGSizeMake(i * self.scrollView.frame.size.width, self.scrollView.frame.size.height);
[self.scrollView setContentOffset:CGPointMake(self.indexOfSelectedArticle * self.scrollView.frame.size.width, 0) animated:NO];
});
}
-(void)setupViewWithNewsArticles
{
int indexOfSelectedArticle = [self.dataList indexOfObject:self.selectedNewsArticle];
ArticleDetailViewController *articleDetailVC = [[ArticleDetailViewController alloc] init];
dispatch_async(dispatch_get_main_queue(), ^{
articleDetailVC.view.frame = CGRectMake(indexOfSelectedArticle * self.scrollView.frame.size.width, 0, self.scrollView.frame.size.width, self.scrollView.frame.size.height);
[self.scrollView addSubview:articleDetailVC.view];
});
//[self.viewControllers replaceObjectAtIndex:indexOfSelectedArticle withObject:articleDetailVC];
[articleDetailVC layoutViewWithNewsArticle:[self.dataList objectAtIndex:indexOfSelectedArticle]];
int i = 0;
for (NewsArticle *article in self.dataList) {
if (i != indexOfSelectedArticle) {
ArticleDetailViewController *articleDetailVC = [[ArticleDetailViewController alloc] init];
dispatch_async(dispatch_get_main_queue(), ^{
articleDetailVC.view.frame = CGRectMake(i * self.scrollView.frame.size.width, 0, self.scrollView.frame.size.width, self.scrollView.frame.size.height);
[self.scrollView addSubview:articleDetailVC.view];
});
//[self.viewControllers replaceObjectAtIndex:i withObject:articleDetailVC];
}
i++;
}
self.scrollView.contentSize = CGSizeMake(i * self.scrollView.frame.size.width, self.scrollView.frame.size.height);
[self.scrollView setContentOffset:CGPointMake(indexOfSelectedArticle * self.scrollView.frame.size.width, 0) animated:NO];
}
#pragma mark -
#pragma mark UIScrollView Delegate Methods
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (fmodf(scrollView.contentOffset.x, scrollView.frame.size.width) == 0) {
if (self.dataSet) {
self.selectedPage = scrollView.contentOffset.x / self.scrollView.frame.size.width;
ArticleDetailViewController *articleDetailVC = [[ArticleDetailViewController alloc] initWithNibName:#"ArticleDetailViewController" bundle:nil];
articleDetailVC.view.frame = CGRectMake(self.selectedPage * self.scrollView.frame.size.width, 0, self.scrollView.frame.size.width, self.scrollView.frame.size.height);
[self.scrollView addSubview:articleDetailVC.view];
[articleDetailVC layoutViewWithThumbArticle:[self.dataList objectAtIndex:self.selectedPage]];
}
}
}
#pragma mark -
#pragma mark MBProgressHUDDelegate methods
- (void)hudWasHidden
{
[self.mbProcess removeFromSuperview];
}
#end
ArticleDetailViewController.h
#import "DDViewController.h"
#import "ThumbArticle.h"
#import "NewsArticle.h"
#import "MBProgressHUD.h"
#import "DDAsyncParser+NewsArticles.h"
#interface ArticleDetailViewController : DDViewController <MBProgressHUDDelegate,UIWebViewDelegate,ParserDelegate>
//View
#property (nonatomic,strong) IBOutlet UIView *contentView;
#property (nonatomic) IBOutlet UIImageView *image;
#property (nonatomic) IBOutlet UILabel *labelCategory;
#property (nonatomic) IBOutlet UILabel *labelImgCaption;
#property (nonatomic) IBOutlet UILabel *labelEdition;
#property (nonatomic) IBOutlet UIWebView *webView;
#property (nonatomic) IBOutlet UIActivityIndicatorView *activity;
#property (nonatomic) NSUInteger textFontSize;
#property (nonatomic) MBProgressHUD *mbProcess;
//Data
#property (nonatomic) ThumbArticle *selectedThumbArticle;
ArticleDetailViewController.m
#import "ArticleDetailViewController.h"
#import "DDUtilities.h"
#import "DDUserDefaults.h"
#import "NewsArticle.h"
#import "DDFeedParser.h"
#interface ArticleDetailViewController ()
#property (nonatomic) NewsArticle *parsedNewsArticle;
-(void)loadData;
-(void)updateImageCaptionLabel;
-(void)populateWebView;
#end
#implementation ArticleDetailViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
[self.view addSubview:self.contentView];
((UIScrollView*)self.view).contentSize = self.contentView.frame.size;
self.dataSet = NO;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (!self.dataSet) {
[[DDAsyncParser sharedInstance] parseArticleWithXMLURL:self.selectedThumbArticle.articleXMLUrl delegate:self];
}
}
- (void)viewWillUnload
{
[self.webView setDelegate:nil];
[self.webView stopLoading];
}
- (void)viewWillDisappear:(BOOL)animated{
[self.webView setDelegate:nil];
[self.webView stopLoading];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark -
#pragma mark Public Methods
-(void)layoutViewWithThumbArticle:(ThumbArticle*)article
{
if (!self.dataSet) {
self.selectedThumbArticle = article;
[[DDAsyncParser sharedInstance] parseArticleWithXMLURL:self.selectedThumbArticle.articleXMLUrl delegate:self];
}
}
-(void)layoutViewWithNewsArticle:(NewsArticle*)article
{
if (!self.dataSet) {
self.parsedNewsArticle = article;
[self loadData];
}
}
#pragma mark -
#pragma mark Private Methods
-(void)loadData
{
self.labelCategory.text = self.parsedNewsArticle.articleCategory;
self.labelCategory.font = kCalibriBold14;
self.labelCategory.textColor = kGrayColor;
self.labelEdition.text = self.parsedNewsArticle.articleEdition;
self.labelEdition.font = kCalibriBold14;
self.labelEdition.textColor = kGrayColor;
[self updateImageCaptionLabel];
[self populateWebView];
self.dataSet = YES;
}
-(void)updateImageCaptionLabel
{
self.labelImgCaption.text = #"";
NSString *imgAuthor = #"";
if (self.parsedNewsArticle.articleImgAuthor.length != 0) {
imgAuthor = [NSString stringWithFormat:#"Foto: %#",self.parsedNewsArticle.articleImgAuthor];
}
NSString *imgCaption = #"";
if (self.parsedNewsArticle.articleImgDescription.length != 0) {
imgCaption = [NSString stringWithFormat:#"%# \n%#",self.parsedNewsArticle.articleImgDescription,imgAuthor];
} else {
imgCaption = imgAuthor;
}
self.labelImgCaption.text = imgCaption;
CGSize maximumLabelSize = CGSizeMake(296,9999);
CGSize expectedLabelSize = [imgCaption sizeWithFont:self.labelImgCaption.font
constrainedToSize:maximumLabelSize
lineBreakMode:self.labelImgCaption.lineBreakMode];
CGRect newFrame = self.labelImgCaption.frame;
newFrame.size.height = expectedLabelSize.height;
self.labelImgCaption.frame = newFrame;
}
-(void)populateWebView
{
NSString *htmlContentString = [DDUtilities createHTMLStringForArticleDetail:self.parsedNewsArticle];
[self.webView loadHTMLString:htmlContentString baseURL:nil];
}
-(void)checkSavedTextFontSize
{
self.textFontSize = [[DDUserDefaults getValueForKey:#"textFontSize"]integerValue];
if (self.textFontSize != 0) {
NSString *jsString = [[NSString alloc] initWithFormat:#"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '%d%%'",
self.textFontSize];
[self.webView stringByEvaluatingJavaScriptFromString:jsString];
} else {
self.textFontSize = 100;
}
}
#pragma mark -
#pragma mark UIWebView Delegate
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
if (navigationType == UIWebViewNavigationTypeLinkClicked)
{
[[UIApplication sharedApplication] openURL:[request URL]];
return NO;
}
return YES;
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
[self checkSavedTextFontSize];
CGRect frame = webView.frame;
frame.size.height = 1;
webView.frame = frame;
CGSize fittingSize = [webView sizeThatFits:CGSizeZero];
frame.size = fittingSize;
dispatch_async(dispatch_get_main_queue(), ^{
self.webView.frame = CGRectMake(frame.origin.x, self.labelImgCaption.frame.origin.y + self.labelImgCaption.frame.size.height + 5.0f, frame.size.width, frame.size.height);
self.contentView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.webView.frame.origin.y + self.webView.frame.size.height + 30.0f);
((UIScrollView*)self.view).contentSize = self.contentView.frame.size;
});
//[DDUtilities setImageView:self.image forLink:self.parsedNewsArticle.articleImgUrl placeholder:YES withActivityIndicator:self.activity];
}
-(void)webViewDidStartLoad:(UIWebView *)webView
{
}
-(void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
}
#pragma mark -
#pragma mark MBProgressHUDDelegate methods
- (void)hudWasHidden
{
[self.mbProcess removeFromSuperview];
}
#pragma mark -
#pragma mark ParserDelegate methods
-(void)didFinishWithObject:(id)object
{
self.parsedNewsArticle = object;
[self loadData];
}
You create a controller (which has a view). You assign the view as a subview of some other view. That's it. So, ARC will helpfully destroy your article detail controller that you aren't using any more. Anything that it has set itself as the delegate of (like a web view) will now crash when it tries to call the delegate.
Solution: store the article detail controller (add a strong property) so that it is retained while the view is on display.
Or, add the controller as a chile view controller.
I'm trying to implement an questionnaire app.
I have an UIPageViewController attached to the main view controller called QuizPageViewController.m. other controllers such as buttons are placed in content view controller called QuizContentViewController.m.
Now my question is how do I navigate pages of UIPageViewController programmatically from QuizContentViewController.m (eg. when buttonDoneClicked clicked)? I'm already aware of the fact that i can programmatically navigate pages using following command but my problem is I don't have access to its arguments from content view controller (QuizContentViewController).
setViewControllers:direction:animated:completion:.
following is my code.
QuizPageViewController.h
#import <UIKit/UIKit.h>
#import "QuizContentViewController.h"
#class QuizPageViewController;
#protocol QuizPageViewControllerDelegate <NSObject>
#optional // Delegate protocols
- (void)dismissReaderViewController:(QuizPageViewController *)viewController;
#end
#interface QuizPageViewController : UIViewController <UIPageViewControllerDataSource, QuizContentViewControllerDelegate>{
}
- (void)moveForward:(id)sender;
- (void)moveBackwards:(id)sender;
- (void)abort:(id)sender;
#property (nonatomic, unsafe_unretained, readwrite) id <QuizPageViewControllerDelegate> delegate;
#property (nonatomic, strong) UIPageViewController *pageView;
#end
QuizPageViewController.m
#import "QuizPageViewController.h"
#interface QuizPageViewController ()
#end
#implementation QuizPageViewController
#synthesize pageView, delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[self QuestionDet];
[super viewDidLoad];
// Do any additional setup after loading the view.
NSDictionary *option = [NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:UIPageViewControllerSpineLocationMin] forKey:UIPageViewControllerOptionSpineLocationKey];
pageView = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:option];
[pageView setDataSource:self];
QuizContentViewController *initialVC = [self viewControllerAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:initialVC];
[pageView setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
CGRect viewRect = self.view.bounds;
[[pageView view] setFrame:viewRect];
[self addChildViewController:self.pageView];
[self.view addSubview:[pageView view]];
[pageView didMoveToParentViewController:self];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger) indexOfViewController:(QuizContentViewController *)viewController{
return viewController.dataObjquizNo;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController{
int index = [self indexOfViewController:(QuizContentViewController *)viewController];
if (index == 0 || index == NSNotFound) {
return nil;
}
index --;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController{
int index = [self indexOfViewController:(QuizContentViewController *)viewController];
if (index == NSNotFound) {
return nil;
}
index++;
return [self viewControllerAtIndex:index];
}
- (QuizContentViewController *)viewControllerAtIndex:(NSInteger)index{
if (index > [quizdet count] - 1) {
return nil;
}
QuizContentViewController *cVC = [[QuizContentViewController alloc] init];
cVc.delegate = self;
Questions *quizz = [quizdet objectAtIndex:index];
[cVC setDataObjQuiz:[quizz quiz]];
[cVC setDataObjAns1:[quizz answer1]];
[cVC setDataObjAns2:[quizz answer2]];
[cVC setDataObjAns3:[quizz answer3]];
[cVC setDataObjAns4:[quizz answer4]];
[cVC setDataObjquizNo:index];
[cVC setDataObjtotalNoOfQuiz:[quizdet count]];
return cVC;
}
- (void)moveForward:(id)sender{
// Navigation forward code should goes here...
}
- (void)moveBackwards:(id)sender{
// Navigation backwards code should goes here...
}
- (void)abort:(id)sender{
[delegate dismissReaderViewController:self];
}
}
QuizContentViewController.h
#import <UIKit/UIKit.h>
#class QuizContentViewController;
#protocol QuizContentViewControllerDelegate <NSObject>
- (void)moveForward:(id)sender;
- (void)moveBackwards:(id)sender;
- (void)abort:(id)sender;
#end
#interface QuizContentViewController : UIViewController{
UITextView *txtVwQuiz;
UILabel *lblSummery;
NSString *dataObjQuiz;
NSString *dataObjAns1;
NSString *dataObjAns2;
NSString *dataObjAns3;
NSString *dataObjAns4;
NSInteger dataObjquizNo;
NSInteger dataObjtotalNoOfQuiz;
}
#property(nonatomic, unsafe_unretained, readwrite) id <QuizContentViewControllerDelegate> delegate;
#property (nonatomic, strong) NSString *dataObjQuiz;
#property (nonatomic, strong) NSString *dataObjAns1;
#property (nonatomic, strong) NSString *dataObjAns2;
#property (nonatomic, strong) NSString *dataObjAns3;
#property (nonatomic, strong) NSString *dataObjAns4;
#property NSInteger dataObjquizNo;
#property NSInteger dataObjtotalNoOfQuiz;
#end
QuizContentViewController.m
#import "QuizContentViewController.h
#import <QuartzCore/QuartzCore.h>
#define isPhone568 ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone && [UIScreen mainScreen].bounds.size.height == 568)
#define iPhone568ImageNamed(image) (isPhone568 ? [NSString stringWithFormat:#"%#-568h.%#", [image stringByDeletingPathExtension], [image pathExtension]] : image)
#define iPhone568Image(image) ([UIImage imageNamed:iPhone568ImageNamed(image)])
# define IS_IPAD UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone
#interface QuizContentViewController ()
#end
#implementation QuizContentViewController
#synthesize dataObjQuiz, dataObjAns1, dataObjAns2, dataObjAns3, dataObjAns4, dataObjquizNo, dataObjtotalNoOfQuiz, delegate;
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor clearColor]; // Transparent
UIImageView *backgroundImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:iPhone568ImageNamed(#"QuizPage.png")]];
[self.view addSubview:backgroundImage];
[self.view sendSubviewToBack:backgroundImage];
// Do any additional setup after loading the view.
// Override point for customization after application launch
CGSize viewSize = self.view.bounds.size;
float boader;
float heightofSubVw;
boader = IS_IPAD?15:37;
heightofSubVw = viewSize.height/10;
lblSummery = [[UILabel alloc] initWithFrame:CGRectMake(boader, 5, viewSize.width-(2*boader), heightofSubVw*1)];
[lblSummery setBackgroundColor:[UIColor clearColor]];
[lblSummery setTextAlignment:UITextAlignmentLeft];
[lblSummery setFont:[UIFont systemFontOfSize:IS_IPAD?14.f:28.f]];
[lblSummery setText:[NSString stringWithFormat:#" Question %d out of %d.",dataObjquizNo+1, dataObjtotalNoOfQuiz]];
[lblSummery setTextColor:[UIColor orangeColor]];
[self.view addSubview:lblSummery];
txtVwQuiz = [[UITextView alloc] initWithFrame:CGRectMake(boader, heightofSubVw, viewSize.width-(2*boader), heightofSubVw*4)];
[txtVwQuiz setText:dataObjQuiz];
[txtVwQuiz setBackgroundColor:[UIColor clearColor]];
[txtVwQuiz setTextAlignment:UITextAlignmentLeft];
[txtVwQuiz setTextColor:[UIColor whiteColor]];
[txtVwQuiz setFont:[UIFont systemFontOfSize:IS_IPAD?15.f:30.f]];
[txtVwQuiz setEditable:NO];
[self.view addSubview:txtVwQuiz];
NSArray *options =[[NSArray alloc]
initWithObjects:dataObjAns1,dataObjAns2,dataObjAns3,dataObjAns4,nil];
MIRadioButtonGroup *group =[[MIRadioButtonGroup alloc]
initWithFrame:CGRectMake(boader, heightofSubVw*4, viewSize.width-(2*boader), heightofSubVw*5)
andOptions:options andColumns:1];
[self.view addSubview:group];
UIButton *btnBack = [[UIButton alloc] initWithFrame:CGRectMake(boader+(IS_IPAD?6:12), heightofSubVw*8.5, IS_IPAD?44:88, IS_IPAD?44:88)];
[btnBack addTarget:self action:
#selector(navButtonBackClicked:)
forControlEvents:UIControlEventTouchUpInside];
btnBack.contentHorizontalAlignment =
UIControlContentHorizontalAlignmentCenter;
[btnBack setBackgroundImage:[UIImage imageNamed:
#"Navigation_Back.png"] forState:UIControlStateNormal];
[self.view addSubview:btnBack];
UIButton *btnDone = [[UIButton alloc] initWithFrame:CGRectMake(viewSize.width - (boader+(IS_IPAD?10 + 44 :20 + 88)), heightofSubVw*8.5, IS_IPAD?44:88, IS_IPAD?44:88)];
[btnDone addTarget:self action:
#selector(navButtonDoneClicked:)
forControlEvents:UIControlEventTouchUpInside];
btnDone.contentHorizontalAlignment =
UIControlContentHorizontalAlignmentCenter;
[btnDone setBackgroundImage:[UIImage imageNamed:
#"Navigation_Done.png"] forState:UIControlStateNormal];
[self.view addSubview:btnDone];
UIButton *btnAbort = [[UIButton alloc] initWithFrame:CGRectMake(viewSize.width - (boader+(IS_IPAD?2*10 + 88:2*20 + 176)), heightofSubVw*8.5, IS_IPAD?44:88, IS_IPAD?44:88)];
[btnAbort addTarget:self action:
#selector(navButtonAbortClicked:)
forControlEvents:UIControlEventTouchUpInside];
btnAbort.contentHorizontalAlignment =
UIControlContentHorizontalAlignmentCenter;
[btnAbort setBackgroundImage:[UIImage imageNamed:
#"Navigation_Abort.png"] forState:UIControlStateNormal];
[self.view addSubview:btnAbort];
}
-(IBAction) navButtonDoneClicked:(UIButton *) sender{
id index;
index = [NSNumber numberWithInt:dataObjquizNo];
if ([delegate respondsToSelector:#selector(moveForward:)]) {
[delegate moveForward:index];
}
}
-(IBAction) navButtonAbortClicked:(UIButton *) sender{
id index;
index = [NSNumber numberWithInt:dataObjquizNo];
if ([self.delegate respondsToSelector:#selector(abort:)]) {
[self.delegate abort:index];
}
}
-(IBAction) navButtonBackClicked:(UIButton *) sender{
id index;
index = [NSNumber numberWithInt:dataObjquizNo];
if ([delegate respondsToSelector:#selector(moveBackwards:)]) {
[delegate moveBackwards:index];
}
}
If I understand your question correctly, the QuizContentViewController's view has buttons that are pressed and once pressed it needs to notify your mainviewcontroller so it can set the view appropriately on QuizPageViewController? If this is the case, protocols are a great way for viewcontrollers to send messages to other viewcontrollers. Here is apple documentation about protocols: Working with Protocols.
Protocols are a little tricky the first time you use them. Once everything is set-up correctly you should be able to do something like this:
-(void)buttonBackClicked:(id)sender
{
[self.delegate shouldMoveBack];
}