问题
I created a subclass of UIButton
which acts as a custom Back button inside UIBarButtonItem
:
- - - - - - - - - - - - - - - - -
| UIBarButtonItem |
| ------------------------ |
/ |
| \ BackButton : UIButton | |
------------------------
| _ _ _ _ _ _ _ _ _ _ _|
As I was switching my code to using UIAppearance
, I noticed that BackButton.Appearance
is, unsuprisingly, inherited from UIButton.Appearance
, and therefore changing it will change all UIButton
s throughout the application and not just my custom buttons.
I know I can use AppearanceWhenContainedIn
but, because UIBarButtonItem
is not an appearance container, I'd have to insert another view between them to recognize back buttons, which I don't want to do.
How do I provide a UIAppearance
proxy for my custom UIButton
subclass that has all the methods available in UIButton.UIButtonAppearance
? I noticed that its constructor is internal.
回答1:
Although there may be a simpler answer, I ended up inheriting from UIAppearance
directly and copy-pasting relevant piece of UIButton
and UIButton+UIButtonAppearance
binding implementation from MonoDevelop C# disassembler and fixing internal fields here and there.
Inside BackButton
:
static readonly IntPtr class_ptr = Class.GetHandle(typeof(BackButton));
public new static BackButtonAppearance Appearance
{
get {
return new BackButtonAppearance (Messaging.IntPtr_objc_msgSend (class_ptr, UIAppearance.SelectorAppearance));
}
}
public new static BackButtonAppearance AppearanceWhenContainedIn (params Type[] containers)
{
return new BackButtonAppearance (UIAppearance.GetAppearance (class_ptr, containers));
}
Nested BackButtonAppearance
:
public class BackButtonAppearance : UIAppearance {
public BackButtonAppearance(IntPtr handle) : base(handle) { }
// These are copied from UIButton disassembly:
static readonly IntPtr selSetBackgroundImageForState_ = Selector.GetHandle ("setBackgroundImage:forState:");
static readonly IntPtr selSetTitleShadowColorForState_ = Selector.GetHandle ("setTitleShadowColor:forState:");
static readonly IntPtr selSetTitleColorForState_ = Selector.GetHandle ("setTitleColor:forState:");
// These are copied from UIButton+UIButtonAppearance disassembly,
// with internal UIButton.selSet* fields replaced by fields declared above.
[Export ("setBackgroundImage:forState:"), CompilerGenerated]
public virtual void SetBackgroundImage (UIImage image, UIControlState forState)
{
UIApplication.EnsureUIThread ();
if (this.IsDirectBinding) {
Messaging.void_objc_msgSend_IntPtr_UInt32 (base.Handle, selSetBackgroundImageForState_, (image != null) ? image.Handle : IntPtr.Zero, (uint)forState);
} else {
Messaging.void_objc_msgSendSuper_IntPtr_UInt32 (base.SuperHandle, selSetBackgroundImageForState_, (image != null) ? image.Handle : IntPtr.Zero, (uint)forState);
}
}
[Export ("setTitleColor:forState:"), CompilerGenerated]
public virtual void SetTitleColor (UIColor color, UIControlState forState)
{
UIApplication.EnsureUIThread ();
if (this.IsDirectBinding) {
Messaging.void_objc_msgSend_IntPtr_UInt32 (base.Handle, selSetTitleColorForState_, (color != null) ? color.Handle : IntPtr.Zero, (uint)forState);
} else {
Messaging.void_objc_msgSendSuper_IntPtr_UInt32 (base.SuperHandle, selSetTitleColorForState_, (color != null) ? color.Handle : IntPtr.Zero, (uint)forState);
}
}
[Export ("setTitleShadowColor:forState:"), CompilerGenerated]
public virtual void SetTitleShadowColor (UIColor color, UIControlState forState)
{
UIApplication.EnsureUIThread ();
if (this.IsDirectBinding) {
Messaging.void_objc_msgSend_IntPtr_UInt32 (base.Handle, selSetTitleShadowColorForState_, (color != null) ? color.Handle : IntPtr.Zero, (uint)forState);
} else {
Messaging.void_objc_msgSendSuper_IntPtr_UInt32 (base.SuperHandle, selSetTitleShadowColorForState_, (color != null) ? color.Handle : IntPtr.Zero, (uint)forState);
}
}
}
This allows me to bring my custom view to support UIAppearance-style API.
回答2:
Adding answer for completeness.
Use BackButton.GetAppearance<BackButton>()
instead BackButton.Appearance
.
来源:https://stackoverflow.com/questions/12939321/how-to-create-a-uiappearance-proxy-for-an-inherited-view-in-monotouch