We are working on a Qt project, and there is some Mac specific code that we need to add. We need to register for an event, in a sample program we did that by using:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(notificationHandler:)
name:NSWorkspaceDidDeactivateApplicationNotification
object:nil];
Since we can use that directly in our mm file on Qt, we are taking the approach of doing something like:
MyClass::MyClass() : {
// do other setup ...
CFNotificationCenterAddObserver
(
CFNotificationCenterGetLocalCenter(),
this,
¬ificationHandler,
CFSTR("???"),
NULL,
CFNotificationSuspensionBehaviorDeliverImmediately
);
}
whats the string for "NSWorkspaceDidDeactivateApplicationNotification"?? Or how do we attatch ourselves to this particular notification?
We tried NSGod's approach, but since no Objective-C code can be added in a .h with Qt, then we added a private member which its class is defined in the mm file, that containes the actual logic. like this:
SelectedStuffManager.h
class MacWrap;
class SelectedStuffManager
{
public:
....
doSomething();
MacWrap* d;
private:
....
};
SelectedStuffManager.mm
#class MDWorkspaceWatcher;
class MacWrap
{
public:
MacWrap();
~MacWrap();
void applicationDeactivated(NSNotification * notification);
SystemEventsApplication *systemApplication;
NSRunningApplication *runApp;
private:
MDWorkspaceWatcher *workspaceWatcher;
};
MacWrap::MacWrap() {
this->workspaceWatcher = [[MDWorkspaceWatcher alloc] initWithMyClass:this];
}
MacWrap::~MacWrap() {
[this->workspaceWatcher release];
}
void MacWrap::applicationDeactivated(NSNotification* notification)
{
// guardar el id del proceso para utilizarlo luego
runApp = [[notification userInfo] valueForKey:#"NSWorkspaceApplicationKey"];
NSString *systemEventsASppName = [runApp bundleIdentifier];
if( [ systemEventsASppName isNotEqualTo:#"com.yo.SelectedText"])
{
systemApplication = [SBApplication applicationWithBundleIdentifier:systemEventsASppName];
NSLog(#"Launched. %#",systemEventsASppName);
}
}
#interface MDWorkspaceWatcher : NSObject {
MacWrap *manager;
}
- (id)initWithMyClass:(MacWrap*)obj;
- (void)didDeactivateApp:(NSNotification *)notification; #end
#implementation MDWorkspaceWatcher
- (id)initWithMyClass:(MacWrap*)obj {
if ((self = [super init])) {
manager = obj;
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(didDeactivateApp:)
name:NSWorkspaceDidDeactivateApplicationNotification
object:nil];
}
return self;
}
- (void)dealloc {
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
[super dealloc];
}
- (void)didDeactivateApp:(NSNotification *)notification {
manager->applicationDeactivated(notification);
}
#end
SelectedStuffManager::SelectedStuffManager()
{
d = new MacWrap();
}
SelectedStuffManager::doSomething()
{
if ([[d->runApp localizedName] isEqualTo: #"something"]) --> here it fails, bad memory access
{
...
}
}
It seems like someone is freeing both runApp and systemApplication, so we get a null pointer or bad memory. How or why could this be happening?
I don't believe you can just do like you're hoping to. First of all, NSWorkspace uses its own NSNotificationCenter, which is different than the default NSNotificationCenter returned with +defaultCenter.
I don't believe there's a strict CF-equivalent to those NSWorkspace calls. There are likely high-level Carbon-based equivalents, but they're not available in 64-bit, and should likely be avoided.
You should be able to accomplish what you want using a small Objective-C helper class to receive the notifications and forward them to your C++ class like in the following code:
EDIT: updated to remove any Objective-C from header files. Just use generic void * pointers, which you can cast inside the .mm file.
.h:
//#class MDWorkspaceWatcher;
class MyClass {
private:
// MDWorkspaceWatcher *workspaceWatcher;
void *workspaceWatcher;
public:
MyClass();
~MyClass();
// const void didActivateApp(NSNotification *notification) const;
// const void didDeactivateApp(NSNotification *notification) const;
const void didActivateApp(void *anNSnotification) const;
const void didDeactivateApp(void *anNSnotification) const;
};
.mm:
Objective-C part:
#interface MDWorkspaceWatcher : NSObject {
MyClass *myClass;
}
- (id)initWithMyClass:(MyClass *)aMyClass;
#end
#implementation MDWorkspaceWatcher
- (id)initWithMyClass:(MyClass *)aMyClass {
if ((self = [super init])) {
myClass = aMyClass;
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(didActivateApp:)
name:NSWorkspaceDidActivateApplicationNotification
object:nil];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(didDeactivateApp:)
name:NSWorkspaceDidDeactivateApplicationNotification
object:nil];
}
return self;
}
// very important:
- (void)dealloc {
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
[super dealloc];
}
- (void)didActivateApp:(NSNotification *)notification {
myClass->didActivateApp(notification);
}
- (void)didDeactivateApp:(NSNotification *)notification {
myClass->didDeactivateApp(notification);
}
#end
C++ part:
MyClass::MyClass() {
this->workspaceWatcher = [[MDWorkspaceWatcher alloc] initWithMyClass:this];
}
MyClass::~MyClass() {
[(MDWorkspaceWatcher *)this->workspaceWatcher release];
}
MyClass::didActivateApp(void *anNSnotification) {
NSDictionary *appInfo = [(NSNotification *)anNSnotification userInfo];
NSLog(#"appInfo == %#", appInfo);
}
MyClass::didDeactivateApp(void *anNSnotification) {
NSDictionary *appInfo = [(NSNotification *)anNSnotification userInfo];
NSLog(#"appInfo == %#", appInfo);
}
Note that an NSDictionary is toll-free-bridged with CFDictionaryRef, so you can simply cast the appInfo NSDictionary to a CFDictionaryRef and then call the CF functions to get at the contents of the dictionary if you prefer C over Objective-C.
Note that the notification center owns the appInfo dictionary (in other words, it will be autoreleased), so you shouldn't call CFRelease() on it like you might with CFCreate*/CFCopy*-related code.
Related
I have one class MyOldController with init method
-(instancetype) initWithMyController: (MyController *) myController {
if((self = [self init])) {
_myController = myController;
}
return self;
}
I want swizzle this initialization method to another and this my swizzle code
#implementation MyOldController(Swizzle)
+ (void)load {
[MyOldController swizzleMethods];
}
+ (void)swizzleMethods {
method_exchangeImplementations(class_getInstanceMethod(self, #selector(initWithMyController)), class_getInstanceMethod(self, #selector(swizzle_ initWithMyController)));
}
I try write this
-(instancetype) swizzle_initWithMyController: (MyController *) myController {
if((self = [self init])) {
_myController = myController;
}
return self;
}
But it drops error
Then I renamed init method to this and updated (void)swizzleMethods
-(instancetype) initWithMyController_swizzle: (MyController *) myController {
if((self = [self init])) {
_myController = myController;
}
return self;
}
Error message disappeared but swizzle doesn't works. It just calls old initialization method, not my new.
Which point i missed? Is swizzling of initialization method have some special way to do it?
(Starting with the required caveat: this is incredibly dangerous and should never be used in production code. Swizzling initializers is particularly dangerous given designated initializer chaining, and should definitely never be done for anything but exploration and debugging without first confirming the implementation of the swizzled initializer. OK, got that out of the way.)
I can't reproduce your issue. And initializer should always start with with init, so your second approach is correct. I suspect you've just made a small mistake, perhaps in your #selector (which has a typo in your question, which suggests maybe there's a mistake in your actual code). Here is code that does what you're describing.
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface MyOldController: NSObject
- (instancetype)initWithInt:(NSInteger)x
#end
#implementation MyOldController
- (instancetype)initWithInt:(NSInteger)x
{
self = [super init];
if (self) {
NSLog(#"init");
}
return self;
}
#end
#implementation MyOldController(Swizzle)
+ (void)load {
[MyOldController swizzleMethods];
}
+ (void)swizzleMethods {
method_exchangeImplementations(class_getInstanceMethod(self, #selector(initWithInt:)), class_getInstanceMethod(self, #selector(initWithInt_swizzle:)));
}
- (instancetype)initWithInt_swizzle:(NSInteger)x
{
self = [super init];
if (self) {
NSLog(#"init_swizzle");
}
return self;
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
MyOldController *controller = [[MyOldController alloc] initWithInt:1];
NSLog(#"%#", controller);
}
return 0;
}
This prints, as expected:
2018-06-21 12:23:14.431936-0400 test[30981:401466] init_swizzle
2018-06-21 12:23:14.432172-0400 test[30981:401466] <MyOldController: 0x10051ee10>
I have searched a lot but didn't find useful code or tutorial.
In my application, I have an mutable array which update in every 60 seconds.
The objects in array is being displayed by table view in multiple view controllers.
I want to reload table view automatically when only when values in array changes or updated.
For this, I want to add observer on mutable array i.e when values in array changes then it should call a particular method for e.g
-(void)ArrayUpdatedNotification:(NSMutableArray*)array
{
//Reload table or do something
}
Thanks in advance.
You can abstract the array into a data container class with accessor methods, and then use key-value observing to observe when the array that backs the container object is changed (you cannot use KVO on an NSArray directly).
A simple example of a class used as an abstraction on top of an array follows. You use its insertObject:inDataAtIndex: and removeObjectFromDataAtIndex: methods instead of directly accessing the with addObject: and removeObject:.
// DataContainer.h
#interface DataContainer : NSObject
// Convenience accessor
- (NSArray *)currentData;
// For KVC compliance, publicly declared for readability
- (void)insertObject:(id)object inDataAtIndex:(NSUInteger)index;
- (void)removeObjectFromDataAtIndex:(NSUInteger)index;
- (id)objectInDataAtIndex:(NSUInteger)index;
- (NSArray *)dataAtIndexes:(NSIndexSet *)indexes;
- (NSUInteger)countOfData;
#end
// DataContainer.m
#interface DataContainer ()
#property (nonatomic, strong) NSMutableArray *data;
#end
#implementation DataContainer
// We'll use automatic notifications for this example
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
{
if ([key isEqualToString:#"data"]) {
return YES;
}
return [super automaticallyNotifiesObserversForKey:key];
}
- (id)init
{
self = [super init];
if (self) {
// This is the ivar which provides storage
_data = [NSMutableArray array];
}
return self;
}
// Just a convenience method
- (NSArray *)currentData
{
return [self dataAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self countOfData])]];
}
// These methods enable KVC compliance
- (void)insertObject:(id)object inDataAtIndex:(NSUInteger)index
{
self.data[index] = object;
}
- (void)removeObjectFromDataAtIndex:(NSUInteger)index
{
[self.data removeObjectAtIndex:index];
}
- (id)objectInDataAtIndex:(NSUInteger)index
{
return self.data[index];
}
- (NSArray *)dataAtIndexes:(NSIndexSet *)indexes
{
return [self.data objectsAtIndexes:indexes];
}
- (NSUInteger)countOfData
{
return [self.data count];
}
#end
The reason that we do this is so we can now observe changes made to the underlying array. This is done through Key Value Observing. A simple view controller that instantiates and observes a data controller is shown:
// ViewController.h
#interface ViewController : UIViewController
#end
// ViewController.m
#interface ViewController ()
#property (nonatomic,strong) DataContainer *dataContainer;
#end
#implementation ViewController
static char MyObservationContext;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Instantiate a DataContainer and store it in our property
_dataContainer = [[DataContainer alloc] init];
// Add self as an observer. The context is used to verify that code from this class (and not its superclass) started observing.
[_dataContainer addObserver:self
forKeyPath:#"data"
options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew)
context:&MyObservationContext];
}
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
// Check if our class, rather than superclass or someone else, added as observer
if (context == &MyObservationContext) {
// Check that the key path is what we want
if ([keyPath isEqualToString:#"data"]) {
// Verify we're observing the correct object
if (object == self.dataContainer) {
NSLog(#"KVO for our container property, change dictionary is %#", change);
}
}
}
else {
// Otherwise, call up to superclass implementation
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Insert and remove some objects. Console messages should be logged.
[self.dataContainer insertObject:[NSObject new] inDataAtIndex:0];
[self.dataContainer insertObject:[NSObject new] inDataAtIndex:1];
[self.dataContainer removeObjectFromDataAtIndex:0];
}
- (void)dealloc
{
[_dataContainer removeObserver:self forKeyPath:#"data" context:&MyObservationContext];
}
#end
When this code runs, three changes to the data are observed by the view controller and logged to the console:
KVO for our container property, change dictionary is {
indexes = "<NSIndexSet: 0x8557d40>[number of indexes: 1 (in 1 ranges), indexes: (0)]";
kind = 2;
new = (
"<NSObject: 0x8557d10>"
);
}
KVO for our container property, change dictionary is {
indexes = "<NSIndexSet: 0x715d2b0>[number of indexes: 1 (in 1 ranges), indexes: (1)]";
kind = 2;
new = (
"<NSObject: 0x71900c0>"
);
}
KVO for our container property, change dictionary is {
indexes = "<NSIndexSet: 0x8557d40>[number of indexes: 1 (in 1 ranges), indexes: (0)]";
kind = 3;
old = (
"<NSObject: 0x8557d10>"
);
}
While this is somewhat complex (and can get much more involved), this is the only way to be notified automatically that a mutable array's contents were changed.
What is can do is - After updating your Array send a Notification (NSNotificationCenter) and this notification will be received by all the controllers. On receiving the notificaiton the controller should do [tableview reloaddata].
Code example:
// Adding an observer
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateTable:) name:#"arrayUpdated" object:nil];
// Post a notification
[[NSNotificationCenter defaultCenter] postNotificationName:#"arrayUpdated" object:nil];
// the void function, specified in the same class where the Notification addObserver method has defined
- (void)updateTable:(NSNotification *)note {
[tableView reloadData];
}
If you want to use shiny blocks you can do this
// Create an instance variable for your block holder in your interface extension
#property (strong) id notificationHolder;
// Listen for notification events (In your TableView class.
self.notificationHolder = [[NSNotificationCenter defaultCenter] addObserverForName:#"NotificationName"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
NSLog(#"Received notification");
}];
Then in dealloc (or when you don't use it anymore)
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self.notificationHolder];
}
Then in some other class
// Send a notification
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotificationName" object:nil];
Ask if something is not clear! Hope it helps!
EDIT DUE TO COMMENT
The "YourEvent" is the name of the notification, this means that you can name it to whatever you want. (Perhaps "UpdateArrayNotification could be a good name?)
Something to think about: Note that you can have several observers for the same notification. This means that one 'post' will be snapped up by all observers.
i am trying to initialize the device motion manager in a class that contains all of my global variables to use in different classes. However i cant seem to get this working.
I have this in my global class:
// Motion Manager
CMMotionManager *motionManager;
Then set the property in a different class header file:
#property (retain) CMMotionManager *motionManager;
And on the .m file i synthetize and start the updates:
#synthesize motionManager;
motionManager = [[CMMotionManager alloc] init];
if (motionManager.deviceMotionAvailable)
{
motionManager.deviceMotionUpdateInterval = 1.0/100.0;
[motionManager startDeviceMotionUpdates];
NSLog(#"Device Started");
}
But when i call this on my third class:
motionManager.deviceMotionAvailable
It returns NO.
PD: both classes import the global class, and the third class imports the second one's header.
Ok it Seems i can use a singleton class to achieve this:
h file:
//
// MotionManagerSingleton.h
// Air Interface
//
// Created by MacBook on 11/12/28.
// Copyright 2011年 __MyCompanyName__. All rights reserved.
// File created using Singleton XCode Template by Mugunth Kumar (http://blog.mugunthkumar.com)
// More information about this template on the post http://mk.sg/89
// Permission granted to do anything, commercial/non-commercial with this file apart from removing the line/URL above
#import <Foundation/Foundation.h>
#import <CoreMotion/CoreMotion.h>
#interface MotionManagerSingleton : NSObject
+ (MotionManagerSingleton*) sharedInstance;
- (void)startMotionManager;
- (CMAcceleration)getCurrentAcceleration;
- (CMAttitude*)getCurrentAttitude;
- (CMAttitude*)getStartingAttitude;
- (void)setStartingAttitude: (CMAttitude*) ref;
- (bool)isDeviceReady;
- (void)destroyMotionManager;
- (NSTimeInterval) getStamp;
#end
m file:
//
// MotionManagerSingleton.m
// Air Interface
//
// Created by MacBook on 11/12/28.
// Copyright 2011年 __MyCompanyName__. All rights reserved.
// File created using Singleton XCode Template by Mugunth Kumar (http://blog.mugunthkumar.com)
// More information about this template on the post http://mk.sg/89
// Permission granted to do anything, commercial/non-commercial with this file apart from removing the line/URL above
#import "MotionManagerSingleton.h"
#implementation MotionManagerSingleton
CMMotionManager *motionManager;
CMAttitude *referenceAttitude;
bool initialized;
#pragma mark -
#pragma mark Singleton Methods
+ (MotionManagerSingleton*)sharedInstance {
static MotionManagerSingleton *_sharedInstance;
if(!_sharedInstance) {
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[super allocWithZone:nil] init];
});
}
return _sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
#if (!__has_feature(objc_arc))
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX; //denotes an object that cannot be released
}
- (void)release {
//do nothing
}
- (id)autorelease {
return self;
}
#endif
#pragma mark -
#pragma mark Custom Methods
// Add your custom methods here
- (void)startMotionManager{
if (!initialized) {
motionManager = [[CMMotionManager alloc] init];
if (motionManager.deviceMotionAvailable) {
motionManager.deviceMotionUpdateInterval = 1.0/70.0;
[motionManager startDeviceMotionUpdates];
NSLog(#"Device Motion Manager Started");
initialized = YES;
}
}
}
- (CMAcceleration)getCurrentAcceleration{
return motionManager.deviceMotion.userAcceleration;
}
- (CMAttitude*)getCurrentAttitude{
return motionManager.deviceMotion.attitude;
}
- (CMAttitude*)getStartingAttitude{
return referenceAttitude;
}
- (float)getInterval{
return motionManager.accelerometerUpdateInterval;
}
- (NSTimeInterval) getStamp{
return motionManager.deviceMotion.timestamp;
}
- (void)setStartingAttitude: (CMAttitude*) ref{
referenceAttitude = motionManager.deviceMotion.attitude;
}
- (bool)isDeviceReady{
return motionManager.deviceMotionActive;
}
- (void)destroyMotionManager{
[motionManager stopDeviceMotionUpdates];
motionManager = nil;
}
#end
and whenever I want to use it i can just declare a variable in that class header like this:
MotionManagerSingleton *Manager;
and use it like this on the m file:
Manager = [MotionManagerSingleton sharedInstance];
if ([Manager isDeviceReady]) {
NSLog(#"Device Is Ready on Drawing View Controller");
}
referenceAttitude = [Manager getStartingAttitude];
There is already a singleton class in every iOS App, and usually it's called AppDelegate :-)
This is because AppDelegate technically implements UIApplicationDelegate.
So, I followed this suggestion by Jonathan Hui, and it works perfectly for me.
I hope this helps, and it's not too late of an answer..
I need to send a distributed notification from my cocoa app to my firebreath project, so I need to create an observer and a selector in my firebreath code.
I changed the class extension to ".mm" in order to support objective-c code. I already have objective-c code in my firebreath project and is working ok. But when I try to create an observer I get errors in my code and I don't know how to resolve it.
Here is my source code from firebreath project:
//This is the selector
- (void)receiveAppConfirmationNotification:(NSNotification*)notif{
//The application is alive.
NSLog(#"The application is alive!!!!!!!!");
}
std::string MyProjectAPI::bgp(const std::string& val)
{
//Add an observer to see if the application is alive.
NSString *observedObject = #"com.test.net";
NSDistributedNotificationCenter *center = [NSDistributedNotificationCenter defaultCenter];
[center addObserver: self
selector: #selector(receiveAppConfirmationNotification:)
name: #"App Confirmation Notification"
object: observedObject];
}
Here are my errors:
...firebreath/../projects/MyProject/MyProjectAPI.mm:133: error: expected unqualified-id before '-' token. This is the line where I defined the "receiveAppConfirmationNotification" method.
...firebreath/../projects/MyProject/MyProjectAPI.mm:157: error: 'self' was not declared in this scope.
How can I do to define the selector?
How can I do to add the observer as the class itself?
A selector has to be part of an objective-c++ class; you can't just throw it in the middle of nowhere there, it should be in the #implementation section of the class.
To keep things as compatible as possible, I recommend putting both #interface and #implementation sections in the .mm file so that the .h file is compatible with C++, but that's up to you; it'll just be easier if you do it that way. You can use the pimpl pattern to help with this if you want.
I did the interface and the implementation and the code is without errors. The problem is that I am able to send notifications to my cocoa application, but I am not able to recibe a notification from the application to the plugin.
Here is header file:
#ifdef __OBJC__
#interface FBMyProject : NSObject {
NSString *parameter_val;
}
#property (nonatomic, retain) NSString *parameter_val;
-(void) receiveAppConfirmationNotification:(NSNotification*)notif;
#end
#endif
class MyProjectAPI : public FB::JSAPIAuto
{
public:
...
}
#endif
Here is my source file:
#implementation FBMyProject
#synthesize parameter_val;
-(void) receiveAppConfirmationNotification:(NSNotification*)notif{
//The application is alive.
NSLog(#"The application is alive!!!!!!!!");
}
- (id)init
{
self = [super init];
if (self) {
NSString *observedObject = #"test.com";
NSDistributedNotificationCenter *center = [NSDistributedNotificationCenter defaultCenter];
[center addObserver: self
selector: #selector(receiveAppConfirmationNotification:)
name: #"App Confirmation Notification"
object: observedObject];
}
return self;
}
- (void)dealloc
{
// unregister notification
[[NSDistributedNotificationCenter defaultCenter] removeObserver: self
name: #"App Confirmation Notification"
object: nil];
[self.parameter_val release];
[super dealloc];
}
#end
std::string MyProjectAPI::bgp(const std::string& val)
{
FBMyProject *my_project = [[FBMyProject alloc] init];
my_project.parameter_val = [NSString stringWithUTF8String:val.c_str()];
[my_project release];
return val;
}
Here is my source from the cocoa application:
NSDictionary *data = [NSDictionary dictionaryWithObjectsAndKeys:
#"OK", #"confirmation",
nil];
//Post the notification
NSString *observedObject = #"test.com";
NSDistributedNotificationCenter *center = [NSDistributedNotificationCenter defaultCenter];
[center postNotificationName: #"App Confirmation Notification"
object: observedObject
userInfo: data
deliverImmediately: YES];
I would like to do something similar in CSS than "BODY {color:red;}" but in objectif C. I mean if I have 10 differents UIView, I would like to change all the UIView texts color in one time.
Cheers
Simple case - you somehow gathered them together
for (UIView *v in styledViews) {
// apply current style here
}
I doubt that this is your case
Complex case - there are tons of styled views everywhere.
Disclaimer: I can't guarantee anything about following code, it works on my simulator, which doesn't mean it will not blow up in user's hands. I wrote it because it was fun and may help Thomas to solve his problem. I didn't check documentation thoroughly because it's already 5 a.m. here
1) Encapsulate style stuff in some StyleManager class (in this example applyCurrentStyle: will apply current style to any view passed to it). It should post notification each time style is changed (e.g. kStyleManagerNotificationStyleChanged)
2) Make UIView category (like UIView+Style) with public setStyleManager: method.
3) Implement it:
#import "UIView+Style.h"
#import <objc/runtime.h>
#interface StyleSubscription : NSObject {
StyleManager *styleManager;
NSObject *subscriber;
}
#property (readonly) StyleManager *styleManager;
- (id)initWithStyleManager:(NSObject*)p subscriber:(NSObject*)s;
#end
#implementation StyleSubscription
#synthesize styleManager;
- (id)initWithStyleManager:(StyleManager*)sManager subscriber:(NSObject*)s {
if (self = [super init]) {
styleManager = [sManager retain];
subscriber = s;
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:subscriber
name:kStyleManagerNotificationStyleChanged
object:styleManager];
[styleManager release];
[super dealloc];
}
#end
#implementation UIView (Style)
static char styleSubsriptionKey;
- (StyleManager*)styleManager {
StyleSubscription *s = objc_getAssociatedObject(self, &styleSubsriptionKey);
return s.styleManager;
}
- (void)styleChanged:(NSNotification*)n {
[[self styleManager] applyCurrentStyle:self];
}
- (void)setStyleManager:(StyleManager*)sManager {
if ([self styleManager] == sManager) {
return;
}
StyleSubscription *subscr = nil;
if (sManager != nil) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(styleChanged:)
name:kStyleManagerNotificationStyleChanged
object:sManager];
subscr = [[[StyleSubscription alloc] initWithStyleManager:sManager
subscriber:self] autorelease];
}
objc_setAssociatedObject(self, &styleSubsriptionKey, subscr, OBJC_ASSOCIATION_RETAIN);
[sManager applyCurrentStyle:self];
}
#end
Each time style manager posts notification correspondent views will be updated with a new style. View will unsubscribe from style notifications automatically upon deallocation. Style manager can be removed explicitly [view setStyleManager:nil].
you can fix this in a nice way.here is a tutorial
http://dot-ios.blogspot.com/2013/02/design-uilabel-in-optimize-way-for.html