- What we'll make is a simple tweak that adds a blur to the lock screen, with the option to change the its alpha/intensity on the fly. How can this happen without a respring? The Settings app will need to communicate with SpringBoard to notify that the blur value has been changed, so that it can refresh the UI. The
NSNotificationCenter
Foundation API makes that really straightforward. You can explore theNSNotificationCenter
API's documentation here
Open your terminal and create a tweak project, your Tweak.x
should look like this:
#import "Tweak.h"
static CGFloat blurIntensity = 0.85f;
static void loadPrefs(void) {
NSUserDefaults *prefs = [[NSUserDefaults alloc] initWithSuiteName:@"com.example.respringlesstweakprefs"];
blurIntensity = [prefs objectForKey:@"blurIntensity"] ? [prefs floatForKey:@"blurIntensity"] : 0.85;
}
%hook CSCoverSheetViewController
%property (nonatomic, strong) _UIBackdropView *blurView;
%new
- (void)setupBlur {
_UIBackdropViewSettings *settings = [_UIBackdropViewSettings settingsForStyle:2];
if(!self.blurView) {
self.blurView = [[_UIBackdropView alloc] initWithFrame:CGRectZero autosizesToFitSuperview:YES settings:settings];
self.blurView.alpha = blurIntensity;
[self.view insertSubview:self.blurView atIndex:0];
}
}
%new
- (void)updateBlurIntensity {
loadPrefs();
self.blurView.alpha = blurIntensity;
}
- (void)viewDidLoad {
%orig;
[self setupBlur];
[NSDistributedNotificationCenter.defaultCenter addObserver:self selector:@selector(updateBlurIntensity) name:@"com.example.respringlesstweakprefs/DidUpdateBlurIntensityNotification" object: nil];
}
%end
%ctor {
loadPrefs();
}
Then your Tweak.h
should have:
@import UIKit;
@interface _UIBackdropViewSettings : NSObject
+ (id)settingsForStyle:(NSInteger)style;
@end
@interface _UIBackdropView : UIView
- (id)initWithFrame:(CGRect)frame autosizesToFitSuperview:(BOOL)autosizes settings:(_UIBackdropViewSettings *)settings;
@end
@interface CSCoverSheetViewController: UIViewController
@property (nonatomic, strong) _UIBackdropView *blurView;
- (void)setupBlur;
@end
@interface NSDistributedNotificationCenter: NSNotificationCenter
@end
-
First we create the
void loadPrefs(void)
function that will contain our prefs, adding theCGFloat
key for the blur intensity. -
We hook
- (void)viewDidLoad
fromCSCoverSheetViewController
where we call the original implementation and our custom method- (void)setupBlur
where we create a_UIBackdropView
blur with custom settings, nothing crazy going on there. Finally the most important part: we create a notification observer by callingaddObserver:selector:name:object:
onNSDistributedNotificationCenter
. We're using the distributed variant because we need to communicate between two different processes (Preferences & SpringBoard). This observer,CSCoverSheetViewController
will be listening to notifications with the name ofcom.example.respringlesstweakprefs/DidUpdateBlurIntensityNotification
, when one gets send, it'll callupdateBlurIntensity
, which will update thealpha
value accordingly & reflect it on the UI.
Now we have to actually make the preferences, so create a preference bundle project, your root view controller should look like this:
#import "RTRootVC.h"
@implementation RTRootVC
- (NSArray *)specifiers {
if(!_specifiers) _specifiers = [self loadSpecifiersFromPlistName:@"Root" target:self];
return _specifiers;
}
- (void)setPreferenceValue:(id)value specifier:(PSSpecifier *)specifier {
NSUserDefaults *prefs = [[NSUserDefaults alloc] initWithSuiteName:@"com.example.respringlesstweakprefs"];
[prefs setObject:value forKey:specifier.properties[@"key"]];
[super setPreferenceValue:value specifier:specifier];
if(![specifier.properties[@"key"] isEqualToString: @"blurIntensity"]) return;
[NSDistributedNotificationCenter.defaultCenter postNotificationName:@"com.example.respringlesstweakprefs/DidUpdateBlurIntensityNotification" object:nil];
}
@end
Then your RTRootVC.h
:
@import Preferences.PSListController;
@import Preferences.PSSpecifier;
@interface RTRootVC : PSListController
@end
@interface NSDistributedNotificationCenter : NSNotificationCenter
@end
-
The logic is simple. We implement
- (void)setPreferenceValue:(id)value specifier:(PSSpecifier *)specifier
where we create an instance ofNSUserDefaults
with our suite name and set the float value for the given key, to finally post a notification when the slider value is modified, which in turn will be received byCSCoverSheetViewController
which will update the UI accordingly. -
That's how a respringless tweak works, in this case every time you update the slider in the prefs panel, the method we implemented gets called which posts the notification, the observer listening for it receives it and calls the designated method. Now you have succesfully created your first respringless tweak!
-
If you want to try the project yourself it's available here
-
Here's how the blur looks ↓