I am a new objective-c developer. The purpose of the app being developed is to switch between 3 graphs each displaying scientific data. When the user drags their finger on these graphs, the data for that point is displayed. Currently, to switch between these three graphs, the UIScreenEdgePanGestureRecognizer was used. However, since Apple has got rid of this feature in recent updates, I want to use segmented controls to switch between the three graphs. I have been able to get the segmented controls to appear, however, I have not been able to get them to actually get the graphs to switch. I have attached the relavant parts of the (ORIGINAL) viewcontroller.m below. How would I go about this? Thanks.
For reference, the names of the three graphs are ts, ph, and pv.
- (void)viewDidLoad
{
[super viewDidLoad];
/*
// Show/hide nav bar
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(doubleTap)];
[tap setNumberOfTapsRequired:2];
[self.view addGestureRecognizer:tap];
*/
touchHasRegistered = NO;
allowQualityScrubbing = NO;
shouldFineTune = 0;
hasFineTuned = NO;
[[UIApplication sharedApplication] setStatusBarHidden:YES];
[self.navigationController setNavigationBarHidden:YES];
[self.containerView addSubview:self.chartView];
[self.view insertSubview:self.secondContainerView
aboveSubview:self.containerView];
[self.view insertSubview:self.infoView
aboveSubview:self.secondContainerView];
[self.containerView bringSubviewToFront:self.infoButton];
[self.view setBackgroundColor:[UIColor whiteColor]];
[self.chartView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.containerView);
}];
[self.infoView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.containerView);
}];
if (self.secondContainerView.superview != nil && self.chartView.image != nil) {
[self.secondContainerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.containerView).with.offset(20.0);
make.top.equalTo(self.containerView).with.offset(20.0);
make.height.equalTo([NSNumber numberWithFloat:self.secondContainerView.frame.size.height]);
make.width.equalTo([NSNumber numberWithFloat:self.secondContainerView.frame.size.width]);
}];
}
[self.secondContainerView addSubview:self.displayView];
[self chooseNewFileWithChartType:self.chartView.chart.substanceType valueType:#"ts"];
UIScreenEdgePanGestureRecognizer *rightRecog = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self
action:#selector(resetChart:)];
[rightRecog setEdges:UIRectEdgeRight];
[rightRecog setCancelsTouchesInView:YES];
[self.chartView addGestureRecognizer:rightRecog];
UIScreenEdgePanGestureRecognizer *leftRecog = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self
action:#selector(resetChart:)];
[leftRecog setEdges:UIRectEdgeLeft];
[leftRecog setCancelsTouchesInView:YES];
[self.chartView addGestureRecognizer:leftRecog];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.view addSubview:self.popupView];
[self.popupView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(#(self.popupView.frame.size.height));
make.width.equalTo(#(self.popupView.frame.size.width));
make.center.equalTo(self.view);
}];
/*
// Add Adjuster Views
NSSet *tags = [self tagsForAdjusterViews];
CGFloat height = self.displayView.containerViewHeight/self.displayView.numberOfRows;
for (id tag in tags) {
RUAAdjusterView *adjusterView = [[RUAAdjusterView alloc] initWithFrame:CGRectZero
tag:[(NSNumber *)tag integerValue]];
adjusterView.delegate = self;
[adjusterView setBackgroundColor:[UIColor clearColor]];
[self.secondContainerView addSubview:adjusterView];
[self.secondContainerView bringSubviewToFront:adjusterView];
[adjusterView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.secondContainerView);
make.right.equalTo(self.secondContainerView);
make.top.equalTo([NSNumber numberWithFloat:(height*([(NSNumber *)tag floatValue] - 1) + self.displayView.containerViewOriginY + 2.0f)]);
make.height.equalTo([NSNumber numberWithFloat:height - 4.0f]);
}];
}
*/
}
- (NSSet *)tagsForAdjusterViews
{
return [NSSet setWithObjects:#1, #2, #6, #7, nil];
}
- (BOOL)prefersStatusBarHidden
{
return YES;
}
#pragma mark - Lazy Init
- (LocationIndicatorImageView *)chartView
{
if (!_chartView) {
_chartView = (LocationIndicatorImageView *)[[LocationIndicatorImageView alloc] initWithFrame:self.containerView.frame
image:[UIImage imageNamed:#"Water_ts_chart.png"]
sender:self];
[_chartView setChart:[RUChart chartWithChartType:#"ts"]];
}
return _chartView;
}
-(UIView *)displayView
{
if (!_displayView) {
_displayView = [[DisplayView alloc] initWithFrame:self.secondContainerView.frame];
[_displayView setDataSource:self];
}
return _displayView;
}
-(UIView *)secondContainerView
{
if (!_secondContainerView) {
CGFloat height = 343.0f;
CGFloat width = 225.0f;
_secondContainerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, height)];
}
return _secondContainerView;
}
-(UIImageView *)infoView
{
if (!_infoView) {
_infoView = [[UIImageView alloc] initWithFrame:CGRectZero];
[_infoView setImage:[UIImage imageNamed:#"Legend.png"]];
[_infoView setHidden:YES];
[_infoView setUserInteractionEnabled:NO];
[_infoView setBackgroundColor:[UIColor whiteColor]];
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(30, 30, 310, 310)];
UITapGestureRecognizer *ytTap = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(showYoutubeVideo)];
[ytTap setNumberOfTapsRequired:1];
[container setUserInteractionEnabled:YES];
[container addGestureRecognizer:ytTap];
[_infoView addSubview:container];
UIImageView *youtube = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 60, 60)];
[youtube setImage:[UIImage imageNamed:#"youtube.png"]];
UITextView *textView1 = [[UITextView alloc] initWithFrame:CGRectMake(youtube.frame.origin.x + youtube.frame.size.width,
youtube.frame.origin.y,
250,
youtube.frame.size.height/2.0)];
UITextView *textView2 = [[UITextView alloc] initWithFrame:CGRectMake(youtube.frame.origin.x + youtube.frame.size.width,
youtube.frame.origin.y + youtube.frame.size.height/2.0,
250,
youtube.frame.size.height/2.0)];
UIFont *font = [UIFont fontWithName:#"HelveticaNeue-Light" size:16.0];
[textView1 setText:#"Learn about Thermodynamic"];
[textView1 setFont:font];
[textView1 setTextContainerInset:UIEdgeInsetsMake(11.0, 4.0, 4.0, 0.0)];
[textView1 setUserInteractionEnabled:NO];
[textView2 setText:#"Properties of Water"];
[textView2 setFont:font];
[textView2 setTextContainerInset:UIEdgeInsetsMake(0.0, 4.0, 0.0, 0.0)];
[textView2 setUserInteractionEnabled:NO];
[container addSubview:youtube];
[container addSubview:textView1];
[container addSubview:textView2];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(dismissInfo)];
[tap setNumberOfTapsRequired:1];
[_infoView addGestureRecognizer:tap];
}
return _infoView;
}
- (void)showYoutubeVideo
{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"http://www.youtube.com/watch?v=rJR-6OEw09k"]
options:#{}
completionHandler:nil];
}
- (H2O_Wagner_Pruss *)wagPruss
{
if (!_wagPruss) {
_wagPruss = [[H2O_Wagner_Pruss alloc] initEOS];
}
return _wagPruss;
}
- (NSArray *)superheatedValues
{
if (!_superheatedValues) {
_superheatedValues = [[NSArray alloc] init];
}
return _superheatedValues;
}
- (NSArray *)superheatedKeys
{
if (!_superheatedKeys) {
_superheatedKeys = [[NSArray alloc] init];
}
return _superheatedKeys;
}
- (NSArray *)chartValueTypes
{
if (!_chartValueTypes) {
_chartValueTypes = [NSArray arrayWithObjects:#"ts",#"ph",#"pv", nil];
}
return _chartValueTypes;
}
- (RUAPopupView *)popupView
{
if (!_popupView) {
_popupView = [[RUAPopupView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 200.0f, 160.0f) text:#"t-s"];
}
return _popupView;
}
- (RUASpaceController *)spaceController
{
if (!_spaceController) {
_spaceController = [[RUASpaceController alloc] init];
// NOTE: Seems like (10/8.0 and 10/9.0) and 20/30.0 felt best of ones I tried. Could use some refining.
_spaceController.numPoints = 10;
_spaceController.maxDiff = 7.0;
}
return _spaceController;
}
#pragma mark - Gesture Selectors
- (IBAction)displayInfo:(id)sender {
[self.infoView setHidden:NO];
[self.infoView setUserInteractionEnabled:YES];
}
-(void)dismissInfo
{
[self.infoView setHidden:YES];
[self.infoView setUserInteractionEnabled:NO];
}
-(void)doubleTap
{
[self.popupView showHideAnimated:YES];
/*
if (self.navigationController.isNavigationBarHidden) {
[self.navigationController setNavigationBarHidden:NO animated:YES];
[[UIApplication sharedApplication] setStatusBarHidden:NO];
} else {
[self.navigationController setNavigationBarHidden:YES animated:YES];
[[UIApplication sharedApplication] setStatusBarHidden:YES];
}
*/
}
- (void)resetChart:(UIScreenEdgePanGestureRecognizer *)recog
{
[self.popupView.layer removeAllAnimations];
if (recog.state == UIGestureRecognizerStateEnded) {
NSInteger index = [self.chartValueTypes indexOfObject:self.chartView.chart.valueType];
NSLog(#"%#, %#", self.chartValueTypes[((index+1)+3)%3], self.chartValueTypes[((index-1)+3)%3]);
NSString *type;
if (recog.edges == UIRectEdgeRight) {
type = self.chartValueTypes[((index+1)+3)%3];
} else if (recog.edges == UIRectEdgeLeft) {
type = self.chartValueTypes[((index-1)+3)%3];
}
NSString *letter1 = [type substringToIndex:1];
NSString *letter2 = [type substringFromIndex:1];
NSString *displayName = [NSString stringWithFormat:#"%#-%#",letter1.uppercaseString,letter2];
self.popupView.text = displayName;
[self.chartView resetImage:[UIImage imageNamed:[NSString stringWithFormat:#"Water_%#_chart.png",type]]];
self.chartView.chart = [RUChart chartWithChartType:type];
[self inspectInfoButtonWithChartValueType:type];
[self chooseNewFileWithChartType:self.chartView.chart.substanceType valueType:type];
[self.secondContainerView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.containerView).with.offset(20.0);
make.height.equalTo([NSNumber numberWithFloat:self.secondContainerView.frame.size.height]);
make.width.equalTo([NSNumber numberWithFloat:self.secondContainerView.frame.size.width]);
}];
if (self.chartView.chart.displayPosition == RUChartDisplayPositionLeft) {
[self.secondContainerView mas_updateConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.containerView).with.offset(20.0);
}];
} else if (self.chartView.chart.displayPosition == RUChartDisplayPositionRight) {
[self.secondContainerView mas_updateConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.containerView).with.offset(-20.0);
}];
}
[self.popupView showHideAnimated:YES];
if (touchHasRegistered) {
if ([self.chartView.chart.valueType isEqualToString:#"ph"]) {
if ([self.chartView pointIsWithinBoundsForPrimaryAxisValue:currentEnthalpy secondaryAxisValue:currentPressure]) {
[self.chartView moveMarkerToPrimaryAxisValue:currentEnthalpy
secondaryAxisValue:currentPressure];
} else {
[self.chartView removeMarker];
}
} else if ([self.chartView.chart.valueType isEqualToString:#"pv"]) {
if ([self.chartView pointIsWithinBoundsForPrimaryAxisValue:currentSpecVolume secondaryAxisValue:currentPressure]) {
[self.chartView moveMarkerToPrimaryAxisValue:currentSpecVolume
secondaryAxisValue:currentPressure];
} else {
[self.chartView removeMarker];
}
} else if ([self.chartView.chart.valueType isEqualToString:#"ts"]) {
if ([self.chartView pointIsWithinBoundsForPrimaryAxisValue:currentEntropy secondaryAxisValue:currentTemp]) {
[self.chartView moveMarkerToPrimaryAxisValue:currentEntropy
secondaryAxisValue:currentTemp];
} else {
[self.chartView removeMarker];
}
} else {
touchHasRegistered = NO;
[self.chartView removeMarker];
}
}
}
}
UISegmentedControl look like a lot to setup, but actually is not.
The following code just shows more detailed control in color and label positions inside the segments. If there is no further use somewhere else, there is no property needed to hold UISegmentedControl *. Call this once in -initWithFrame: or -viewDidLoad.
- (void)setupSegmentCtrl {
UISegmentedControl *segmentedCtrl = [[UISegmentedControl alloc] initWithItems:#[#"A",#"B",#"C"]];
segmentedCtrl.momentary = YES;
NSUInteger segItems = segmentedCtrl.numberOfSegments;
segmentedCtrl.frame = CGRectMake(0, 0, 60*segItems, 40);
// sorry - funny color scheme used to demonstrate
segmentedCtrl.tintColor = UIColor.orangeColor;
//segmentedCtrl.backgroundColor = UIColor.clearColor;
UIColor *dark = [UIColor colorWithWhite:0.5 alpha:0.5];
[segmentedCtrl setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:dark,NSForegroundColorAttributeName, [UIFont fontWithName:#"HelveticaNeue-Light" size:16.0],NSFontAttributeName, nil] forState:UIControlStateNormal];
[segmentedCtrl setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:UIColor.redColor, NSForegroundColorAttributeName, nil] forState:UIControlStateSelected];
[segmentedCtrl setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:UIColor.greenColor, NSForegroundColorAttributeName, nil] forState:UIControlStateHighlighted];
// you can move the segments labels about some pixels with the following..
//[segmentedCtrl setContentPositionAdjustment:UIOffsetMake(-1, -2) forSegmentType:UISegmentedControlSegmentAny barMetrics:UIBarMetricsDefault];
// manually set an active index, .. as default
[segmentedCtrl setSelectedSegmentIndex:0];
[self.view addSubview:segmentedCtrl];
// next lines work for all UIControls, setting a target and action manually.
// there are a lot UIControlEvent to choose/combine from available
[segmentedCtrl addTarget:self action:#selector(segmentSelectedAction:) forControlEvents:UIControlEventValueChanged];
}
and as defined you will want a method that takes action when you touch the segments.
-(void)segmentSelectedAction:(UISegmentedControl *)seg {
NSLog(#"selectedSegmentIndex=%d", seg.selectedSegmentIndex);
// what ever you gonna do with the seg.selectedSegmentIndex
}
and maybe good to know when using UISegmentedControl, when going in dark mode it has different color scheme for the background. So switch and test how it looks like.
If you have to change layout cause of device rotates, you will have to expose UISegmentedControl *segmentCtrl as property or class variable and change frame and so on in -layoutSubviews to your needs.
A last word to UIViews and UIGestureRecognizers. Sometime its much more practical to write your own UIView subclass and allocate that instead. Then you are able to use the following methods inside your subclass to catch touches directly.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
for (UITouch *touch in touches) {
NSLog(#"touchesBegan= %#",touch.description);
}
}
// and the other possible..
-(void)touchesMoved:withEvent:
-(void)touchesEnded:withEvent:
-(void)touchesCancelled:withEvent:
Related
So I have a UISwitch that backgroundColor already set to clearColor in tableViewCell.m :
- (instancetype)initWithStyle:(UITableViewCellStyle)style
reuseIdentifier:(NSString *)reuseIdentifier
{
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])
{
self.selectionStyle = UITableViewCellSelectionStyleNone;
self.backgroundColor = [UIColor colorWithHexString:#"#333333"];
[self initUI];
}
return self;
}
- (void)initUI {
[self addSubview:self.topLine];
[self addSubview:self.imgView];
[self addSubview:self.titleLab];
[self addSubview:self.rightView];
[self addSubview:self.rightSwitch];
[self addSubview:self.cellLine];
[self addSubview:self.bottomLine];
}
- (UISwitch *)rightSwitch {
if (!_rightSwitch) {
self.rightSwitch = [[UISwitch alloc] init];
self.rightSwitch.frame = CGRectMake(253*kScaleXAndWidth, 8*kScaleYAndHeight, 51*kScaleXAndWidth, 31*kScaleYAndHeight);
self.rightSwitch.hidden = YES;
[self.rightSwitch setBackgroundColor:[UIColor clearColor]];
[self.rightSwitch addTarget:self action:#selector(rightSwitchClick:) forControlEvents:UIControlEventTouchUpInside];
}
return _rightSwitch;
}
rightSwitchClick is a block, then in cellForRowAtIndexPath TableViewController.m :
QuickLoginCell *cell = [tableView dequeueReusableCellWithIdentifier:QuickLoginCellID forIndexPath:indexPath];
cell.rightView.hidden = YES;
cell.rightSwitch.hidden = NO;
__block QuickLoginCell *blockCell = cell;
if (isIDlogin) {
[cell.rightSwitch setEnabled:NO];
}
else{
[cell.rightSwitch setEnabled:YES];
}
cell.rightSwitch.on = NO;
cell.bottomLine.hidden = NO;
if (![BetwayUtils isEmptyString:patternLock]) {
cell.rightSwitch.on = YES;
cell.bottomLine.hidden = YES;
}
[cell.imgView setImage:[UIImage imageNamed:#"ic_patternLock"]];
cell.rightSwitchAddClick = ^{
if (blockCell.rightSwitch.on) {
PatternLockViewController *vc = [PatternLockViewController new];
[strongSelf.navigationController pushViewController:vc animated:YES];
}
else{
}
};
so when turn on it will directly go to PatternLockViewController, and after I have set the patternLock it will pop to TableViewController again and the switch will be turned on now. the problem is when I try to switch it off the backgroundColor suddenly change to white like this :
When I remove :
PatternLockViewController *vc = [PatternLockViewController new];
[strongSelf.navigationController pushViewController:vc animated:YES];
so inside the block there is no code and the UISwitch backgroundColor is clearColor, I tried to switch on and off and it works as it supposed to be. so i am a little bit confused on this matter since I dont set UISwitch backgroundColor to white anywhere.
UPDATE
Already tried using delegate to refresh table when pop from patternlockviewcontroller but still no avail
I solve it using :
- (void)prepareForReuse {
[super prepareForReuse];
[self.rightSwitch setBackgroundColor:[UIColor clearColor]];
[self.rightSwitch setTintColor:[UIColor whiteColor]];
[self.rightSwitch setThumbTintColor:[UIColor whiteColor]];
}
on my tableViewCell.m I hope it will help someone here.
I want to implement slide menu in my iOS app like drawer (Andriod). I went through a tutorial, but all of them are using third party libraries. Is there any possibility to create a custom slide menu. I tried to create it with the following code, but it's only working with xib file:
- (IBAction)sidemenu:(id)sender
{
[UIView animateWithDuration:0.50f animations:^{
view.frame = self.view.frame;
} completion:^(BOOL finished) {
swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(SwipGestureLeftAction:)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipeLeft];
}];
}
- (void)SwipGestureAction
{
UISwipeGestureRecognizer *swiperight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(SwipGestureRightAction:)];
swiperight.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:swiperight];
}
#pragma mark AddSwipeGestureLeftAndRight
- (void)SwipGestureRightAction:(UISwipeGestureRecognizer *)swipeRight
{
[UIView animateWithDuration:0.50f animations:^{
view.frame = self.view.frame;
} completion:^(BOOL finished) {
swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(SwipGestureLeftAction:)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipeLeft];
}];
}
- (void)SwipGestureLeftAction:(UISwipeGestureRecognizer *)swipeRight
{
[UIView animateWithDuration:0.50f animations:^{
[view setFrame:CGRectMake(self.view.frame.origin.x - self.view.frame.size.width, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height)];
} completion:^(BOOL finished){
[self.view removeGestureRecognizer:swipeLeft];
}];
}
Here is what I have for you:
I made a super class for all my slide menus in all projects. It manages the showing and hiding of the slide menu, and handles orientation changes. It slides in from left on top of the current view, and it partially obscures the remainder of the view with a dark transparent background.
If you ever need other behaviour (like pushing out the current view) just override the animation part.
My slide menu is a Singleton because in our applications we only use one slide menu on every screen.
#import <UIKit/UIKit.h>
#interface IS_SlideMenu_View : UIView <UIGestureRecognizerDelegate>
{
UIView* transparentBgView;
BOOL hidden;
int lastOrientation;
}
#property (strong, nonatomic) UIView *menuContainerV;
+ (id)sharedInstance;
- (BOOL)isShown;
- (void)hideSlideMenu;
- (void)showSlideMenu;
#end
#import "IS_SlideMenu_View.h"
#implementation IS_SlideMenu_View
+ (id)sharedInstance
{
static id _sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[[self class] alloc] init];
});
return _sharedInstance;
}
- (instancetype)initWithFrame:(CGRect)frame
{
frame = [[[UIApplication sharedApplication] delegate] window].frame;
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
transparentBgView = [[UIView alloc] initWithFrame:frame];
[transparentBgView setBackgroundColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:0.6]];
[transparentBgView setAlpha:0];
transparentBgView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(gestureRecognized:)];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(gestureRecognized:)];
[transparentBgView addGestureRecognizer:tap];
[transparentBgView addGestureRecognizer:pan];
[self addSubview:transparentBgView];
frame.size.width = 280;
self.menuContainerV = [[UIView alloc] initWithFrame:frame];
CALayer *l = self.menuContainerV.layer;
l.shadowColor = [UIColor blackColor].CGColor;
l.shadowOffset = CGSizeMake(10, 0);
l.shadowOpacity = 1;
l.masksToBounds = NO;
l.shadowRadius = 10;
self.menuContainerV.autoresizingMask = UIViewAutoresizingFlexibleHeight;
[self addSubview: self.menuContainerV];
hidden = YES;
}
//----- SETUP DEVICE ORIENTATION CHANGE NOTIFICATION -----
UIDevice *device = [UIDevice currentDevice];
[device beginGeneratingDeviceOrientationNotifications];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:device];
lastOrientation = [[UIDevice currentDevice] orientation];
return self;
}
//********** ORIENTATION CHANGED **********
- (void)orientationChanged:(NSNotification *)note
{
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if(orientation == UIDeviceOrientationPortrait || orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight){
NSLog(#"%ld",orientation);
if(!hidden && lastOrientation != orientation){
[self hideSlideMenu];
hidden = YES;
lastOrientation = orientation;
}
}
}
- (void)showSlideMenu {
UIWindow* window = [[[UIApplication sharedApplication] delegate] window];
self.frame = CGRectMake(0, 0, window.frame.size.width, window.frame.size.height);
[self.menuContainerV setTransform:CGAffineTransformMakeTranslation(-window.frame.size.width, 0)];
[window addSubview:self];
// [[UIApplication sharedApplication] setStatusBarHidden:YES];
[UIView animateWithDuration:0.5 animations:^{
[self.menuContainerV setTransform:CGAffineTransformIdentity];
[transparentBgView setAlpha:1];
} completion:^(BOOL finished) {
NSLog(#"Show complete!");
hidden = NO;
}];
}
- (void)gestureRecognized:(UIGestureRecognizer *)recognizer
{
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
[self hideSlideMenu];
} else if ([recognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
static CGFloat startX;
if (recognizer.state == UIGestureRecognizerStateBegan) {
startX = [recognizer locationInView:self.window].x;
} else
if (recognizer.state == UIGestureRecognizerStateChanged) {
CGFloat touchLocX = [recognizer locationInView:self.window].x;
if (touchLocX < startX) {
[self.menuContainerV setTransform:CGAffineTransformMakeTranslation(touchLocX - startX, 0)];
}
} else
if (recognizer.state == UIGestureRecognizerStateEnded) {
[self hideSlideMenu];
}
}
}
- (void)hideSlideMenu
{
UIWindow* window = [[[UIApplication sharedApplication] delegate] window];
window.backgroundColor = [UIColor clearColor];
[UIView animateWithDuration:0.5 animations:^{
[self.menuContainerV setTransform:CGAffineTransformMakeTranslation(-self.window.frame.size.width, 0)];
[transparentBgView setAlpha:0];
} completion:^(BOOL finished) {
[self removeFromSuperview];
[self.menuContainerV setTransform:CGAffineTransformIdentity];
// [[UIApplication sharedApplication] setStatusBarHidden:NO];
hidden = YES;
NSLog(#"Hide complete!");
}];
}
- (BOOL)isShown
{
return !hidden;
}
#end
Subclasses only need to add subviews to the menuContainerV view, and manage them.
An example:
I created a subclass that has an header view and a table view as its content. I created the content view in a xib, and the owner of the xib is this subclass. This way I can bind outlets to the xib.
#import "IS_SlideMenu_View.h"
#interface CC_SlideMenu_View : IS_SlideMenu_View<UITableViewDelegate, UITableViewDataSource>
#property (weak, nonatomic) IBOutlet UIView *headerView;
#property (weak, nonatomic) IBOutlet UITableView *tableView;
...
#end
When the slide menu gets instantiated I load the xib and add the content view to the menuContainerV view.
#import "CC_SlideMenu_View.h"
#implementation CC_SlideMenu_View
- (instancetype)init
{
self = [super init];
if (self) {
UIView *v = [[[NSBundle mainBundle] loadNibNamed:#"CC_SlideMenu_View" owner:self options:nil] firstObject];
v.frame = self.menuContainerV.bounds;
[self.menuContainerV addSubview:v];
self.tableView.backgroundColor = [UIColor darkGrayColor];
}
return self;
}
...
#end
The result is something like this.
Im new in OOP and I've read some tutorials and books. I have the concept more or less clear but I haven't seen many examples. I've made a simple webView app which only displays a website and got some tabs and push notifications. It's not the big deal. Almost all the code from father class can be reused, and no need to rewrite it, so I've made some inheritance and overriding solutions to childrens, but I'm would like to know if the way I did it is the correct approach to object oriented way, also with the design and even with the code documentation. I need to show this code in a future job interview and I would like to refine it and also learn about experts from here with with more experience than I have.
Any help would be highly appreciated and many thanks!
This is my father class:
//
// FirstViewController.h
#import <UIKit/UIKit.h>
#import "iAd/iAd.h"
#interface FirstViewController : UIViewController <UIWebViewDelegate,UIGestureRecognizerDelegate, ADBannerViewDelegate, UIScrollViewDelegate>
#property (strong, nonatomic) IBOutlet UITabBarItem *tabInicio;
+(void)viewController:(NSString*)link;
-(void)configureView:(UIViewController*)vista;
-(void)iAdDisplay:(UIViewController*)vista;
-(void)autoResizeWhenRotates:(UIViewController*)vista;
-(void)configureWebView:(UIViewController *)vista withUrlLink:(NSURL *)url;
-(void)showHideStatusBar:(UIViewController*)vista;
-(void)addGestures:(UIViewController*)vista;
-(void)configureToolBar:(UIViewController*)vista;
-(void)addIndicator:(UIViewController*)vista;
#end
And the implementation file:
//
// FirstViewController.m
// push
//
#import "FirstViewController.h"
#interface FirstViewController ()
#property UIActivityIndicatorView *indicator;
#property UIToolbar *cToolBar;
#property BOOL isHidden;
#end
#implementation FirstViewController
static UITabBarController *static_Tab = nil;
static UIWebView* static_iVar = nil;
- (void)viewDidLoad {
[super viewDidLoad];
[self configureView:self];
//configure autoresize when device rotate
NSURL *url = [NSURL URLWithString:#"http://hnoslopez.com"];
[self configureStatic_iVar:self withUrlLink:url];
//Set tab bar icons
[[[self.tabBarController.viewControllers objectAtIndex:1] tabBarItem]setImage:[UIImage imageNamed:#"nosotros.png"]];
[[[self.tabBarController.viewControllers objectAtIndex:2] tabBarItem]setImage:[UIImage imageNamed:#"catalogo.png"]];
[[[self.tabBarController.viewControllers objectAtIndex:3] tabBarItem]setImage:[UIImage imageNamed:#"blog.png"]];
self.tabBarController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.tabBarController.view.autoresizesSubviews = YES;
//Set first tab
_tabInicio = [[UITabBarItem alloc] initWithTitle:#"Inicio" image:[UIImage imageNamed:#"inicioIcon.png"] tag:0];
[[UITabBar appearance] setSelectedImageTintColor:[UIColor greenColor]];
[self.tabBarController setSelectedIndex:0];
self.tabBarItem = _tabInicio;
}
-(void)configureView:(UIViewController *)vista{
//configure toolbar
[self configureToolBar:vista];
//show hide status bar with a gesture recognizer and an animation
[self showHideStatusBar:vista];
//iAd display
[self iAdDisplay:vista];
//auto resizing when device rotates
[self autoResizeWhenRotates:vista];
//Add gestures
[self addGestures:vista];
//Add Indicator
[self addIndicator:vista];
}
-(void)configureToolBar:(UIViewController *)vista{
//Add a toolbar, back and reload button...
UIToolbar *cToolBar = [[UIToolbar alloc] init];
//auto resizing
cToolBar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingNone;
cToolBar.autoresizesSubviews = YES;
cToolBar.frame = CGRectMake(0, 0, vista.view.frame.size.width, 44);
NSMutableArray *items = [[NSMutableArray alloc] init];
//Set toolbar properties..
[cToolBar setBarStyle:UIBarStyleBlack];
[cToolBar setTranslucent:YES ];
[cToolBar setTintColor:[UIColor greenColor]];
//Back Button..
NSString *backArrowString = #"\U000025C0\U0000FE0E AtrĂ¡s"; //BLACK LEFT-POINTING TRIANGLE PLUS VARIATION SELECTOR
UIBarButtonItem *back = [[UIBarButtonItem alloc] initWithTitle:backArrowString style:UIBarButtonItemStyleDone target:nil action:#selector(goBack)];
//Reload button..
UIBarButtonItem *right = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:nil action:#selector(refreshControl)];
[right setTintColor:[UIColor greenColor]];
//Flexible space between buttons..
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
[items addObject:back];
[items addObject:flexibleSpace];
[items addObject:right];
[cToolBar setItems:items animated:NO];
[vista.view addSubview:cToolBar];
}
-(void)showHideStatusBar:(UIViewController *)vista{
//Animation performed to hide/show bar when slide your finger..
if ([vista respondsToSelector:#selector(setNeedsStatusBarAppearanceUpdate)]) {
// iOS 7
[vista performSelector:#selector(setNeedsStatusBarAppearanceUpdate)];
} else {
// iOS 6
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
}
}
-(void)iAdDisplay:(UIViewController*)vista{
//Display iAd..
[vista setCanDisplayBannerAds:YES];
ADBannerView * adView = [[ADBannerView alloc] initWithFrame:CGRectZero];
adView.frame = CGRectOffset(adView.frame, 0, 44);
adView.delegate = self;
//resizing
adView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
adView.autoresizesSubviews = YES;
[vista.view addSubview:adView];
}
-(void)autoResizeWhenRotates:(UIViewController*)vista{
vista.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
vista.view.autoresizesSubviews = YES;
}
-(void)addGestures:(UIViewController*)vista{
//SWIPE DOWN
UISwipeGestureRecognizer *gest1 = [[UISwipeGestureRecognizer alloc] initWithTarget:vista action:#selector(hideTabBar:)];
gest1.delegate = self;
[gest1 setDirection:UISwipeGestureRecognizerDirectionDown];
[vista.view addGestureRecognizer:gest1];
//SWIPE UP
UISwipeGestureRecognizer *gest2 = [[UISwipeGestureRecognizer alloc] initWithTarget:vista action:#selector(showTabBar:)];
gest2.delegate = self;
[gest2 setDirection:UISwipeGestureRecognizerDirectionUp];
[vista.view addGestureRecognizer:gest2];
}
-(void)addIndicator:(UIViewController *)vista{
//loading indicator
// _indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
UIActivityIndicatorView* indicador = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[indicador setCenter:vista.view.center];
[indicador setHidesWhenStopped:YES];
//resizing
indicador.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
indicador.autoresizesSubviews = YES;
[vista.view addSubview:indicador];
[indicador startAnimating];
}
-(void)configureWebView:(UIViewController *)vista withUrlLink:(NSURL *)url{
//Children method to configure children webViews...
float width = [UIScreen mainScreen].bounds.size.width;
float height = [UIScreen mainScreen].bounds.size.height;
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, width,height)];
webView.delegate = self;
//ADD Delegate to disable lateral scroll
webView.scrollView.delegate = self;
//auto resizing
webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
webView.autoresizesSubviews = YES;
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[webView loadRequest:requestObj];
[vista.view addSubview:webView];
}
-(void)configureStatic_iVar:(UIViewController *)vista withUrlLink:(NSURL *)url{
//Configure father class webView...
float width = [UIScreen mainScreen].bounds.size.width;
float height = [UIScreen mainScreen].bounds.size.height;
static_iVar = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, width,height)];
static_iVar.delegate = self;
//ADD Delegate to disable lateral scroll
static_iVar.scrollView.delegate = self;
//auto resizing
static_iVar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
static_iVar.autoresizesSubviews = YES;
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[static_iVar loadRequest:requestObj];
[vista.view addSubview:static_iVar];
}
#pragma - mark selectors
-(void)goBack{
NSArray *subviews = [self.view subviews];
for (UIView *subview in subviews){
if([subview isKindOfClass:[UIWebView class]] && [subview respondsToSelector:#selector(goBack)])
{
[subview performSelector:#selector(goBack)];
}
}
}
-(void)refreshControl{
[super viewDidAppear:YES];
//iterate an array of subviews and finds indicator
NSArray *subviews = [self.view subviews];
for (UIView *subview in subviews)
{
if([subview isKindOfClass:[UIActivityIndicatorView class]] && [subview respondsToSelector:#selector(startAnimating)])
{
[subview performSelector:#selector(startAnimating)];
}
if([subview isKindOfClass:[UIWebView class]] && [subview respondsToSelector:#selector(reload)])
{
[subview performSelector:#selector(reload)];
}
}
}
- (void) hideTabBar:(UITabBarController *) tabbarcontroller
{
//Perform animation to hide tab bar
CGRect screenRect = [[UIScreen mainScreen] bounds];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
float fHeight = screenRect.size.height;
for(UIView *view in self.tabBarController.view.subviews)
{
if([view isKindOfClass:[UITabBar class]])
{
[view setFrame:CGRectMake(view.frame.origin.x, fHeight, view.frame.size.width, view.frame.size.height)];
}
}
[UIView commitAnimations];
}
- (void) showTabBar:(UITabBarController *) tabbarcontroller
{
//Perform animation to show tab bar..
CGRect screenRect = [[UIScreen mainScreen] bounds];
float fHeight = screenRect.size.height - self.tabBarController.tabBar.frame.size.height;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
for(UIView *view in self.tabBarController.view.subviews)
{
if([view isKindOfClass:[UITabBar class]] )
{
[view setFrame:CGRectMake(view.frame.origin.x, fHeight, view.frame.size.width, view.frame.size.height)];
}
}
[UIView commitAnimations];
}
+(void)viewController:(NSString*)link{
//Loads push notification links in first tab..
NSURL *url = [NSURL URLWithString:link];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[static_iVar loadRequest:requestObj];
}
#pragma mark - delegate methods
- (void)webViewDidStartLoad:(UIWebView *)webView
{
//iterate an array of subviews and finds indicator
NSArray *subviews = [self.view subviews];
for (UIView *subview in subviews)
{
if([subview isKindOfClass:[UIActivityIndicatorView class]] && [subview respondsToSelector:#selector(startAnimating)])
{
[subview performSelector:#selector(startAnimating)];
}
}
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
//iterate an array of subviews and finds indicator
NSArray *subviews = [self.view subviews];
if([subviews count] == 0)
{
return;
}
else
{
for (UIView *subview in subviews)
{
if([subview isKindOfClass:[UIActivityIndicatorView class]] && [subview respondsToSelector:#selector(stopAnimating)])
{
[subview performSelector:#selector(stopAnimating)];
}
}
}
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
//iterate an array of subviews and finds indicator
NSArray *subviews = [self.view subviews];
if([subviews count] == 0)
{
return;
}
else
{
for (UIView *subview in subviews)
{
if([subview isKindOfClass:[UIActivityIndicatorView class]] && [subview respondsToSelector:#selector(stopAnimating)])
{
[subview performSelector:#selector(stopAnimating)];
}
}
}
CGSize contentSize = webView.scrollView.contentSize;
CGSize viewSize = self.view.bounds.size;
float rw = viewSize.width / contentSize.width;
webView.scrollView.minimumZoomScale = rw;
webView.scrollView.maximumZoomScale = rw;
webView.scrollView.zoomScale = rw;
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
return TRUE;
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
return YES;
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
[scrollView setContentOffset:CGPointMake(0, scrollView.contentOffset.y)];
}
- (BOOL)prefersStatusBarHidden {
return YES;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
And this is a children viewController:
//
// SecondViewController.h
//
#import <UIKit/UIKit.h>
#import "iAd/iAd.h"
#import "FirstViewController.h"
#interface secondViewController : FirstViewController <UIWebViewDelegate,UIGestureRecognizerDelegate,ADBannerViewDelegate, UIScrollViewDelegate>
#property (weak, nonatomic) IBOutlet UIWebView *secondView;
#property (strong, nonatomic) IBOutlet UITabBarItem *secondTab;
#end
Implementation file:
//
// SecondViewController.m
//
//
#import "secondViewController.h"
#import "iAd/iAd.h"
#interface secondViewController ()
#property UIActivityIndicatorView *indicador;
#end
#implementation secondViewController
- (void)viewDidLoad {
[super viewDidLoad];
[super configureView:self];
//configure webView
NSURL *url = [NSURL URLWithString:#"http://hnoslopez.com/nosotros/"];
[self configureWebView:self withUrlLink:url];
//Configure second tab
_secondTab = [[UITabBarItem alloc] initWithTitle:#"Nosotros" image:[UIImage imageNamed:#"nosotros.png"] tag:1];
[[UITabBar appearance] setSelectedImageTintColor:[UIColor greenColor]];
[self.tabBarController setSelectedIndex:1];
self.tabBarItem = _secondTab;
}
- (BOOL)prefersStatusBarHidden {
return YES;
}
-(void)goBack{
//reloads webView
NSArray *subviews = [self.view subviews];
for (UIView *subview in subviews){
if([subview isKindOfClass:[UIWebView class]] && [subview respondsToSelector:#selector(goBack)])
{
[subview performSelector:#selector(goBack)];
}
}
}
-(void)refreshControl{
[super viewDidAppear:YES];
//iterate an array of subviews and finds indicator
NSArray *subviews = [self.view subviews];
if([subviews count] == 0)
{
return;
}
else
{
for (UIView *subview in subviews)
{
//start animating indicator
if([subview isKindOfClass:[UIActivityIndicatorView class]] && [subview respondsToSelector:#selector(startAnimating)])
{
[subview performSelector:#selector(startAnimating)];
}
//reloads webView
if([subview isKindOfClass:[UIWebView class]] && [subview respondsToSelector:#selector(reload)])
{
[subview performSelector:#selector(reload)];
}
}
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)webViewDidStartLoad:(UIWebView *)webView
{
//iterate an array of subviews and finds indicator
NSArray *subviews = [self.view subviews];
if([subviews count] == 0)
{
return;
}
else
{
for (UIView *subview in subviews)
{
if([subview isKindOfClass:[UIActivityIndicatorView class]] && [subview respondsToSelector:#selector(startAnimating)])
{
[subview performSelector:#selector(startAnimating)];
}
}
}
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
//iterate an array of subviews and finds indicator
NSArray *subviews = [self.view subviews];
if([subviews count] == 0)
{
return;
}
else
{
for (UIView *subview in subviews)
{
if([subview isKindOfClass:[UIActivityIndicatorView class]] && [subview respondsToSelector:#selector(stopAnimating)])
{
[subview performSelector:#selector(stopAnimating)];
}
}
}
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
//iterate an array of subviews and finds indicator
NSArray *subviews = [self.view subviews];
if([subviews count] == 0)
{
return;
}
else
{
for (UIView *subview in subviews)
{
if([subview isKindOfClass:[UIActivityIndicatorView class]] && [subview respondsToSelector:#selector(stopAnimating)])
{
[subview performSelector:#selector(stopAnimating)];
}
}
}
CGSize contentSize = webView.scrollView.contentSize;
CGSize viewSize = self.view.bounds.size;
float rw = viewSize.width / contentSize.width;
webView.scrollView.minimumZoomScale = rw;
webView.scrollView.maximumZoomScale = rw;
webView.scrollView.zoomScale = rw;
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
return TRUE;
}
- (void) hideTabBar:(UITabBarController *) tabbarcontroller
{
CGRect screenRect = [[UIScreen mainScreen] bounds];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
float fHeight = screenRect.size.height;
for(UIView *view in self.tabBarController.view.subviews)
{
if([view isKindOfClass:[UITabBar class]])
{
[view setFrame:CGRectMake(view.frame.origin.x, fHeight, view.frame.size.width, view.frame.size.height)];
}
}
[UIView commitAnimations];
}
- (void) showTabBar:(UITabBarController *) tabbarcontroller
{
CGRect screenRect = [[UIScreen mainScreen] bounds];
float fHeight = screenRect.size.height - self.tabBarController.tabBar.frame.size.height;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
for(UIView *view in self.tabBarController.view.subviews)
{
if([view isKindOfClass:[UITabBar class]] )
{
[view setFrame:CGRectMake(view.frame.origin.x, fHeight, view.frame.size.width, view.frame.size.height)];
}
}
[UIView commitAnimations];
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
return YES;
}
#pragma mark resuelve problema de que la pagina hace scroll horizontal
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
[scrollView setContentOffset:CGPointMake(0, scrollView.contentOffset.y)];
}
#end
I have a parent->child navigation setup in application. I use navigation via pushViewController function.
-(void)loadMemosViewController:(id)sender{
if(activeHullGuid != nil && activeHullGuid.length > 0)
{
NSString *storyboardName = #"MainStoryboard_iPhone1";
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
MemosViewController *loginVC = [storyboard instantiateViewControllerWithIdentifier:#"sid_Memos"];
loginVC.keyReference = [[KeyValuePairIS alloc] initWithData:&controllerID:activeHullGuid];
[self.navigationController pushViewController:loginVC animated:YES];
}
}
for back navigation I use only default implementation in IOS (that would be a click on a back button).
This setup works for most situations, but recent implementation is causing problems.
The problem is this:
I have parent view controller named "hullViewController" and a child "memosViewController". The navigation between them works. Child does not report any information back to parent. HullViewController is also an editable form, which changes edit state via button in navigation bar.
Now if I change this edit/read state on hullViewController works nonstop. If I visit the child memosViewController, and go back to parent, I can only change state once more, then application crashes with exc_bad_access code=1.
After profiling with "Zombies" I found the culprit for exception is my probably disposed child memosViewController.
An Objective-C message was sent to a deallocated 'MemosViewController' object (zombie) at address: 0xdd52f10
it seams to crash on an IOS internal event, since none of my breakpoints are hit before crash.
A you can see the child is instanced during creation and I don't reference it to nothing else. Why would the edit state change request the child object?
What I tried already:
-declaring MemosViewController as a class variable. (application did not crash anymore, but would not change state anymore).
-initialising MemosViewController on viewDidLoad, changed nothing.
-calling child with class init only (not via storyboard), loaded child without UI, but result was same.
Project is set up with ARC, so I have minimum control on disposal of objects.
I have been searching for a solution quite a while now, with no results. Any help to solve my error editing if I visit the child would be appreciated.
UPDATE
I have additionally discovered, that when I get back to parent from child, the reference self.navigationItem still points to child, and any update to navigation buttons crashes the app.
**attaching custom ViewController, since it could be related to problem **
#import "UITableViewControllerEx.h"
#import "UITextFieldEx.h"
#import "UITextViewEx.h"
#import "GlobalValues.h"
#import "UITableViewEx.h"
#interface UITableViewControllerEx ()
#end
#implementation UITableViewControllerEx
UIBarButtonItem *bbi_navigateToMaster;
UIBarButtonItem *editButton;
UIButton *cmdEdit;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
[self setNavigationBackground];
[self setApplicationTintColor];
[self setApplicationTitleFont];
[self setupLeftBarButtonItem];
[self setBackButton];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//UITextFieldEx delegate to control the length of fields
- (BOOL)textField:(UITextFieldEx *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSUInteger newLength = [textField.text length] + [string length] - range.length;
return (newLength > textField.maxLength) ? NO : YES;
}
//UITextViewEx delegate to control the length of fields
-(BOOL)textView:(UITextViewEx *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
NSUInteger newLength = [textView.text length] + [text length] - range.length;
return (newLength > textView.maxLength) ? NO : YES;
}
//function to set left button to always pop to root controller
- (void)setBackButtonToReturnToMaster {
UIButton *cmdHome = [[UIButton alloc] initWithFrame:CGRectMake(0,0,30,30)];
[cmdHome setImage:[UIImage imageNamed:#"home"] forState:UIControlStateNormal];
bbi_navigateToMaster = [[UIBarButtonItem alloc] initWithCustomView:cmdHome];
[cmdHome addTarget:self action:#selector(backToMaster:) forControlEvents:UIControlEventTouchUpInside ];
self.navigationItem.leftBarButtonItems = [NSArray arrayWithObjects:bbi_navigateToMaster , nil];
/*
bbi_navigateToMaster = [[UIBarButtonItem alloc] initWithTitle:#"" style:UIBarButtonItemStylePlain target:self action:#selector(backToMaster:)];
self.navigationItem.leftBarButtonItems = [NSArray arrayWithObjects:bbi_navigateToMaster , nil];
[bbi_navigateToMaster setImage:[UIImage imageNamed:#"home"]];
[bbi_navigateToMaster setImageInsets:UIEdgeInsetsMake(2, 2, 2, 2)];*/
}
//pop to root controller
-(void)backToMaster:(id)sender {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
}
else { [self.navigationController popToRootViewControllerAnimated:YES]; }
}
//find superview element of given type
- (UIView *)findSuperViewWithClass:(Class)superViewClass uiViewToSearch:(UIView*)bottomView{
UIView *superView = bottomView.superview;
UIView *foundSuperView = nil;
while (nil != superView && nil == foundSuperView) {
if ([superView isKindOfClass:superViewClass]) {
foundSuperView = superView;
break;
} else {
superView = superView.superview;
}
}
return foundSuperView;
}
-(void)setNavigationBackground{
if ([self.navigationController.navigationBar respondsToSelector:#selector(setBackgroundImage:forBarMetrics:)] ) {
UIImage *image = [UIImage imageNamed:#"navigationBackground"];
[self.navigationController.navigationBar setBackgroundImage:image forBarMetrics:UIBarMetricsDefault];
UIView* uv = [[UIView alloc] initWithFrame:CGRectMake(0, self.navigationController.navigationBar.frame.size.height-1,self.navigationController.navigationBar.frame.size.width, 1)];
[uv setBackgroundColor:[GlobalValues getTintColor]];
[self.navigationController.navigationBar insertSubview:uv atIndex:10];
}
}
//sets the tint color of szstem items (title, szstem buttons, ...)
-(void)setApplicationTintColor {
NSArray *ver = [[UIDevice currentDevice].systemVersion componentsSeparatedByString:#"."];
if ([[ver objectAtIndex:0] intValue] >= 7) {
self.navigationController.navigationBar.barTintColor = [GlobalValues getTintColor];
self.navigationController.navigationBar.tintColor = [GlobalValues getTintColor];
self.navigationController.navigationBar.translucent = NO;
[self.navigationController.navigationBar setTitleTextAttributes:#{NSForegroundColorAttributeName : [UIColor whiteColor]}];
UIColor *color = [GlobalValues getTintColor];
self.view.tintColor = color;
}else {
//self.navigationController.navigationBar.tintColor = [GlobalValues getTintColor];
/*NSDictionary *textTitleOptions = [NSDictionary dictionaryWithObjectsAndKeys:[UIColor whiteColor], UITextAttributeTextColor, [UIColor clearColor], UITextAttributeTextShadowColor, nil];
[[UINavigationBar appearance] setTitleTextAttributes:textTitleOptions];*/
}
}
//sets the navigation title
-(void)setApplicationTitleFont {
NSArray *ver = [[UIDevice currentDevice].systemVersion componentsSeparatedByString:#"."];
if ([[ver objectAtIndex:0] intValue] >= 7) {
[self.navigationController.navigationBar setTitleTextAttributes:
[NSDictionary dictionaryWithObjectsAndKeys:
[UIFont fontWithName:#"HelveticaNeue-Light" size:21],
NSFontAttributeName, [UIColor whiteColor], UITextAttributeTextColor, [UIColor clearColor], UITextAttributeTextShadowColor, nil]];
}else {
[self.navigationController.navigationBar setTitleTextAttributes: #{
UITextAttributeTextColor: [UIColor whiteColor],
UITextAttributeFont: [UIFont fontWithName:#"Helvetica-Light" size:21.0f]
}];
}
}
-(void)setupLeftBarButtonItem{
cmdEdit = [[UIButton alloc] initWithFrame:CGRectMake(0,0,30,30)];
[cmdEdit setImage:[UIImage imageNamed:#"locked"] forState:UIControlStateNormal];
editButton = [[UIBarButtonItem alloc] initWithCustomView:cmdEdit];
[cmdEdit addTarget:self action:#selector(setEditState) forControlEvents:UIControlEventTouchUpInside];
}
- (UIBarButtonItem *)leftBarButtonItem
{
if (self.tableView.editing) {
[cmdEdit setImage:[UIImage imageNamed:#"unlocked"] forState:UIControlStateNormal];
return editButton;
}
else {
[cmdEdit setImage:[UIImage imageNamed:#"locked"] forState:UIControlStateNormal];
return editButton;
}
}
-(void)updateEditButton{
if (self.tableView.editing) {
[cmdEdit setImage:[UIImage imageNamed:#"unlocked"] forState:UIControlStateNormal];
}
else {
[cmdEdit setImage:[UIImage imageNamed:#"locked"] forState:UIControlStateNormal];
}
}
-(void)setEditState{
if (!self.tableView.editing) {
[self setEditing:YES animated:YES];
} else {
[self setEditing:NO animated:YES];
}
[self updateEditButton];
}
}*/
-(void) setBackButton{
UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *backBtnImage = [UIImage imageNamed:#"back"] ;
[backBtn setBackgroundImage:backBtnImage forState:UIControlStateNormal];
[backBtn addTarget:self action:#selector(goback) forControlEvents:UIControlEventTouchUpInside];
backBtn.frame = CGRectMake(0, 0, 30, 30);
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithCustomView:backBtn] ;
self.navigationItem.leftBarButtonItem = backButton;
}
- (void)goback
{
[self.navigationController popViewControllerAnimated:YES];
}
#pragma mark - Table view data source
#pragma mark - Table view delegate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 0;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 0;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
cell.backgroundView=[[UIView alloc] initWithFrame:CGRectZero];
cell.backgroundColor = [UIColor clearColor];
cell.layer.backgroundColor = [UIColor clearColor].CGColor;
}
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView *customTitleView = [ [UIView alloc] initWithFrame:CGRectMake(10, 0, 300, 44)];
UIView *customTitleLineView = [ [UIView alloc] initWithFrame:CGRectMake(10, 43, self.view.frame.size.width -20, 0.5f)];
customTitleLineView.backgroundColor = [GlobalValues getTintColor];
UILabel *titleLabel = [ [UILabel alloc] initWithFrame:CGRectMake(20, 0, 300, 44)];
titleLabel.text = [self tableView:tableView titleForHeaderInSection:section];
titleLabel.font = [UIFont fontWithName:#"HelveticaNeue" size:18];
titleLabel.textColor = [GlobalValues getTintColor];
titleLabel.backgroundColor = [UIColor clearColor];
if (titleLabel.text.length != 0) {
[customTitleView addSubview:customTitleLineView];
}
[customTitleView addSubview:titleLabel];
return customTitleView;
}
#end
Seems I have found a solution to my problem.
Class UITableViewControllerEx contains functionality to setup edit button. The class variable "UIBarButtonItem *editButton;" is then used as edit button on all forms that inherit from "UITableViewControllerEx"
the solution was to instantiate UIBarButtonItem on each form inheriting UITableViewControllerEx with local name (like editButtonHull) and given as param to logic of superclass.
Thanks to #akashg for suggestion that Navigation bar modification might be the problem
when I tested my App in instruments for memory leak I found nothing(running using simulator).But When I run it in a mobile and then checked, there are many leaks in UIKit objects. This happening in every view.In simulator no such leaks are showing.
Below is the screenshot of the instrument where some leakage happened.
When I moved to secondViewController from HomeView, no leaks found.If again coming back to home,these many leaks are found. So, is it mean that, I have to release/nil all the UI objects which I used in that secondView. For your information, below are the UI objects I used in secondView.
1.Two Background UIImageView
2.One TitleBar UIImageView
3.3 UIButtons(Back,left and right button for iCarousel)
4.One iCarousel view
5.UIPageController(For this I have used a third Party code SMPageControl)
6.One title label.
Note : Mine is Non-ARC code.
Did anyone faced this problem before.How can I overcome this problem,since I have this problem in every View in my App.Because of this, my App getting memory waring frequently and crashing often.
Thank you.
Below is the my implementation file of that View.
EDIT1 :
#implementation CatalogueViewController
#synthesize deptCarousel = _deptCarousel;
#synthesize carouselItems = _carouselItems;
#synthesize categorymAr = _categorymAr;
#synthesize spacePageControl = _spacePageControl;
#synthesize wrap;
- (void)dealloc {
_deptCarousel = nil;
[_categorymAr release];
_categorymAr = nil;
_deptCarousel.delegate = nil;
_deptCarousel.dataSource = nil;
[_deptCarousel release];
[_carouselItems release];
[viewGesture release];
viewGesture = nil;
[_spacePageControl release];
_spacePageControl = nil;
imgViewBG = nil;
imgViewBG2 = nil;
btnPrev = nil;
btnNext = nil;
// [self releaseObjects];
[super dealloc];
}
- ( IBAction) btnBackClicked {
[self.navigationController popViewControllerAnimated:YES];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = NSLocalizedString(#"catalogue", #"Catalogue");
// Do any additional setup after loading the view from its nib.
_deptCarousel.type = iCarouselTypeLinear;
_deptCarousel.scrollSpeed = 0.3f;
_deptCarousel.bounceDistance = 0.1f;
_deptCarousel.scrollToItemBoundary = YES;
_deptCarousel.stopAtItemBoundary = YES;
[_deptCarousel setScrollEnabled:NO];
UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipeNext:)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[viewGesture addGestureRecognizer:swipeLeft];
[swipeLeft release];
UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipePrev:)];
swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
[viewGesture addGestureRecognizer:swipeRight];
[swipeRight release];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
[viewGesture addGestureRecognizer:singleTap];
[singleTap release];
_carouselItems = [[NSMutableArray alloc] initWithCapacity:1];
_categorymAr = [[NSMutableArray alloc] initWithCapacity:1];
[self addCatalogues];
_spacePageControl.numberOfPages = [_categorymAr count];
[_spacePageControl setPageIndicatorImage:[UIImage imageNamed:IS_IPAD?#"Marker1.fw.png" : #"Markeri.png"]];
[_spacePageControl setCurrentPageIndicatorImage:[UIImage imageNamed:IS_IPAD?#"Marker-Highlight.png" : #"Marker-Highlight_i.png"]];
[_spacePageControl addTarget:self action:#selector(spacePageControl:) forControlEvents:UIControlEventValueChanged];
}
- (void)spacePageControl:(SMPageControl *)sender{
[_deptCarousel scrollToItemAtIndex:sender.currentPage animated:YES];
}
- ( void ) addCatalogues {
[_categorymAr addObjectsFromArray:[[DBModel database] categoryList]];
for (int i = 0; i < [_categorymAr count]; i++) {
[_carouselItems addObject:[NSNumber numberWithInt:i]];
}
[_deptCarousel reloadData];
}
- (void)viewDidUnload{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[self phoneType];
[super viewWillAppear:animated];
if (IS_IPAD) {
UIInterfaceOrientation statusBarOrientation = [[UIApplication sharedApplication] statusBarOrientation];
[self handleOrientation:statusBarOrientation];
}
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- ( void ) phoneType{
if(!IS_IPAD){
if(IS_IPHONE5){
imgViewBG.image = [UIImage imageNamed:#"Background5_5.jpg"];
imgViewBG.center = CGPointMake(162,265);
imgViewBG2.image = [UIImage imageNamed:#"Background11_5.png"];
_spacePageControl.center = CGPointMake(160, 478);
_deptCarousel.center = CGPointMake(160, 355);
viewGesture.center = CGPointMake(160, 355);
btnPrev.center = CGPointMake(25, 355);
btnNext.center = CGPointMake(295, 355);
}
else{
imgViewBG.image = [UIImage imageNamed:#"Background5.jpg"];
imgViewBG2.image = [UIImage imageNamed:#"Background9.png"];
}
}
}
-(void)textFieldDidBeginEditing:(UITextField *)textField{
textFieldSearch.placeholder = #"";
UIButton *clearButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
[clearButton setImage:[UIImage imageNamed:IS_IPAD?#"Btn_X_Large.fw.png":#"Btn_X.fw.png"] forState:UIControlStateNormal];
[clearButton addTarget:self action:#selector(btnClearTextField) forControlEvents:UIControlEventTouchUpInside];
[textFieldSearch setRightViewMode:UITextFieldViewModeAlways];
[textFieldSearch setRightView:clearButton];
[clearButton release];
}
-(void)textFieldDidEndEditing:(UITextField *)textField{
[textFieldSearch setRightView:nil];
if ([textFieldSearch.text isEqualToString:#""]) {
textFieldSearch.placeholder = NSLocalizedString(#"hud_search_for_a_product_here",#"");
}
}
-(IBAction)btnClearTextField{
textFieldSearch.text = #"";
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAll;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (IS_IPAD) {
return YES;
} else {
return (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown);
}
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation )toInterfaceOrientation duration:(NSTimeInterval)duration{
if (IS_IPAD) {
[self handleOrientation:toInterfaceOrientation];
}
}
- ( void ) handleOrientation:(UIInterfaceOrientation )toInterfaceOrientation {
if (toInterfaceOrientation == UIInterfaceOrientationPortrait || toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
imgViewBG.image = [UIImage imageNamed:#"Background_Catalogue_P.jpg"];
imgViewBG2.image = [UIImage imageNamed:#"Background_Overlay_P.fw.png"];
btnPrev.center = CGPointMake(90, 640);
btnNext.center = CGPointMake(677, 640);
textFieldSearch.frame = CGRectMake(187, 54, 418, 25);
_deptCarousel.frame = CGRectMake(235, 250, 300, 800);
_spacePageControl.center = CGPointMake(385, 920);
viewGesture.center = CGPointMake(385, 658);
}else {
imgViewBG.image = [UIImage imageNamed:#"Background_Catalogue_L.jpg"];
imgViewBG2.image = [UIImage imageNamed:#"Background_Overlay_L.fw.png"];
btnPrev.center = CGPointMake(54, 385);
btnNext.center = CGPointMake(640, 385);
textFieldSearch.frame = CGRectMake(240, 55, 567, 25);
_deptCarousel.frame = CGRectMake(50, 250, 600, 300);
_spacePageControl.center = CGPointMake(346, 660);
viewGesture.center = CGPointMake(347, 405);
}
}
- ( IBAction )btnDepartmentClicked:(id)sender {
int btnTag = [sender tag];
ProductCategoriesViewController *productView = [[ProductCategoriesViewController alloc] initWithNibName:#"ProductCategoriesView" bundle:nil];
if ( btnTag == 0 ) {
[productView setStrTitle:NSLocalizedString(#"women", #"Women")];
}else if ( btnTag == 1 ) {
[productView setStrTitle:NSLocalizedString(#"men", #"Men")];
} else {
[productView setStrTitle:NSLocalizedString(#"sports", #"Sports")];
}
[self.navigationController pushViewController:productView animated:YES];
[productView release];
}
- ( BOOL ) textFieldShouldReturn:( UITextField * )textField {
[textField resignFirstResponder];
[Flurry logEvent:#"Product searched" withParameters:[NSDictionary dictionaryWithObjectsAndKeys:textField.text,#"1", nil]];
[self productSearch:textField.text isBar:NO isQR:NO];
return YES;
}
- ( void ) productSearch:( NSString * )_searchText isBar:( BOOL )_isBar isQR:( BOOL )_isQr {
if ([_searchText isEqualToString:#""]) {
return;
}
NSMutableArray *ProductList = [[NSMutableArray alloc] init];
[ProductList addObjectsFromArray:[[DBModel database] productSearch:_searchText isBar:_isBar isQR:_isQr]];
if ( [ProductList count] == 0 ) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"product", #"")
message:NSLocalizedString(#"cannot_find_product", #"")
delegate:nil
cancelButtonTitle:NSLocalizedString(#"ok", #"")
otherButtonTitles:nil];
[alert show];
[alert release];
} else {
GeneralProductListViewController *generalProductList = [[GeneralProductListViewController alloc] initWithNibName:IS_IPAD?#"GeneralProductListView~iPad": #"GeneralProductListView" bundle:nil];
[generalProductList setMArProducts:ProductList];
[self.navigationController pushViewController:generalProductList animated:YES];
[generalProductList release];
}
[ProductList release];
}
-(IBAction) spin:(id)sender {
if([sender tag]==0)
{
[_deptCarousel scrollToItemAtIndex:[self.deptCarousel currentItemIndex]+1 animated:YES];
// [_deptCarousel scrollByNumberOfItems:1 duration:2.0];
}
else{
[_deptCarousel scrollToItemAtIndex:[self.deptCarousel currentItemIndex]-1 animated:YES];
}
}
-(void)swipeNext:(UISwipeGestureRecognizer *)recognizer{
[_deptCarousel scrollToItemAtIndex:[self.deptCarousel currentItemIndex]+1 animated:YES];
}
-(void)swipePrev:(UISwipeGestureRecognizer *)recognizer{
[_deptCarousel scrollToItemAtIndex:[self.deptCarousel currentItemIndex]-1 animated:YES];
}
-(void) handleSingleTap:(UITapGestureRecognizer *)recognizer{
if ([_categorymAr count] > 0) {
ProductCategoriesViewController *prodCatView = [[ProductCategoriesViewController alloc] initWithNibName:IS_IPAD ?
#"ProductCategoriesView~iPad" : #"ProductCategoriesView" bundle:nil];
Category *categoryObj = [_categorymAr objectAtIndex:[self.deptCarousel currentItemIndex]];
[prodCatView setStrTitle:categoryObj.categoryName];
[prodCatView setCategoryId:categoryObj.categoryId];
[Flurry logEvent:#"Category List" withParameters:[NSDictionary dictionaryWithObjectsAndKeys:categoryObj.categoryName,[NSString stringWithFormat:#"%d",categoryObj.categoryId], nil]];
[self.navigationController pushViewController:prodCatView animated:YES];
[prodCatView release];
}
}
//-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// pageControl.currentPage = [self.deptCarousel currentItemIndex] ;
//}
#pragma mark
#pragma mark NavigationBarViewDelegate metho
- ( void ) navigationBackClicked {
[self.navigationController popViewControllerAnimated:YES];
}
#pragma mark -
#pragma mark iCarousel methods
- (NSUInteger)numberOfItemsInCarousel:(iCarousel *)carousel
{
return [_carouselItems count];
}
- (NSUInteger)numberOfVisibleItemsInCarousel:(iCarousel *)carousel
{
//limit the number of items views loaded concurrently (for performance reasons)
return NUMBER_OF_VISIBLE_ITEMS;
}
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index
{
Category *categoryObj = [_categorymAr objectAtIndex:index];
//create a numbered view
UIView *view = nil;
NSString *imagePath = [[APP_CACHES_DIR stringByAppendingPathComponent:#"catalogues"] stringByAppendingString:[NSString stringWithFormat:#"/%d.jpg", categoryObj.categoryId]];
if (![[NSFileManager defaultManager] fileExistsAtPath:imagePath]) {
view = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:IS_IPAD?#"Gallery Placeholder.png":#"Gallery Placeholder.png"]] autorelease];
} else {
view = [[[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:[[APP_CACHES_DIR stringByAppendingPathComponent:#"catalogues"] stringByAppendingString:[NSString stringWithFormat:#"/%d.jpg", categoryObj.categoryId]]]] autorelease];
}
if (IS_IPAD) {
view.frame = CGRectMake(0, 0, 420, 420);
} else {
view.frame = CGRectMake(0, 0, 200, 200);
}
// UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(view.bounds.origin.x, view.bounds.origin.y+view.bounds.size.height, view.bounds.size.width, 44)] autorelease];
// label.text = categoryObj.categoryName;
// label.textColor = [UIColor blackColor];
// label.backgroundColor = [UIColor clearColor];
// label.textAlignment = UITextAlignmentCenter;
// label.font = [UIFont fontWithName:#"Helvetica-Bold" size:IS_IPAD?26:14];
// [view addSubview:label];
return view;
}
- (NSUInteger)numberOfPlaceholdersInCarousel:(iCarousel *)carousel
{
//note: placeholder views are only displayed on some carousels if wrapping is disabled
return INCLUDE_PLACEHOLDERS? 2: 0;
}
- (UIView *)carousel:(iCarousel *)carousel placeholderViewAtIndex:(NSUInteger)index
{
//create a placeholder view
UIView *view = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#""]] autorelease];
UILabel *label = [[[UILabel alloc] initWithFrame:view.bounds] autorelease];
label.text = (index == 0)? #"[": #"]";
label.backgroundColor = [UIColor clearColor];
label.textAlignment = UITextAlignmentCenter;
label.font = [label.font fontWithSize:50];
_spacePageControl.currentPage = index;
// [view addSubview:label];
return view;
}
- (CGFloat)carouselItemWidth:(iCarousel *)carousel
{
//usually this should be slightly wider than the item views
return ITEM_SPACING;
}
- (CATransform3D)carousel:(iCarousel *)_carousel transformForItemView:(UIView *)view withOffset:(CGFloat)offset
{
//implement 'flip3D' style carousel
//set opacity based on distance from camera
view.alpha = 1.0 - fminf(fmaxf(offset, 0.0), 1.0);
//do 3d transform
CATransform3D transform = CATransform3DIdentity;
transform.m34 = _deptCarousel.perspective;
transform = CATransform3DRotate(transform, M_PI / 8.0, 0, 1.0, 0);
return CATransform3DTranslate(transform, 0.0, 0.0, offset * _deptCarousel.itemWidth);
}
- (BOOL)carouselShouldWrap:(iCarousel *)carousel
{
//wrap all carousels
// return NO;
return wrap;
}
- (void)carousel:(iCarousel *)carousel didSelectItemAtIndex:(NSInteger)index {
if (index == [self.deptCarousel currentItemIndex]) {
ProductCategoriesViewController *prodCatView = [[ProductCategoriesViewController alloc] initWithNibName:IS_IPAD ?
#"ProductCategoriesView~iPad" : #"ProductCategoriesView" bundle:nil];
Category *categoryObj = [_categorymAr objectAtIndex:index];
[prodCatView setStrTitle:categoryObj.categoryName];
[prodCatView setCategoryId:categoryObj.categoryId];
[Flurry logEvent:#"Category List" withParameters:[NSDictionary dictionaryWithObjectsAndKeys:categoryObj.categoryName,[NSString stringWithFormat:#"%d",categoryObj.categoryId], nil]];
[self.navigationController pushViewController:prodCatView animated:YES];
[prodCatView release];
}
}
-(void) carouselDidScroll:(iCarousel *)carousel{
// [_deptCarousel scrollToItemAtIndex:[self.deptCarousel currentItemIndex]+3 animated:YES];
// [_deptCarousel scrollByNumberOfItems:1 duration:1];
}
- (void)carouselCurrentItemIndexUpdated:(iCarousel *)carousel{
_spacePageControl.currentPage = [self.deptCarousel currentItemIndex];
}
- ( IBAction ) myCart {
if ( [[DBModel database] isShoppingListEmpty] ) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"at_shopping_cart", #"")
message:NSLocalizedString(#"amsg_shopping_cart_empty", #"")
delegate:nil cancelButtonTitle:NSLocalizedString(#"ok", #"") otherButtonTitles:nil];
[alert show];
[alert release];
return;
}
MyCartViewController *myCartView = [[MyCartViewController alloc] initWithNibName:IS_IPAD ? #"MyCartView~iPad" : #"MyCartView" bundle:nil];
[self.navigationController pushViewController:myCartView animated:YES];
[myCartView release];
}
First, as noted before, use ARC. There is no single thing you could do that will more improve memory management.
Whether you use ARC or not, you should always use accessors to access your ivars (except in init and dealloc). As noted by #LombaX, you're setting your ivars incorrectly in viewDidLoad. Using accessors would help this.
You should run the static analyzer, which will help you find other memory mistakes.
I would suspect that you have an IBOutlet that is configured as retain and that you are not releasing in dealloc. That is the most likely cause of the leaks I'm seeing in your screenshots. ARC will generally make such problems go away automatically.
It is very possible that you have a retain loop. This generally would not show up as a leak. You should use heapshot to investigate that. Your leaks are pretty small; they may not be the actual cause of memory warnings. What you want to investigate (with the Allocations instrument) is what is actually significantly growing your memory use.
But first ARC. Then accessors. Then remove all build warnings. Then remove all Static Analyzer warnings. Then use the Allocations instrument.
Side note: the fact that it says the responsible party is "UIKit" does not mean that this is a bug in UIKit. It just means that UIKit allocated the memory that was later leaked. The cause of the leak could be elsewhere. (That said, UIKit does have several small leaks in it. In general they should not give you trouble, but you may never be able to get rid of 100% of small leaks in an iOS app.)
First:
you have a possible and visible leak, but I'm not sure if it is the same leak you have found in instruments:
These two lines are in your viewDidLoad method
_carouselItems = [[NSMutableArray alloc] initWithCapacity:1];
_categorymAr = [[NSMutableArray alloc] initWithCapacity:1];
But: viewDidLoad: is called every time the view is loaded by it's controller. If the controller purges the view (for example after a memory warning), at the second viewDidLoad your _carouselItems and _categorymAr instance variables will lost the reference to the previously created NSMutableArray, causing a leak
So, change that lines and use the syntesized setters:
self.carouselItems = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];
self.categorymAr = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];
the syntesized setter is configured to release the previous object before assignin a new one.
However: it's possible that you have another leak.
If you can reproduce the leak simply (if I understand, the leak appears simply moving from a VC to another), you can use the "heapshot" function of instruments.
Assuming that your leak appears moving from the first VC to the second and coming back:
open instruments with the allocations tool
go from the first VC to the second and come back.
press "mark heap" on the left. A line will appear.
go again from the first VC to the second and come back.
press "heapshot" again
do this several times (9-10)
the heapshot tool takes a "snapshot" of the living objects at the time you pushed the button and shows you only the difference.
If there are 2-3 new objects, you will see it in the list.
This is a good starting point to investigate a leak.
Look at the attached image:
Consider that you must mark the heap several time and discriminate "false positive" by looking at the object created, in my example you can se a possible leak (heapshot5, 1,66KB), but after looking at the content it's not --> it was a background task that started in that moment.
Moreover, delays of the autorelease pool and the cache of some UIKit objects can show something in the heapshot, this is why I say to try it several times.
One easy way to detect where your leaks come from is to use the Extended Detail view of the Instruments.
To do that click on "View"->"Extended detail" and a right menu with the stack trace of the "leak" will appear. There you will easily find the leaking code for each leak and if they come from your app.