Advertise Books Events Forum News Social Networking Support Us

sdkIQ for iPhone
($4.99)

Shape Up
($0.99)

Your First iPhone App
($1.99)

iVidCam Free
(free)

Kid Art
($0.99)

iPUBQUIZ
(£1.19)

ArtStudio
($3.99)

Want your application or service advertised on iPhone Dev SDK?

Go Back   iPhone Dev SDK Forum > iPhone SDK Development Forums > iPhone SDK Development

Reply
 
LinkBack Thread Tools Display Modes
Old 11-08-2008, 10:53 AM   #1 (permalink)
New Member
 
Join Date: Nov 2008
Posts: 1
Default Add toolbar on top of keyboard

Some apps – like Twinkle e.g. – add a custom toolbar on top of the existing keyboard mostly to add "send" or "done" buttons. These toolbars animate in from the bottom along with the keyboard.

Is anybody aware of how this can be done?
BeSharp is offline   Reply With Quote
Old 11-08-2008, 11:24 AM   #2 (permalink)
New Member
 
RickMaddy's Avatar
 
Join Date: Oct 2008
Location: Denver, CO
Posts: 2,121
Default

I added a label to the top of the keyboard in my app. I can explain how to add a subview to the keyboard.

First you need to register to receive keyboard notifications. I have the following in my app delegate:

Code:
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
Here is a snippet from my 'keyboardWillShow'. This allows you to get info about the size and location of the keyboard.

Code:
- (void)keyboardWillShow:(NSNotification *)note {
    NSDictionary *info = [note userInfo];
    NSValue *beginPoint = [info objectForKey:UIKeyboardCenterBeginUserInfoKey];
    NSValue *endPoint = [info objectForKey:UIKeyboardCenterEndUserInfoKey];
    NSValue *keyBounds = [info objectForKey:UIKeyboardBoundsUserInfoKey];
    
    CGPoint pntBegin;
    CGPoint pntEnd;
    CGRect bndKey;
    [beginPoint getValue:&pntBegin];
    [endPoint getValue:&pntEnd];
    [keyBounds getValue:&bndKey];
}
The next trick is actually finding the keyboard view. The keyboard view is actually in a second UIWindow separate from your app's main window. So you need to write code to walk all of the windows in the app. Then for each window you need to find the one that has a subview containing the keyboard. I do this by finding the subview whose description contains the string 'UIKeyboard'. I do this to avoid compiler warnings because the real class for the keyboard view isn't exposed via the SDK. But all you need is a UIView reference anyway.

Once you get the keyboard view, along with the coordinates you got earlier, you can now add a subview to the keyboard view.

I'm not going to give all the code because I worked very hard to figure this out on my own and it gives my app a little edge. Hopefully the info I gave will get you far enough to figure the details out.

Enjoy.
RickMaddy is offline   Reply With Quote
Old 12-15-2008, 10:33 PM   #3 (permalink)
New Member
 
Join Date: Dec 2008
Posts: 2
Default

I stumbled upon this post and found it very helpful.

@RickMaddy, I'm trying to do something similar to what you've described, but instead of adding a UILabel, I'm trying to add a UIToolbar and a UIBarButtonItem that the user can click. I've been able to add the toolbar and the button, but my problem is, the toolbar button is only able to be clicked if it's on TOP of the keyboard. If I add the toolbar as a subview ABOVE the keyboard, then the button cannot be clicked. Here's the relevant code snippet from the keyboardWillShow: method. (Thanks to both RickMaddy and keyboardcowboy for the posts. Much of the code below is taken from their posts.)

Code:
- (void)keyboardWillShow:(NSNotification *)note
{
    NSDictionary *info = [note userInfo];
    NSValue *keyBounds = [info objectForKey:UIKeyboardBoundsUserInfoKey];

    CGRect bndKey;
    [keyBounds getValue:&bndKey];

	UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, -40, bndKey.size.width, 40)];
	UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Test button" style:UIBarButtonItemStyleBordered target:self action:@selector(buttonClicked:)];
	NSArray *items = [[NSArray alloc] initWithObjects:barButtonItem, nil];
	[toolbar setItems:items];
	[items release];

	UIWindow* tempWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:1];
	UIView* keyboard;
	for(int i = 0; i < [tempWindow.subviews count]; i++)
	{
		//Get a reference of the current view 
		keyboard = [tempWindow.subviews objectAtIndex:i];
		
		//Check to see if the description of the view we have referenced is "UIKeyboard" if so then we found
		//the keyboard view that we were looking for
		if([[keyboard description] hasPrefix:@"<UIKeyboard"] == YES)
		{
			[keyboard addSubview:toolbar];
		}
	}
}

- (void)buttonClicked:(NSNotification *)note
{
	UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"You clicked the keyboard button!" message:@"Hey!  You clicked the button on top of the keyboard." delegate:self cancelButtonTitle:@"Yep" otherButtonTitles:nil];
	[alert show];
	[alert release];
}
With the above code, the toolbar is added just above the keyboard, and there's a button labeled "Test button" on the toolbar. However, it doesn't accept user interaction. BUT if you change the code where the toolbar is created to the following (keep everything else the same):

Code:
	UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, bndKey.size.width, 40)];
Now, the toolbar is added ON TOP OF (overlaying) the keyboard (not where we want it) but the button works in this case.

Any suggestions for how to get the button to work when it's just above the keyboard?
densming is offline   Reply With Quote
Old 12-15-2008, 10:43 PM   #4 (permalink)
New Member
 
RickMaddy's Avatar
 
Join Date: Oct 2008
Location: Denver, CO
Posts: 2,121
Default

I'm not positive but I think the problem lies with the fact that the button is actually located with a bounding box outside of it's parent's (grandparent's actually) view. I saw this once before. Buttons don't get touch events if their bounding box is outside the frame of their parent view.

I've never tried this, and it may make the code even more fragile to future changes by Apple, but you might be able to resize and reposition the keyboard view so it extends high enough so you can place the toolbar within its frame. Yuck.
RickMaddy is offline   Reply With Quote
Old 12-15-2008, 10:46 PM   #5 (permalink)
New Member
 
RickMaddy's Avatar
 
Join Date: Oct 2008
Location: Denver, CO
Posts: 2,121
Default

Ooh, I just had an idea that might work. Add the toolbar with the negative offset so it animates and appears in the correct position. Then after you get the 'keyboard did appear' event, change the toolbar's superview to the window or some other, bigger view. When you get the event 'keyboard will hide', put it back so the superview is the keyboard again so it properly animates out of view.

Just guessing but this may work.
RickMaddy is offline   Reply With Quote
Old 12-16-2008, 12:39 AM   #6 (permalink)
New Member
 
Join Date: Dec 2008
Posts: 2
Default

Great suggestion! You were right on. On the keyboardDidShow: method, I removed the toolbar from the keyboard's view and added it as a subview to the UIWindow and now it works great! Your help is much appreciated.
densming is offline   Reply With Quote
Old 12-16-2008, 09:01 AM   #7 (permalink)
Registered Member
 
Join Date: Nov 2008
Posts: 59
Default

thank,for discussing i am searching for some UIKeyboard related tutorial
you solve my problem also.
thank's again
Thelordofring is offline   Reply With Quote
Old 01-18-2009, 03:32 PM   #8 (permalink)
New Member
 
Join Date: Jan 2009
Posts: 4
Default

Quote:
Originally Posted by RickMaddy View Post
...Then after you get the 'keyboard did appear' event, change the toolbar's superview to the window or some other, bigger view...
How do you change the superview? I can't get it to work... :-/
msson is offline   Reply With Quote
Old 01-18-2009, 07:35 PM   #9 (permalink)
New Member
 
RickMaddy's Avatar
 
Join Date: Oct 2008
Location: Denver, CO
Posts: 2,121
Default

[someView removeFromSuperview];
[newParentView addSubview:someView];
RickMaddy is offline   Reply With Quote
Old 01-19-2009, 02:49 AM   #10 (permalink)
New Member
 
Join Date: Jan 2009
Posts: 4
Default

Thats what I tried. Just tried to add it to the wrong superview. :P
Thanks for the quick reply anyway.
msson is offline   Reply With Quote
Old 03-21-2009, 04:21 PM   #11 (permalink)
New Member
 
Join Date: Mar 2009
Posts: 1
Default

Thank you for discussing. To which UIWindow did you add the toolbar as a subView? ... After I do removeFromSuperview the toolbar disappears.

Thanks.
fmk11 is offline   Reply With Quote
Old 04-20-2009, 11:08 AM   #12 (permalink)
New Member
 
Join Date: Jan 2009
Posts: 2
Default My Solution

This thread got me started, so here's my solution. As with the intial approach on this thread I retrieve the keyboard from the notification. I then take it one step further. After adding my UIToolbar to the keyboard I rebound the keyboard view. So the toolbar and they keyboard will both collect touch events within the Keyboard class frame. Handles rotation as well.

Code:
- (void)keyboardWillShow:(NSNotification *)notification 
{	
    for (UIWindow *keyboardWindow in [[UIApplication sharedApplication] windows]) {
		
        // Now iterating over each subview of the available windows
        for (UIView *keyboard in [keyboardWindow subviews]) {
			
            // Check to see if the description of the view we have referenced is UIKeyboard.
            // If so then we found the keyboard view that we were looking for.
            if([[keyboard description] hasPrefix:@"<UIKeyboard"] == YES) {
				NSValue *v = [[notification userInfo] valueForKey:UIKeyboardBoundsUserInfoKey];
				CGRect kbBounds = [v CGRectValue];
				
				if(keyboardToolbar == nil) {
					keyboardToolbar = [[UIToolbar alloc] initWithFrame:CGRectZero];
					keyboardToolbar.barStyle = UIBarStyleBlackTranslucent;
					
					UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Dismiss" style:UIBarButtonItemStyleBordered target:self action:@selector(dismissKeyboard:)];
					NSArray *items = [[NSArray alloc] initWithObjects:barButtonItem, nil];
					[keyboardToolbar setItems:items];	
					[items release];	
				}				
				
				[keyboardToolbar removeFromSuperview];
				keyboardToolbar.frame = CGRectMake(0, 0, kbBounds.size.width, 30);
				[keyboard addSubview:keyboardToolbar];
				keyboard.bounds = CGRectMake(kbBounds.origin.x, kbBounds.origin.y, kbBounds.size.width, kbBounds.size.height + 60);
				
				for(UIView* subKeyboard in [keyboard subviews]) {
					if([[subKeyboard description] hasPrefix:@"<UIKeyboardImpl"] == YES) {
						subKeyboard.bounds = CGRectMake(kbBounds.origin.x, kbBounds.origin.y - 30, kbBounds.size.width, kbBounds.size.height);	
					}						
				}
            }
        }
    }
}
jtmille3 is offline   Reply With Quote
Old 04-21-2009, 06:41 PM   #13 (permalink)
New Member
 
Join Date: Apr 2009
Posts: 2
Default followup question

Excuse my noob question, but I'm trying out your code, and xcode says keyboardToolbar is undeclared when I compile... what should it be declared as?
gjosef is offline   Reply With Quote
Old 06-01-2009, 08:17 PM   #14 (permalink)
New Member
 
Join Date: Apr 2009
Posts: 3
Default

Maybe I'm doing something wrong but with the code above, I have noticed that it adds multiple toolbars one over the other and soon enough the Dismiss button doesn't work. If I use it on one tab view then it's fine but the problem is I use it on muliple tabs which is why it keeps adding I think. If anyone knows how to resolve this, please share.

Last edited by meridius; 06-02-2009 at 06:36 AM.
meridius is offline   Reply With Quote
Old 07-07-2009, 08:08 AM   #15 (permalink)
New Member
 
Join Date: Jan 2009
Posts: 2
Default Toolbar(TextField) on Keyboard Revisited

Having not visited this topic in quite some time here is a brief update...

This code is working in my demo to this point. I setup a Messages type app for educational purposes. Table datasource and delegates not provided. Obviously the messages array isn't provided either.

Notes based on comments:

1.) You have to make sure you don't keep stacking controls on top of the keyboard. As you can see in keyboardDidHide method that I remove the text field from the keyboard and apply it back to the bottom of the tableview. And vice versa when when the keyboard is called again.

2.) Obviously this is not all the code but enough to make it work. Types are implied by what is being instantiated. If there is no type defined obviously it's defined in the header.

3.) IMPORTANT! Have not checked the code for memory leaks. Use at your own discretion.

Code:
@synthesize tableView;
@synthesize textField;

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.tableView.separatorColor = [UIColor clearColor];
    self.tableView.backgroundColor = [UIColor colorWithRed:(0xDD/255.0) green:(0xE2/255.0) blue:(0xEB/255.0) alpha:1.0];
    self.view.backgroundColor = [UIColor colorWithRed:(0xDD/255.0) green:(0xE2/255.0) blue:(0xEB/255.0) alpha:1.0];
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:self.view.window]; 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:self.view.window]; 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:self.view.window]; 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:self.view.window]; 
}

const int TEXTFIELD_HEIGHT = 30;

- (void) viewWillAppear:(BOOL)animated {    
    if(self.textField == nil) {
        self.textField = [[KeyboardTextField alloc] initWithFrame:CGRectZero];
        self.textField.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"border.png"]];
        self.textField.delegate = self;
        self.textField.returnKeyType = UIReturnKeySend;
        [self.view addSubview:self.textField];
        self.textField.frame = CGRectMake(0, self.view.frame.size.height - TEXTFIELD_HEIGHT, self.view.frame.size.width, TEXTFIELD_HEIGHT);
    }    

    NSString *dummyData = [[NSBundle mainBundle] pathForResource:@"dummy" ofType:@"plist"];
    messages = [[NSArray arrayWithContentsOfFile:dummyData] retain];
    
    if(messages.count > 0) {
        NSDictionary* dictionary = [messages objectAtIndex:0];
        self.title = [dictionary objectForKey:@"sender"];   
    }    
    
    [self.tableView reloadData]; // manually sync datasource so we can scroll to the bottom
}

- (void) viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self scrollToBottom];
}

- (void)keyboardWillShow:(NSNotification *)notification {	
    for (UIWindow *keyboardWindow in [[UIApplication sharedApplication] windows]) {
		
        // Now iterating over each subview of the available windows
        for (UIView *keyboard in [keyboardWindow subviews]) {
			
            // Check to see if the description of the view we have referenced is UIKeyboard.
            // If so then we found the keyboard view that we were looking for.
            if([[keyboard description] hasPrefix:@"<UIKeyboard"] == YES) {
				NSValue *v = [[notification userInfo] valueForKey:UIKeyboardBoundsUserInfoKey];
				CGRect kbBounds = [v CGRectValue];
				
                keyboard.bounds = CGRectMake(kbBounds.origin.x, kbBounds.origin.y, kbBounds.size.width, kbBounds.size.height + (TEXTFIELD_HEIGHT * 2));
				textField.frame = CGRectMake(0, 0, kbBounds.size.width, TEXTFIELD_HEIGHT);
                [textField removeFromSuperview];
				[keyboard addSubview:textField];
				
				for(UIView* subKeyboard in [keyboard subviews]) {
					if([[subKeyboard description] hasPrefix:@"<UIKeyboardImpl"] == YES) {
						keyboardBounds = CGRectMake(kbBounds.origin.x, kbBounds.origin.y - TEXTFIELD_HEIGHT, kbBounds.size.width, kbBounds.size.height);	
                        subKeyboard.bounds = keyboardBounds;
					}						
				}
            }
        }
    }
}

- (void)keyboardDidShow:(NSNotification*)notification {
    self.tableView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - keyboardBounds.size.height - TEXTFIELD_HEIGHT);
    NSIndexPath* indexPath = [NSIndexPath indexPathForRow:messages.count - 1 inSection:0];
    [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}

- (void)keyboardWillHide:(NSNotification*)notification {
    textField.text = nil;
}

- (void)keyboardDidHide:(NSNotification*)notification {
    [textField removeFromSuperview];
    textField.text = nil;
    [self.view addSubview:textField];
    textField.frame = CGRectMake(0, self.view.frame.size.height - TEXTFIELD_HEIGHT, textField.frame.size.width, textField.frame.size.height);
    self.tableView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - TEXTFIELD_HEIGHT);
    [self scrollToBottom];
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    [self.textField resignFirstResponder];
    return NO;
}

- (void) scrollToBottom {
    NSIndexPath* indexPath = [NSIndexPath indexPathForRow:messages.count - 1 inSection:0];
    [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];        
}
jtmille3 is offline   Reply With Quote
Old 07-27-2009, 11:54 AM   #16 (permalink)
Registered Member
 
Join Date: Feb 2009
Location: India
Posts: 125
Default

Thanks guys..... this thread is really helpful
-John
JohnMabassa is offline   Reply With Quote
Old 07-30-2009, 07:48 AM   #17 (permalink)
I'll build your app!
 
chaseacton's Avatar
 
Join Date: Feb 2009
Location: United States
Posts: 404
Send a message via AIM to chaseacton
Default

Can someone post the xcode project for this please.

Thanks,
Chase
chaseacton is offline   Reply With Quote
Old 08-01-2009, 08:51 AM   #18 (permalink)
Registered Member
 
Join Date: Feb 2009
Location: India
Posts: 125
Default

Here is the link to the implementation..... I have used this logic to my forms and added next and previous button too.....Add Tool bar to keyboard

While working on this method further I came across some issue, I had som textfields where I dont want the toolbar,there also i am getting the keyboard with the toolbar. It seems once created it is not going away. Ny idea how to do fix this issue?
-John

Last edited by JohnMabassa; 08-05-2009 at 07:51 AM.
JohnMabassa is offline   Reply With Quote
Old 08-10-2009, 02:16 AM   #19 (permalink)
Registered Member
 
Join Date: Feb 2009
Location: India
Posts: 125
Default

Quote:
Originally Posted by JohnMabassa View Post
Here is the link to the implementation..... I have used this logic to my forms and added next and previous button too.....Add Tool bar to keyboard

While working on this method further I came across some issue, I had som textfields where I dont want the toolbar,there also i am getting the keyboard with the toolbar. It seems once created it is not going away. Ny idea how to do fix this issue?
-John
Any ideas on the above problem?
JohnMabassa is offline   Reply With Quote
Old 08-11-2009, 04:56 AM   #20 (permalink)
Registered Member
 
Join Date: May 2009
Posts: 17
Default

Quote:
Originally Posted by JohnMabassa View Post
Any ideas on the above problem?
Have the same problem
meeper is offline   Reply With Quote
Old 09-08-2009, 03:22 AM   #21 (permalink)
Registered Member
 
Join Date: Aug 2009
Posts: 51
Default

Quote:
Originally Posted by meeper View Post
Have the same problem
it seems the easiest way in my head would be to make a BOOL = YES whenever one of your textFields is pressed.

under the didStartEditing (if that's what it's called, I forget) make your BOOL yes.

then surround all your code to show the toolbar with an if clause...
keyboardDidShow {
if (myBOOL == YES) {
//do all your code to show the toolbar here
}
}

under didFinishEditing (or whatever it's called) make your BOOL no.

i'll quite possible be trying something very similar quite soon, so i'll post some code example if i do.

if somebody beats me to it, please post your code here!

cheerio,
tim
ziophase is offline   Reply With Quote
Old 12-17-2009, 01:26 PM   #22 (permalink)
Registered Member
 
havenjoy's Avatar
 
Join Date: Dec 2009
Posts: 6
Default

Hello mates
I know it's a quite old thread...
But I'm having the same problem of Meeper and JohnMabassa...and the code that ziph~~ shared isn't working for me >.>
Can someone help???
__________________
Hi, i am a Jogos Gratis freak
havenjoy is offline   Reply With Quote
Old 01-21-2010, 04:44 AM   #23 (permalink)
Registered Member
 
Join Date: Jul 2009
Posts: 29
Default

hi
I am using both textview and text field in single view controller.
Toolbar is showing fine upon keyboard when clicking in textview but when clicking text field again toolbar is showing upon keyboard. but i don't want toolbar in case of text field.
this is my code

- (void)keyboardWillShowNSNotification *)notification
{
for (UIWindow *keyboardWindow in [[UIApplication sharedApplication] windows]) {

// Now iterating over each subview of the available windows
for (UIView *keyboard in [keyboardWindow subviews]) {

// Check to see if the description of the view we have referenced is UIKeyboard.
// If so then we found the keyboard view that we were looking for.
if([[keyboard description] hasPrefix:@"<UIKeyboard"] == YES) {
NSValue *v = [[notification userInfo] valueForKey:UIKeyboardBoundsUserInfoKey];
CGRect kbBounds = [v CGRectValue];

mKeyboardToolbar.barStyle = UIBarStyleBlackOpaque;




UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemD one
target:self action:@selector(dismissKeyboard)];
UIBarButtonItem *flex = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemF lexibleSpace target:nil action:nil];
NSArray *items = [[NSArray alloc] initWithObjects:flex, barButtonItem, nil];
[mKeyboardToolbar setItems:items];
[items release];
[barButtonItem release];
[flex release];

[mKeyboardToolbar removeFromSuperview];
mKeyboardToolbar.frame = CGRectMake(0, 0, kbBounds.size.width, 50);
[keyboard addSubview:mKeyboardToolbar];

if(textField)
mKeyboardToolbar.hidden = YES;
else
mKeyboardToolbar.hidden = NO;


keyboard.bounds = CGRectMake(kbBounds.origin.x, kbBounds.origin.y , kbBounds.size.width, kbBounds.size.height + 100);

for(UIView* subKeyboard in [keyboard subviews]) {
if([[subKeyboard description] hasPrefix:@"<UIKeyboardImpl"] == YES) {
subKeyboard.bounds = CGRectMake(kbBounds.origin.x, kbBounds.origin.y -50, kbBounds.size.width, kbBounds.size.height);
}
}
}
}
}

}
subodh kumar is offline   Reply With Quote
Reply

Bookmarks

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On


Enter the iPhone App Challenge!  Win $500!
» Advertisements
» Stats
Members: 24,130
Threads: 38,899
Posts: 170,660
Top Poster: smasher (2,565)
Welcome to our newest member, HunBirk427
Powered by vBadvanced CMPS v3.1.0

All times are GMT -5. The time now is 02:38 PM.
Powered by vBulletin® Version 3.8.0
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.2.0