Tapping on image i m getting EXC_BAD_ACCESS - iphone

Guys, i thought i found the solution but no.... i making app cont. from past 16 hrs, may be that's y my empty mind is not making any good sound... plz help me..
I am setting Tag to my dynamically generated imageviews and setting tapgestureReconizers on that,tags are given to UITapGestureRecognizer, i want to perform different actions on click on different images, but when ever click on any image it says very silently... :| EXC_BAD_ACCESS.
Code: -
#import <UIKit/UIKit.h>
#class AppDelegate_iPhone,Litofinter,ParsingViewController;
#interface FirstViewController : UIViewController<UIGestureRecognizerDelegate>{
NSMutableArray *array;
NSString *logoString;
AppDelegate_iPhone *appDelegate;
ParsingViewController *obj;
UIScrollView *scrollView;
NSMutableArray *idArray;
}
#property (nonatomic,retain)UIScrollView *scrollView;
-(void)onTapImage:(UITapGestureRecognizer *)recognizer;
#end
#import "FirstViewController.h"
#import "AppDelegate_iPhone.h"
#import "Litofinter.h"
#import "ParsingViewController.h"
#implementation FirstViewController
#synthesize scrollView;
-(id)init{
if(self == [super init]){
obj = [[ParsingViewController alloc] init];
array = [[NSArray alloc] initWithArray: obj.LogoMutableArray];
}
return self;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
int x=20,y=10;
int a=50,b=105;
appDelegate = (AppDelegate_iPhone *)[[UIApplication sharedApplication] delegate];
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0,500, 460)];
scrollView.contentSize = CGSizeMake(320,800);
scrollView.showsVerticalScrollIndicator = YES;
for (Litofinter *lito in appDelegate.logoArray) {
NSString * urlString = [lito.cLogo stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSURL * imageURL = [NSURL URLWithString:urlString];
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage * image = [UIImage imageWithData:imageData];
UIImageView *imgView = [[UIImageView alloc] initWithImage:image];
[imgView setFrame:CGRectMake(x, y, 140, 90)];
imgView.userInteractionEnabled = YES;
imgView.multipleTouchEnabled = YES;
imgView.backgroundColor = [UIColor blueColor];
// imgView.tag = lito.cId;
// NSLog(#"Tag Id = %#",imgView.tag);
NSLog(#"Tag Id = %#",lito.cId);
UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(onTapImage:)];
tgr.delegate = self;
[imgView addGestureRecognizer:tgr];
[scrollView addSubview:imgView];
tgr.view.tag =(int)[NSString stringWithFormat:#"%#",lito.cId];
NSLog(#"Tag Id = %#",tgr.view.tag);
// NSLog(#"Tag Id = %#",lito.cId);
UILabel *cName = [[UILabel alloc]initWithFrame:CGRectMake(a, b, 130, 20)];
cName.text = lito.cName;
[scrollView addSubview:cName];
//Do the rest of your operations here, don't forget to release the UIImageView
x = x + 150;
a = a + 140;
if(x >300)
{
y = y +140;
x = 20;
b = b +150;
a = 50;
}
//[tgr release];
[imgView release];
}
[self.view addSubview:scrollView];
}
-(void)onTapImage:(UITapGestureRecognizer *)recognizer;
{
NSLog(#"Tapped Image tag: %#", recognizer.view.tag);
//NSLog(#"Tapped Image Id ======================== %#",scrollView.gestureRecognizers);
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Message from mAc" message:[NSString stringWithFormat:#"Tag Id : %#",recognizer.view.tag] delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Ok",nil];
[alert show];
}
- (void)dealloc {
[super dealloc];
[scrollView release];
}
#end

This could be due to you trying to log an int as a string in your -(void)onTapImage:(UITapGestureRecognizer *)recognizer selector:
NSLog(#"Tapped Image tag: %#", recognizer.view.tag);
instead, replace %# with %i:
NSLog(#"Tapped Image tag: %i", recognizer.view.tag);

Related

how to load a UIView on button click

I have Seprate UIView which i am using in UIViewController it initially loads but i want that when that is loaded and I again click on the button then it should reload because it has graph creation method which draws graph when view loaded
#import <UIKit/UIKit.h>
#import "ECGraph.h"
#import "ECGraphItem.h"
#import "CereniaAppDelegate.h"
#class GraphsViewController;
#interface Display : UIView {
NSArray *percentages;
CereniaAppDelegate*appDelegate;
}
#property(nonatomic,retain)NSArray*percentages;
-(void) setPercentageArray:(NSArray*) array;
#end
Implementation files
#import "Display.h"
#import "ECGraph.h"
#import "CereniaAppDelegate.h"
#implementation Display
#synthesize percentages;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
- (void)drawRect:(CGRect)rect {
CGContextRef _context = UIGraphicsGetCurrentContext();
ECGraph *graph = [[ECGraph alloc] initWithFrame:CGRectMake(70,-70,800,200) withContext:
_context isPortrait:NO];
appDelegate=[[UIApplication sharedApplication]delegate];
ECGraphItem *item1 = [[ECGraphItem alloc] init];
ECGraphItem *item2 = [[ECGraphItem alloc] init];
ECGraphItem *item3 = [[ECGraphItem alloc] init];
item1.isPercentage = YES;
int value=(int)roundf(appDelegate.graphValueOne);
item1.yValue=value;
item1.width = 70;
item1.name = #"Unvaccinated horse";
int value1=(int)roundf(appDelegate.graphValueTwo);
item2.isPercentage = YES;
item2.yValue=value1;
item2.width = 70;
item2.name = #"Annually Vaccinated horse";
int value3=(int)roundf(appDelegate.graphValueThree);
item3.isPercentage = YES;
item3.yValue=value3;
item3.width = 70;
item3.name = #"Semi-Annually vaccinated horse";
NSArray *items = [[NSArray alloc] initWithObjects:item1,item2,item3,nil];
[graph setXaxisTitle:#""];
[graph setYaxisTitle:#"Risk"];
[graph setDelegate:self];
[graph setBackgroundColor:[UIColor lightGrayColor]];
[graph drawHistogramWithItems:items lineWidth:2 color:[UIColor blackColor]];
}
-(void) setPercentageArray:(NSArray*) array
{
percentages = array;
NSString*test=[percentages objectAtIndex:0];
NSLog(test);
}
- (void)dealloc {
[super dealloc];
}
#end
You can get any view to redraw with
[view setNeedsDisplay];
So on your refresh button you would update the data used by the Display class and then setNeedsDisplay.

I want to perform different actions on tapping different Buttons created dynamically

Buttons are created dynamically and images are given to them through parsed urls, so i viewDidLoad i created them and released them in viewDidLoad only. So, when i click on Buttons i want to perform different tasks on click on different buttons. but i am not able to do it.
I tried to set Tag to them but again it is of no use as it is giving me EXC_BD_ACCESS.... any Help would be appreciated... :)
Code:-
#class AppDelegate_iPhone,Litofinter,ParsingViewController;
#interface FirstViewController : UIViewController<UIGestureRecognizerDelegate>{
NSMutableArray *array;
NSString *logoString;
AppDelegate_iPhone *appDelegate;
ParsingViewController *obj;
UIScrollView *scrollView;
NSMutableArray *idArray;
NSMutableArray * currentPdfUrl;
}
#property (nonatomic,retain)UIScrollView *scrollView;
//-(void)onTapImage:(UITapGestureRecognizer *)recognizer;
-(void)onTapButton:(id)sender;
#end
#import "FirstViewController.h"
#import "AppDelegate_iPhone.h"
#import "Litofinter.h"
#import "ParsingViewController.h"
#import "MyGesture.h"
#implementation FirstViewController
#synthesize scrollView;
-(id)init{
if(self == [super init]){
obj = [[ParsingViewController alloc] init];
array = [[NSArray alloc] initWithArray: obj.LogoMutableArray];
}
return self;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
int x=20,y=10;
int a=50,b=105;
appDelegate = (AppDelegate_iPhone *)[[UIApplication sharedApplication] delegate];
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0,500, 460)];
scrollView.contentSize = CGSizeMake(320,800);
scrollView.showsVerticalScrollIndicator = YES;
for (Litofinter *lito in appDelegate.logoArray) {
NSString * urlString = [lito.cLogo stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSURL * imageURL = [NSURL URLWithString:urlString];
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage * image = [UIImage imageWithData:imageData];
/*
UIImageView *imgView = [[UIImageView alloc] initWithImage:image];
[imgView setFrame:CGRectMake(x, y, 140, 90)];
imgView.userInteractionEnabled = YES;
imgView.multipleTouchEnabled = YES;
imgView.backgroundColor = [UIColor blueColor];
// imgView.tag = lito.cId;
// NSLog(#"Tag Id = %#",imgView.tag);
NSLog(#"Tag Id = %#",lito.cId);
UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(onTapImage:)];
tgr.delegate = self;
[imgView addGestureRecognizer:tgr];
[scrollView addSubview:imgView];
tgr.view.tag =(int)[NSString stringWithFormat:#"%#",lito.cId];
NSLog(#"Tag Id = %#",tgr.view.tag);
// NSLog(#"Tag Id = %#",lito.cId);
*/
UIButton *imageButton = [UIButton buttonWithType:UIButtonTypeCustom];
[imageButton setFrame:CGRectMake(x, y, 140, 90)];
[imageButton setImage:image forState:UIControlStateNormal];
[imageButton addTarget:self action:#selector(onTapButton:) forControlEvents:UIControlEventTouchUpInside];
[imageButton setTag:lito.cId];
[scrollView addSubview:imageButton];
UILabel *cName = [[UILabel alloc]initWithFrame:CGRectMake(a, b, 130, 20)];
cName.text = lito.cName;
[scrollView addSubview:cName];
//Do the rest of your operations here, don't forget to release the UIImageView
x = x + 150;
a = a + 140;
if(x >300)
{
y = y +140;
x = 20;
b = b +150;
a = 50;
}
//[tgr release];
// [imgView release];
}
[self.view addSubview:scrollView];
}
-(void)onTapButton:(id)sender
{
NSLog(#"Tag Id = %#",self.view.tag);
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Message from mAc" message:#"Tag Id" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Ok",nil];
[alert show];
}
You could change the selector depending on the button when setting the target
[imageButton addTarget:self action:#selector(onTapButton:) forControlEvents:UIControlEventTouchUpInside];
If you want to do it with tags, it could also work, your bad access is coming from
NSLog(#"Tag Id = %#",self.view.tag);
Which is assuming that tag is an NSObject (responding to the description selector), when in fact, it is a numeric value. Change it to:
NSLog(#"Tag Id = %i",self.view.tag);
Also, you want to be careful when using
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
as this is a synchronous API, it's ok if it's local files (although using [UIImage imageNamed:] is more economical as it caches the image data), it's really bad if it's network files (as it blocks the thread until the request ends, which could take ages).

adding scrollView to my UIImageView and Tap Recognizer when i click on image

I wanted to add Scroll view to my UIimage views which are created dynamically and when i click on that images i want some action to be performed but it is not working through my code.Kindly help me...
Code :-
#import <UIKit/UIKit.h>
#class AppDelegate_iPhone,Litofinter,ParsingViewController;
#interface FirstViewController : UIViewController {
NSMutableArray *array;
NSString *logoString;
AppDelegate_iPhone *appDelegate;
ParsingViewController *obj;
UIScrollView *scrollView;
}
#property (nonatomic,retain)UIScrollView *scrollView;
#end
- (void)viewDidLoad {
[super viewDidLoad];
int x=5,y=10;
appDelegate = (AppDelegate_iPhone *)[[UIApplication sharedApplication] delegate];
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
scrollView.contentSize = CGSizeMake(320,460);
scrollView.showsVerticalScrollIndicator = YES;
scrollView.showsHorizontalScrollIndicator = YES;
[self.view addSubview:scrollView];
for (Litofinter *lito in appDelegate.logoArray) {
NSString * urlString = [lito.cLogo stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSURL * imageURL = [NSURL URLWithString:urlString];
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage * image = [UIImage imageWithData:imageData];
UIImageView *imgView = [[UIImageView alloc] initWithImage:image];
[imgView setFrame:CGRectMake(x, y, 196, 90)];
[scrollView addSubview:imgView];
UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(onTapImage)];
[imgView addGestureRecognizer:tgr];
[tgr release];
[imgView release];
//Do the rest of your operations here, don't forget to release the UIImageView
x = x + 200;
}
}
-(void)onTapImage
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Message from mAc" message:#"Trail" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Ok",nil];
[alert show];
}
#end
You probably forget to set the .userInteractionEnabled property of your imageView.
Make sure you set it right.

Loading UIImageView from URL to UIScrollView with UIActivityIndicatorView for each UIImageView

I have the following code which works fine UIScrollView. But when loading the picture can be seen only at the last subview and UIActivityIndicatorView not working for loading each picture.
How to be done for such a task?
myProject2ViewController.h
#import <UIKit/UIKit.h>
#import "PageScrollView.h"
#interface myProject2ViewController : UIViewController
{
NSMutableArray *pages;
PageScrollView *scrollView;
UIView *myOneSubview;
UIImageView *imageView;
UIActivityIndicatorView *myIndicator;
}
#property (nonatomic, retain) PageScrollView *scrollView;
#property (nonatomic, retain) UIImageView *imageView;
#property (nonatomic, retain) UIActivityIndicatorView *myIndicator;
#end
myProject2ViewController.m
#import "myProject2ViewController.h"
#implementation myProject2ViewController
#synthesize scrollView;
#synthesize imageView;
#synthesize myIndicator;
- (void)dealloc
{
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *imageUrls = [NSArray arrayWithObjects:
#"link_1",
#"link_2",
#"link_3",
nil];
pages = [[NSMutableArray alloc] init];
for (int i = 0; i < [imageUrls count]; i++) {
CGRect frameOne = CGRectMake(0.0f, 50.0f, 320.0f, 416.0f);
myOneSubview = [[UIView alloc] initWithFrame:frameOne];
myOneSubview.backgroundColor = [UIColor blackColor];
NSString *imageUrl = [imageUrls objectAtIndex:i];
myIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
myIndicator.center = CGPointMake(160.0f, 130.0f);
myIndicator.hidesWhenStopped = YES;
myIndicator.backgroundColor = [UIColor clearColor];
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 50.0f, 320.0f, 416.0f)];
[NSThread detachNewThreadSelector:#selector(loadImages:) toTarget:self withObject:imageUrl];
[myOneSubview addSubview:imageView];
[pages addObject:myOneSubview];
}
scrollView = [[PageScrollView alloc] initWithFrame:self.view.frame];
scrollView.pages = pages;
scrollView.delegate = self;
self.view = scrollView;
}
- (void)loadImages:(NSString *)string
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[myIndicator startAnimating];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:string]];
UIImage *image = [[UIImage alloc] initWithData:imageData];
[imageView setImage:image];
[image release];
[imageData release];
[self performSelectorOnMainThread:#selector(loadImageComplete) withObject:self waitUntilDone:YES];
[pool drain];
}
-(void)loadImageComplete
{
NSLog(#"Load image complete!");
[myIndicator stopAnimating];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
#end
When you do:
CGRectMake(0.0f, 50.0f, 320.0f, 416.0f);
Inside your for loop, you're specifying those same exact position and dimension for EVERY image you create in the for loop. In other words, every next image you create will sit ontop of the previous image, as a result, you will only see the final image you created in the for loop.
Try adding your counter "i" variable to the X and Y value in CGRectMake() to offset every image that gets subsequently created.
So let say each image was 100 pixel by 100 pixel, then something like:
CGRectMake(0.0f + (i * 100.0f), 50.0f, 320.0f, 416.0f);
Would make each image sit right after the previous one (since each image is 100 pixel wide, we offset it by 100 pixel and use that value as the starting point of the next image). If you add an extra value after (i * 100) so it becomes (i * 100 + 50) then that would give you a left padding of 50 pixels from the previous image.
If your images are different dimensions you'll need to calculate the width and height of the image before hand and factor those into your CGRectMake() method.
Hope that helps.
In addition, instead of creating so many views to add to your scroll view, you could write the code like so:
scrollView = [[UIScrollview alloc] init];
. . .
for(...)
{
UIImageView *currentImage = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f + (i * 100.0f), 50.0f, 320.0f, 416.0f);
[currentImage setImage:[UIImage imageNamed:[imageUrls objectAtIndex:i]]];
. . .
[scrollView addSubview: currentImage];
[currentImage release];
}

UIImageView Allocating Memory Every Time Image Changed - How To Release?

I'm trying to create an application that allows users to go through a set of images like a magazine. I've got one UIView class on top of the AppDelegate and on swipe to the left/right I'm either going forward or backward a page. My problem is that when I swipe and change the image source the program keeps allocating more memory, and I'm not certain where/how to release the previously allocated memory. I've looked in to the difference between imageNamed and imageWithContentsOfFile and I believe I'm using those correctly so I'm stumped. Any help would be much appreciated!
AppDelegate.h
#interface AppDelegate : NSObject {
UIWindow *window;
MagazineWebViewController *webViewController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet MagazineWebViewController *webViewController;
- (void)goToA:(NSNumber *)page;
#end
AppDelegate.m
#implementation AppDelegate
#synthesize window, webViewController;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
webViewController = [[MagazineWebViewController alloc] init];
NSNumber *page = [NSNumber numberWithInt:1];
[webViewController setPage:page];
[window addSubview:webViewController.view];
[window makeKeyAndVisible];
}
- (void)goToA:(NSNumber *)page {
UIImage *image = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"%#", page] ofType:#"png"]];
webViewController.imageView.image = image;
[image release];
[webViewController setPage:page];
}
- (void)dealloc {
[webViewController release];
webViewController = nil;
[window release];
[super dealloc];
}
#end
MagazineWebViewController.h
#interface MagazineWebViewController : UIViewController {
UIImageView *imageView;
NSNumber *page;
}
#property (nonatomic, assign)NSNumber *page;
#property (nonatomic, retain)UIImageView *imageView;
- (void)swipeLeft;
- (void)swipeRight;
- (void)tableOfContents;
#end
MagazineWebViewController.m
#implementation MagazineWebViewController
#synthesize page, tocController, imageView;
- (id)init {
UINavigationBar *navBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, 768, 60)];
navBar.tintColor = [UIColor blackColor];
UIBarButtonItem *contents = [[UIBarButtonItem alloc] initWithTitle:#"Table of Contents" style:UIBarButtonItemStylePlain target:self action:#selector(tableOfContents)];
UINavigationItem *item = [[UINavigationItem alloc] initWithTitle:#"Title"];
item.leftBarButtonItem = contents;
item.hidesBackButton = YES;
[navBar pushNavigationItem:item animated:NO];
[contents release];
[item release];
[self.view addSubview:navBar];
[navBar release];
self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 40, 768, 984)];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"1" ofType:#"png"]];
self.imageView.image = image;
[image release];
self.imageView.contentMode = UIViewContentModeScaleToFill;
UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipeLeft)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipeLeft];
[swipeLeft release];
UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipeRight)];
swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:swipeRight];
[swipeRight release];
[self.view addSubview:self.imageView];
[self.imageView release];
return self;
}
- (void)swipeLeft {
int pageNum = [page intValue];
if (pageNum < 115) {
pageNum++;
UIViewAnimationTransition trans = UIViewAnimationTransitionCurlUp;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationTransition:trans forView:self.view.window cache:YES];
BaseballMagazine2011AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
[appDelegate goToA:[NSNumber numberWithInt:pageNum]];
[UIView commitAnimations];
}
}
- (void)swipeRight {
int pageNum = [page intValue];
if (pageNum > 1) {
pageNum--;
UIViewAnimationTransition trans = UIViewAnimationTransitionCurlDown;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationTransition:trans forView:self.view.window cache:YES];
BaseballMagazine2011AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
[appDelegate goToA:[NSNumber numberWithInt:pageNum]];
[UIView commitAnimations];
}
}
- (void)tableOfContents {
}
- (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.
}
- (void)viewDidUnload {
[imageView release];
imageView = nil;
[page release];
page = nil;
[tocController release];
tocController = nil;
[super viewDidUnload];
}
- (void)dealloc {
[imageView release];
imageView = nil;
[page release];
page = nil;
[super dealloc];
}
#end
In your AppDelegate.m, Try prefacing each reference to webViewController with 'self.'. For example:
self.webViewController = [[MagazineWebViewController alloc] init];
By not referencing the property with self, you are bypassing the properties auto generated setters and getters. And since you set this property to retain, this would bypasses some memory handling logic. I'm pretty sure that is your issue... Hope this helps!
I think it's because you alloc every time an UIImage (like said in the comments).
Did you try not doing allocations ?
self.imageView.image = [ UIImage imageNamed:#"1.png" ];
I think you won't have any problem with this.
But it's difficult to say where come from the leak without all your program ^^
Maybe a retain property or something like that.