在 iPhone 上面開發應用程式時, 在使用輸入鍵盤時, 或多或少都會遇到客制化鍵盤的問題, 這邊筆者以
簡單的數字鍵盤來示範客制化的動作. 這部份我想網路上已經有不少 sample code , 但大部份基本上都是
以 SDK 3.x 的版本去實作, 以"特定寫法"來實作客制化在 iOS4 會有問題, 這部份稍候會提到兩版本的差異.
上述看到的例子是 UIKeyboardTypeNumberPad 搭配 "Done" 的圖示所組合而成的. 在開
始介紹如何實作之前, 先稍微提一下網路上查到的一些範例寫法. 因為 SDK 升版之
後在架構上有做了些修改, 所以導致行為上的不正確. 以下面這例子為例, 它可以正
常的在 iOS4 之前的版本運行, 但在 iOS4 上卻會有看不到上面 "Done" 圖示的問題.
上述這段代碼主要原理是透過跟 OS 註冊 keyboard 相關的 notification, 並在顯示
keyboard 時, 在 keyboard view 上添加所需要的特定 UIView, 簡單流程大致如下
1. 註冊 UIKeyboardWillShowNotification : 當 keyboard 要秀時, OS 就會呼叫
keyboardWillShow
2. 當被 keyboardWillShow 叫用時, 搜尋屬於 keyboard 的 view
if ([[keyboard description ] hasPrefix : @"<UIKeyboard" ] == YES )
3. 當找到所需要的 view 時, 再將需要的 view 加入即可
[keyboard addSubview :doneButton];
上面就是一個 customized keyboard 的簡單實作流程. 但是為什麼這段 code 會無法
在 iOS4 上正確執行呢? 問題點主要出在上述的第 2 個步驟.
在舊的 SDK 中, 當 UIKeyboardWillShowNotification 事件發生且叫用 keyboardWillShow
時, 此時的 keyboard view 已經被添加到 windows 裡了, 但是在 iOS4 的世界中, 相同
情況發生時, keyboard view 卻會在下個 event loop 裡才會被添加到 windows 中, 也
就是因為如此, 所以上述
[[[ UIApplication sharedApplication ] windows ] objectAtIndex : 1 ];
會找不到 keyboard view. 除了這原因以外, 還有另一個重要的差異性, 第 2 步驟所比
對的 @"<UIKeyboard" 字串在 iOS4 中也被修正過, 它被藏在 @"<UIPeripheralHostView"
裡.
針對這兩點, 所以將只要將之修正即可正常的在 iOS4 上執行
1. keyboard view
既然知道是 keyboard view 會在下個 event loop 才會被放到 windows 裡, 所以我們
可以透過下面方式將 keyboardWillShow 延遲叫用
[ self performSelector : @selector ( keyboardWillShow :) withObject : nil afterDelay : 0 ];
2. 修正比對 @"<UIKeyboard" 的方式
簡單的數字鍵盤來示範客制化的動作. 這部份我想網路上已經有不少 sample code , 但大部份基本上都是
以 SDK 3.x 的版本去實作, 以"特定寫法"來實作客制化在 iOS4 會有問題, 這部份稍候會提到兩版本的差異.
上述看到的例子是 UIKeyboardTypeNumberPad 搭配 "Done" 的圖示所組合而成的. 在開
始介紹如何實作之前, 先稍微提一下網路上查到的一些範例寫法. 因為 SDK 升版之
後在架構上有做了些修改, 所以導致行為上的不正確. 以下面這例子為例, 它可以正
常的在 iOS4 之前的版本運行, 但在 iOS4 上卻會有看不到上面 "Done" 圖示的問題.
- (
void
)loadView{
...
textFieldContent
.
delegate
=
self
;
textFieldContent
.
placeholder
=
@"press me"
;
textFieldContent
.
keyboardType
=
UIKeyboardTypeNumberPad
;
textFieldContent
.
returnKeyType
=
UIReturnKeyDone
;
[
self
.
view
addSubview
:
textFieldContent
];
[
textFieldContent
release
];
[[
NSNotificationCenter
defaultCenter
]
addObserver
:
self
selector
:
@selector
(
keyboardWillShow
:)
name
:
UIKeyboardWillShowNotification
object
:
nil
];
}
- (
void
)keyboardWillShowOnDelay:(
NSNotification
*)notification{
UIButton
*doneButton = [
UIButton
buttonWithType
:
UIButtonTypeCustom
];
doneButton.
frame
=
CGRectMake
(
0
,
163
,
106
,
53
);
doneButton.
adjustsImageWhenHighlighted
=
NO
;
[doneButton
setImage
:[
UIImage
imageNamed
:
@"DoneUp.png"
]
forState
:
UIControlStateNormal
];
[doneButton
setImage
:[
UIImage
imageNamed
:
@"DoneDown.png"
]
forState
:
UIControlStateHighlighted
];
[doneButton
addTarget
:
self
action
:
@selector
(
doneButton
:)
forControlEvents
:
UIControlEventTouchUpInside
];
UIWindow
* tempWindow = [[[
UIApplication
sharedApplication
]
windows
]
objectAtIndex
:
1
];
UIView
* keyboard;
for
(
int
i=
0
; i<[tempWindow.
subviews
count
]; i++) {
keyboard = [tempWindow.
subviews
objectAtIndex
:i];
if
([[keyboard
description
]
hasPrefix
:
@"<UIKeyboard"
] ==
YES
)
[keyboard
addSubview
:doneButton];
}
}
上述這段代碼主要原理是透過跟 OS 註冊 keyboard 相關的 notification, 並在顯示
keyboard 時, 在 keyboard view 上添加所需要的特定 UIView, 簡單流程大致如下
1. 註冊 UIKeyboardWillShowNotification : 當 keyboard 要秀時, OS 就會呼叫
keyboardWillShow
2. 當被 keyboardWillShow 叫用時, 搜尋屬於 keyboard 的 view
if ([[keyboard description ] hasPrefix : @"<UIKeyboard" ] == YES )
3. 當找到所需要的 view 時, 再將需要的 view 加入即可
[keyboard addSubview :doneButton];
上面就是一個 customized keyboard 的簡單實作流程. 但是為什麼這段 code 會無法
在 iOS4 上正確執行呢? 問題點主要出在上述的第 2 個步驟.
在舊的 SDK 中, 當 UIKeyboardWillShowNotification 事件發生且叫用 keyboardWillShow
時, 此時的 keyboard view 已經被添加到 windows 裡了, 但是在 iOS4 的世界中, 相同
情況發生時, keyboard view 卻會在下個 event loop 裡才會被添加到 windows 中, 也
就是因為如此, 所以上述
[[[ UIApplication sharedApplication ] windows ] objectAtIndex : 1 ];
會找不到 keyboard view. 除了這原因以外, 還有另一個重要的差異性, 第 2 步驟所比
對的 @"<UIKeyboard" 字串在 iOS4 中也被修正過, 它被藏在 @"<UIPeripheralHostView"
裡.
針對這兩點, 所以將只要將之修正即可正常的在 iOS4 上執行
1. keyboard view
既然知道是 keyboard view 會在下個 event loop 才會被放到 windows 裡, 所以我們
可以透過下面方式將 keyboardWillShow 延遲叫用
[ self performSelector : @selector ( keyboardWillShow :) withObject : nil afterDelay : 0 ];
2. 修正比對 @"<UIKeyboard" 的方式
if ([[possibleKeyboard
description]
hasPrefix:
@"<UIPeripheralHostView"])
possibleKeyboard = [[possibleKeyboard
subviews]
objectAtIndex:
0];
if ([[possibleKeyboard
description]
hasPrefix:
@"<UIKeyboard"])
{
foundKeyboard = possibleKeyboard;
break;
}
經過上述兩個修正之後的 code 大概會如下 :
[[
NSNotificationCenter
defaultCenter
] addObserver
:
self
selector:
@selector(
keyboardWillShowOnDelay:)
name:
UIKeyboardWillShowNotification
object:
nil];
- (
void)keyboardWillShowOnDelay:(
NSNotification *)notification
{
[
self
performSelector
:
@selector
(
keyboardWillShow
:) withObject
:
nil
afterDelay
:
0
];
}
- (
void)keyboardWillShow:(
NSNotification *)notification
{
UIView *foundKeyboard =
nil;
UIWindow *keyboardWindow =
nil;
for (
UIWindow *testWindow
in [[
UIApplication
sharedApplication]
windows])
{
if (![[testWindow
class]
isEqual:[
UIWindow
class]])
{
keyboardWindow = testWindow;
break;
}
}
if (!keyboardWindow)
return;
for (
UIView *possibleKeyboard
in [keyboardWindow
subviews])
{
//iOS3
if ([[possibleKeyboard
description]
hasPrefix:
@"<UIKeyboard"])
{
foundKeyboard = possibleKeyboard;
break;
}
else
{
// iOS 4 sticks the UIKeyboard inside a UIPeripheralHostView.
if ([[possibleKeyboard
description]
hasPrefix:
@"<UIPeripheralHostView"])
{
possibleKeyboard = [[possibleKeyboard
subviews]
objectAtIndex:
0];
}
if ([[possibleKeyboard
description]
hasPrefix:
@"<UIKeyboard"])
{
foundKeyboard = possibleKeyboard;
break;
}
}
}
if (foundKeyboard)
{
// create custom button
UIButton
*doneButton = [
UIButton
buttonWithType
:UIButtonTypeCustom
];
doneButton.
frame =
CGRectMake(
0,
163,
106,
53);
doneButton.adjustsImageWhenHighlighted
=
NO
;
[doneButton setImage
:[
UIImage
imageNamed
:
@"DoneUp.png"
]forState
:UIControlStateNormal
];
[doneButton setImage
:[
UIImage
imageNamed
:
@"DoneDown.png"
]forState
:UIControlStateHighlighted
];
[doneButton addTarget
:
self
action
:
@selector
(
doneButton
:)forControlEvents
:UIControlEventTouchUpInside
];
[foundKeyboard
addSubview:doneButton];
}
}