Ios 自定义对象的UI外观代理
我有一个自定义对象,它继承自NSObject。 此对象执行“某些操作”,其中之一是使用一些UIKit对象(UILabel、UIButtons ecc ecc…)创建UIView。 此对象具有一些属性,如:textColor、字体、backgroundColor。。。用于自定义包含的UIKit对象的外观 我想为这个对象的所有创建实例自定义这个属性“一次”,我已经看过UIAppearance协议 标准UIKit对象已经符合UIAppearance协议,但我不想在所有UILabel或UIButton上应用该样式。我只想将样式应用于对象实例中包含的UILabel和UIButton。 此外,我不能(也不希望)在包含时使用AppearanceIn:因为使用我的自定义对象的开发人员可能不知道其中“包含”了什么类型的对象 因此,我正在研究如何使自定义对象符合UIAppearance协议 看来它必须实施Ios 自定义对象的UI外观代理,ios,objective-c,uiappearance,Ios,Objective C,Uiappearance,我有一个自定义对象,它继承自NSObject。 此对象执行“某些操作”,其中之一是使用一些UIKit对象(UILabel、UIButtons ecc ecc…)创建UIView。 此对象具有一些属性,如:textColor、字体、backgroundColor。。。用于自定义包含的UIKit对象的外观 我想为这个对象的所有创建实例自定义这个属性“一次”,我已经看过UIAppearance协议 标准UIKit对象已经符合UIAppearance协议,但我不想在所有UILabel或UIButton上
+ (id)appearance
方法。此方法应返回一个代理对象,您可以在其中发送所有自定义设置。
但是,查看UIKit对象的外观方法,我看到返回了一个私有对象。
外观上属于类的对象
所以,苹果似乎没有给我一个标准的代理对象来定制我自己的,我必须从头开始创建。
这是对的还是我失去了什么
谢谢退房
基本上,您只需要使用UI\u外观\u选择器
标记您的属性,只要您的类是UI视图
的子类,它将处理私有\u外观
类的实际销售,一切都可以正常工作
编辑: 您最好使用单例和一些类方法来运行自己的解决方案,而不是尝试在运行时做一些可怕的事情。它看起来不像
ui外观
支持您的用例
另一方面,您可以将提供的每个对象粘贴到私有的UIView
子类中,然后提供该子类的实例。然后,您可以将发送到NSObject
的外观消息转发到您提供的实例,并在包含时使用appearance:
。不过,这可能会变得混乱,也可能会让你们这一类的消费者感到困惑。经过一些关于使用标准Apple对象的研究后,我“放弃”了。现在还不存在。我已经创建了自己的代理,非常简单(目前只适用于“外观”)
让我们解释一下。
我想在NSObject子类上设置“textColor”的外观,我们称之为“FLObject”。
使FLObject符合UIAppearance协议并重写外观方法。
在这个方法中,您应该返回一个代理类(我创建的):
它是如何工作的?
FlaAppearance为appearanceForClass:方法传递的每个类创建一个自身实例。
如果对同一个类调用它两次,则返回同一个实例
然后,您可以这样做:
[[FLObject appearance] setTextColor:[UIColor redColor]];
flaappearance覆盖forwardInvocation:method,因此它接受发送的所有方法。
然后,它将所有调用放在一个数组中。
初始化FLObject时,对
[(FLAppearance *)[FLAppearance appearanceForClass:[self class]] startForwarding:self];
将开始发送调用并设置外观。
当然,这需要一些调整和错误检查,但我认为这是一个好的开始
@interface FLAppearance ()
@property (strong, nonatomic) Class mainClass;
@property (strong, nonatomic) NSMutableArray *invocations;
@end
static NSMutableDictionary *dictionaryOfClasses = nil;
@implementation FLAppearance
// this method return the same object instance for each different class
+ (id) appearanceForClass:(Class)thisClass
{
// create the dictionary if not exists
// use a dispatch to avoid problems in case of concurrent calls
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (!dictionaryOfClasses)
dictionaryOfClasses = [[NSMutableDictionary alloc]init];
});
if (![dictionaryOfClasses objectForKey:NSStringFromClass(thisClass)])
{
id thisAppearance = [[self alloc]initWithClass:thisClass];
[dictionaryOfClasses setObject:thisAppearance forKey:NSStringFromClass(thisClass)];
return thisAppearance;
}
else
return [dictionaryOfClasses objectForKey:NSStringFromClass(thisClass)];
}
- (id)initWithClass:(Class)thisClass
{
self = [self initPrivate];
if (self) {
self.mainClass = thisClass;
self.invocations = [NSMutableArray array];
}
return self;
}
- (id)init
{
[NSException exceptionWithName:@"InvalidOperation" reason:@"Cannot invoke init. Use appearanceForClass: method" userInfo:nil];
return nil;
}
- (id)initPrivate
{
if (self = [super init]) {
}
return self;
}
-(void)forwardInvocation:(NSInvocation *)anInvocation;
{
// tell the invocation to retain arguments
[anInvocation retainArguments];
// add the invocation to the array
[self.invocations addObject:anInvocation];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [self.mainClass instanceMethodSignatureForSelector:aSelector];
}
-(void)startForwarding:(id)sender
{
for (NSInvocation *invocation in self.invocations) {
[invocation setTarget:sender];
[invocation invoke];
}
}
很好的实现,我稍微修改了代码,并将该类创建为
NSProxy
的子类。在项目中使用它时,我发现内存泄漏:
例如:使用代理设置全局设置/外观,该类的每个实例永远不会达到refCount 0,因此永远不会调用dealloc
泄漏代码:
-(void)forwardInvocation:(NSInvocation *)anInvocation;
{
[...]
// !! This will retain also the target
[anInvocation retainArguments];
[...]
}
修正:
NSInvocation的复制类别
-(id)copy
{
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignature]];
NSUInteger numberOfArguments = [[self methodSignature] numberOfArguments];
[invocation setTarget:self.target];
[invocation setSelector:self.selector];
if (numberOfArguments > 2) {
for (int i = 0; i < (numberOfArguments - 2); i++) {
char buffer[sizeof(intmax_t)];
[self getArgument:(void *)&buffer atIndex:i + 2];
[invocation setArgument:(void *)&buffer atIndex:i + 2];
}
}
return invocation;
}
-(id)复制
{
NSInvocation*invocation=[NSInvocation invocationWithMethodSignature:[self-methodSignature]];
NSUInteger numberOfArguments=[[self-methodSignature]numberOfArguments];
[调用setTarget:self.target];
[调用集合选择器:self.selector];
如果(numberOfArguments>2){
对于(int i=0;i<(numberOfArguments-2);i++){
字符缓冲区[sizeof(intmax_t)];
[self-getArgument:(void*)&缓冲区索引:i+2];
[invocation setArgument:(void*)&buffer-atIndex:i+2];
}
}
返回调用;
}
为了我自己的项目,我收集了所有信息并发布了自定义UIApprance代理作为开源项目谢谢,我知道如何在UIView子类中使用它。正如我指定的,我希望在NSObject子类中实现UIAppearance我选择创建自己的与UIAppearance协议兼容的代理,这比看起来更简单:-)我在答案中添加了代码。工作正常我扩展了它,因此它也将向上移动到类Hirarch,以便它还可以设置父类的选项:(代码太长,无法添加内联)。做得好!作为改进,您可以使用instancetype
而不是id
作为返回类型,这样您甚至可以跳过显式转换到flaappearance
。答案在开始前有一个小错误,应该是:NSInvocation*targetInvocation=[invocation copy];[targetInvocation设置目标:发送方];[targetInvocation调用];targetInvocation=nil;
-(void)forwardInvocation:(NSInvocation *)anInvocation
{
[anInvocation setTarget:nil];
[anInvocation retainArguments];
// add the invocation to the array
[self.invocations addObject:anInvocation];
}
-(void)startForwarding:(id)sender
{
for (NSInvocation *invocation in self.invocations) {
// Create a new copy of the stored invocation,
// otherwise setting the new target, this will never be released
// because the invocation in the array is still alive after the call
NSInvocation *targetInvocation = [invocation copy];
[targetInvocation setTarget:sender];
[targetInvocation invoke];
targetInvocation = nil;
}
}
-(id)copy
{
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignature]];
NSUInteger numberOfArguments = [[self methodSignature] numberOfArguments];
[invocation setTarget:self.target];
[invocation setSelector:self.selector];
if (numberOfArguments > 2) {
for (int i = 0; i < (numberOfArguments - 2); i++) {
char buffer[sizeof(intmax_t)];
[self getArgument:(void *)&buffer atIndex:i + 2];
[invocation setArgument:(void *)&buffer atIndex:i + 2];
}
}
return invocation;
}