LibGcUniversal is a universal library for tweak development I made because I think it's annoying to add a new library for every little thing.
- GcColorPickerCell
- GcImagePickerCell
- GcSymbolPickerCell
- Getting picker results from Sandboxed processes
- GcTwitterCell
- GcDuoTwitterCell
- GcSocialCell
- Little helper methods
- Color utils
- Image utils
- Other
Run the install.sh
script in this repository to automatically copy the necessary files.
- Download this repository
- Copy the
libgcuniversal.tbd
into yourtheos/lib
folder. - Copy the
libgcuniversalrootless.tbd
totheos/lib/iphone/rootless
and rename it tolibgcuniversal.tbd
. - Put the
GcUniversal
folder into yourtheos/include
folder. - Done!
- Add
TWEAKNAME_LIBRARIES = gcuniversal
to your makefiles (not needed if you only use one of the social cells for example) - Add
com.mrgcgamer.libgcuniversal
to theDepends
in yourcontrol
file - Done!
PreferenceBundle plist (Root.plist e.g.)
<dict>
<key>cell</key>
<string>PSLinkCell</string>
<key>cellClass</key>
<string>GcColorPickerCell</string>
<key>label</key>
<string>Your Color Label</string>
<key>defaults</key>
<string>com.your.identifier</string>
<key>key</key>
<string>YourColor</string>
<key>supportsAlpha</key>
<true/>
<key>safeOnDismiss</key>
<true/>
<key>fallback</key>
<string>ffffffff</string>
<key>style</key>
<integer>1</integer>
</dict>
The supportsAlpha
property is optional and will default to true
if nothing else has been specified.
The safeOnDismiss
property is optional and will default to true
if nothing else has been specified.
When safeOnDismiss
is turned off, the user will have to press the X
Button in the top right for the color to be saved, when this option is on, the color will be saved even if the color picker was dismissed via the swipe down gesture.
The fallback
property is optional and will default to a clear color if the fallback color hasn’t been set.
You may use any color hex code which conforms to one of the following color hex code notations:
rgb
(#FAE
)rgba
(#BEEF
/#FAE:0.69
)rrggbb
(#DEADBE
)rrggbbaa
(#DEADBEEF
/#DEADBE:0.42
)
The style
property is optional and will default to 0
, which is the same style used by the imagepicker (rounded corners).
The style
property can also be set to 1
which will result in the "stock" iOS style introduced in iOS 14 (the rainbow circle). NOTE: This option also works on iOS versions below iOS 14
Once this is implemented you can then get the chosen color from within your tweak. So an example of how to retrieve the color could look something like this:
#import <GcUniversal/GcColorPickerUtils.h>
UIColor *color = [GcColorPickerUtils colorFromDefaults:@"DEFAULTS" withKey:@"KEY"];
UIColor *colorWithFallback = [GcColorPickerUtils colorFromDefaults:@"DEFAULTS" withKey:@"KEY" fallback:@"ffffffff"];
Fallback values have to condone to one of the above-mentioned notations.
For other helpful methods (color - HEX conversion), take a look at GcColorPickerUtils.h
PreferenceBundle plist (Root.plist e.g.)
<dict>
<key>cell</key>
<string>PSLinkCell</string>
<key>cellClass</key>
<string>GcImagePickerCell</string>
<key>label</key>
<string>Your Image Label</string>
<key>defaults</key>
<string>com.your.identifier</string>
<key>key</key>
<string>YourImage</string>
<key>usesPhotos</key>
<true/>
<key>usesVideos</key>
<false/>
<key>videoQuality</key>
<integer>1</integer>
</dict>
The usesPhotos
property is optional and will default to true
.
The usesVideo
property is optional and will default to false
.
The videoQuality
property is optional. It impacts both media quality and storage space usage.
The default value of videoQuality
is 1
.
Possible videoQuality
values are as follows:
0
= high quality1
= medium quality2
= low quality
#import <GcUniversal/GcImagePickerUtils.h>
UIImage *scaledThumbnail = [GcImagePickerUtils thumbnailFromDefaults:@"DEFAULTS" withKey:@"KEY" maxSize:400 scale:2]; // scaled to maxSize, lower memory usage
UIImage *thumbnail = [GcImagePickerUtils thumbnailFromDefaults:@"DEFAULTS" withKey:@"KEY"]; // lower res, lower memory usage
UIImage *img = [GcImagePickerUtils imageFromDefaults:@"DEFAULTS" withKey:@"KEY"]; // full size, high memory usage
NOTE:
It is recommended that you make yourself familiar with the GcImagePickerUtils.h header, to see the available methods.
The thumbnailFromDefaults:
methods should be used in most cases as the resulting images are scaled to a max size, based on the decives scale
and screenSize
.
+ (UIImage *)thumbnailFromDefaults:(NSString *)defaults withKey:(NSString *)key ;
If you want to get the images data instead of the image you can use:
NSData *data = [GcImagePickerUtils dataFromDefaults:@"DEFAULTS" withKey:@"KEY"];
If you decided to let the user pick a video, you can retrieve an NSURL
leading to it something like this:
#import <GcUniversal/GcImagePickerUtils.h>
NSURL *videoURL = [GcImagePickerUtils videoURLFromDefaults:@"DEFAULTS" withKey:@"KEY"];
if (videoURL) { // this check is to prevent a crash in case the user didn't select any media
// do something with the videoURL
}
In case you give the user the ability to choose a video or an image, you can easily check which one they went for, by doing something like this:
#import <GcUniversal/GcImagePickerUtils.h>
BOOL isVideo = [GcImagePickerUtils isVideoInDefaults:@"DEFAULTS" withKey:@"KEY"];
if (isVideo) {
// do video stuff
} else {
// do photo stuff
}
It is also possible to check if the user selected an image like this:
BOOL isPhoto = [GcImagePickerUtils isImageInDefaults:@"DEFAULTS" withKey:@"KEY"];
If you prefer that (though it doesn't make any difference which method you use).
In case you want to store some image for later reuse, you can do so using:
+ (BOOL)setImage:(UIImage *)image forDefaults:(NSString *)defaults withKey:(NSString *)key png:(BOOL)png ;
Example:
#import <GcUniversal/GcImagePickerUtils.h>
BOOL success = [GcImagePickerUtils setImage:img forDefaults:@"com.mrgcgamer.test" withKey:@"SomeKey"];
if (success)
NSLog(@"Image saved successfully");
PreferenceBundle plist (Root.plist e.g.)
<dict>
<key>cell</key>
<string>PSLinkCell</string>
<key>cellClass</key>
<string>GcSymbolPickerCell</string>
<key>label</key>
<string>Your Symbol Label</string>
<key>defaults</key>
<string>com.your.identifier</string>
<key>key</key>
<string>YourSymbol</string>
<key>tintColor</key>
<string>6B10E6FF</string>
</dict>
The symbol picker is only supported on iOS 13+, as SFSymbols didn't exist before.
If you include one in your tweak and it run on a lower iOS version, a pop up will appear, telling the user, that symbols are not supported on their iOS version.
This is optional and allows you to specify a color, which will be used as the tintColor
of the selected symbol in the preview.
LibGcUniversal allows you to specify SFSymbols as the icon image for the cell. This functions identical to iconImageSystem
from Cepheis HBListController.
(This feature requires iOS 13+ and will display nothing on lower iOS versions)
<dict>
<key>cell</key>
<string>PSLinkCell</string>
<key>cellClass</key>
<string>GcSOMETHINGPickerCell</string>
<key>iconImageSystem</key>
<dict>
<key>name</key>
<string>heart</string>
<key>backgroundColor</key>
<string>#ff3b30</string>
</dict>
</dict>
Required. The symbol name to use.
Optional. The weight to render the symbol at. The supported values are: ultraLight
, thin
, light
, regular
, medium
, semibold
, bold
, heavy
, black
. The default is regular
.
Optional. The scale to render the symbol at. The supported values are: small
, medium
, large
. The default is medium
.
Optional. The equivalent font size to render the symbol at. The default is 20.0
.
Optional. The color to render the icon in. The default is no value. If the value is not set, the default iOS blue tint color is used. When backgroundColor is set, no value means white (#ffffff) will be used.
Optional. The background color to use for the symbol. When specified, the symbol will be rendered inside an icon shape of the specified background color. The symbol will be scaled down by 20% to appropriately fit the icon shape. The default is no value, which means no icon shape will be rendered.
I would suggest using Cephei if you are working in a sandboxed app, as it makes preferences mush easier and handles XPC stuff for the prefs.
For images, you would probably need to set up some form of IPC, and for a video, I am not quite sure if that is possible at all, without moving it to the apps documents directory due to the fact, that videos are most likely used in an AVPlayer which uses a URL, but the AVPlayer is also going to be restricted by the sandbox and therefore not able to access the directory, where the video will be copied to, by the library, but for colors you can just use Cephei like this:
HBPreferences *prefs = [[HBPreferences alloc] initWithIdentifier:@"DEFAULTS"];
NSString *colorHEX = [prefs objectForKey:@"KEY"];
UIColor *yourColor = [GcColorPickerUtils colorWithHex:colorHEX];
PreferenceBundle plist (Root.plist e.g.)
<dict>
<key>cell</key>
<string>PSButtonCell</string>
<key>cellClass</key>
<string>GcTwitterCell</string>
<key>accountLabel</key>
<string>LabelForTheAccount</string>
<key>account</key>
<string>TwitterTagOfAccount</string>
<key>URL</key>
<string>URLToAnotherFancyImage</string>
</dict>
It is recommended to use a picture with a resolution of around 200x200
.
URL
is an optional value, for the link to an image to display.
If don't want to use the URL, you will also need to include the profile picture of the account in the Resources
folder of your preferences.
The picture will have to have the same name as the Twitter tag of the provided account and be stored as a .png
file.
PreferenceBundle plist (Root.plist e.g.)
<dict>
<key>cellClass</key>
<string>GcDuoTwitterCell</string>
<key>firstLabel</key>
<string>LabelForTheFirstAccount</string>
<key>firstAccount</key>
<string>TwitterTagOfFirstAccount</string>
<key>firstURL</key>
<string>URLToSomeFancyImage</string>
<key>secondLabel</key>
<string>LabelForTheSecondAccount</string>
<key>secondAccount</key>
<string>TwitterTagOfSecondAccount</string>
<key>secondURL</key>
<string>URLToAnotherFancyImage</string>
</dict>
It is recommended to use a picture with a resolution of around 200x200
.
firstURL
and secondURL
are optional values, which can be set, to specify a URL to the images for each account somewhere online.
If you use the URLs, make sure they link to the images directly. It is also possible to have only one image loaded using the URL and the other one using local files.
If don't want to use the URLs, you will also need to include the profile pictures of the two accounts in the Resources
folder of your preferences.
The pictures will have to have the same name as the Twitter tag of the corresponding account and be stored as .png
files.
PreferenceBundle plist (Root.plist e.g.)
<dict>
<key>cell</key>
<string>PSButtonCell</string>
<key>cellClass</key>
<string>GcSocialCell</string>
<key>userName</key>
<string>AccountLabel</string>
<key>subtitle</key>
<string>someFancySubtitle</string>
<key>accessoryType</key>
<integer>0</integer>
<key>accessoryURL</key>
<string>URLToAnAccessoryImage</string>
<key>pfpURL</key>
<string>URLToSomePFP</string>
<key>URL</key>
<string>URLToSomewhere</string>
</dict>
userName
is a required value and will be the username displayed on the cell.
subtitle
is an optional value and will be displayed below the userName
if specified.
If it hasn't been specified the userName
will be horizontally centred on the cell. In case you don't want the username to be centred but also don't want to specify a subtitle, you can specify a subtitle only containing whitespace characters.
accessoryType
is an optional value, which can be specified in order to display an accessory image (the little image on the right-hand side).
The default value of accessoryType
is 0
.
Possible accessoryType
values are as follows:
0
= none1
= Github2
= Reddit3
= Custom
This property is optional and only applies if you chose to set the accessoryType
to 3
/ custom.
Here you can specify the URL to an image will which be displayed as the accessory image
If you want to use a local image as the accessory icon, you will have to put it in the 'Resources' folder of your preference bundle and it will have to have the name:userName.png
where userName
is the same you specified earlier for display.
It is recommended to provide an image with no clear border on any side, to avoid an unexpectedly small or shifted icon.
This property holds the URL to the profile picture you want to display.
If don't want to use the URLs, you will also need to include the profile pictures of the two accounts in the Resources
folder of your preferences.
The pictures will have to have the same name as the Twitter tag of the corresponding account and be stored as .png
files.
URL
is required and is the URL the user will be lead to, once they tapped the cell.
First of all, you will need to import HelperFunction.h as follows:
#import <GcUniversal/HelperFunctions.h>
The HelperFunctions.h file includes this interface (more stuff in the file):
@interface UIView (extension)
-(void)anchorTop:(nullable NSLayoutAnchor <NSLayoutYAxisAnchor *> *)top
leading:(nullable NSLayoutAnchor <NSLayoutXAxisAnchor *> *)leading
bottom:(nullable NSLayoutAnchor <NSLayoutYAxisAnchor *> *)bottom
trailing:(nullable NSLayoutAnchor <NSLayoutXAxisAnchor *> *)trailing
padding:(UIEdgeInsets)insets
size:(CGSize)size ;
-(void)anchorCenterX:(nullable NSLayoutAnchor <NSLayoutXAxisAnchor *> *)centerX
centerY:(nullable NSLayoutAnchor <NSLayoutYAxisAnchor *> *)centerY
size:(CGSize)size ;
-(void)anchorEqualsToView:(UIView *)view padding:(UIEdgeInsets)insets ;
@end
void logToFile(NSString *path, NSString *log);
Therefor you can use the HelperFunctions like this:
UIView *view = [UIView new];
[someOtherView addSubview:view];
[view anchorTop:someOtherView.topAnchor leading:someOtherView.leadingAnchor bottom:nil trailing:nil padding:UIEdgeInsetsMake(32,15,0,0) size:CGSizeMake(64,64)];
Now, this would constrain the topAnchor
of view
to the topAnchor
of someOtherView
with padding of 32
,
also, this would constrain the leadingAnchor
of view
to the leadingAnchor
of someOtherView
with padding of 15
,
furthermore, this would constrain the widthAnchor
and heightAnchor
of view
to 64
each.
If you are looking for a better explanation of this function, I recommend watching this video, as it's also where I got the function from, which I tried to convert to Obj-C.
void logToFile(NSString *path, NSString *log);
This is a simple convenience function, which writes the given log to the end of the of the file at the provided path. (If the file doesn't exist, it will be created)
Don't forget to import the GcColorUtils.h
.
#import <GcUniversal/GcColorUtils.h>
The interface in GcColorUtils.h
looks as follows:
typedef NS_ENUM(NSUInteger, ColorComponent) {
kColorRed,
kColorGreen,
kColorBlue,
kColorHue,
kColorSaturation,
kColorBrightness,
kColorAlpha
};
@interface UIColor (colorUtils)
+ (UIColor *)closestColorFromArray:(NSArray *)colors toColor:(UIColor *)color ;
- (CGFloat)getColorComponent:(ColorComponent)component ;
- (CGFloat)rgbValue;
- (BOOL)isGrayscale;
- (BOOL)isNearGrayscale;
- (BOOL)isDarkColor;
- (BOOL)isLightColor;
@end
All the functions should be relatively self-explanatory I think, but I will explain a few of them anyways.
The closestColorFromArray: toColor: ;
function returns the color from the input array, which is the closest to the input color.
The rgbValue
function returns a float between 0 and 3. This float is calculated by adding the r
, g
and b
values of the color together.
Don't forget to import the GcImageUtils.h
.
#import <GcUniversal/GcImageUtils.h>
The interface in GcImageUtils.h
looks something like this:
BOOL isHEICSupported(void);
NSData *UIImageHEICRepresentation(UIImage *image, CGFloat compressionQuality);
@interface UIImage (imageUtils)
+ (UIImage *)stockImgForBundleID:(NSString *)arg1 ;
+ (UIImage *)thumbnailForImage:(UIImage *)image withMaxSize:(CGFloat)size scale:(CGFloat)scale ;
- (UIColor *)averageColor;
- (NSArray <UIColor *> *)dominantColors;
@end
Return a BOOL
indicating wether the device/iOS combination support the HEIC image format.
Same thing as UIImageJPEGRepresentation or UIImagePNGRepresentation but for HEIC
.
This method will give you the stock (unthemed) icon for an app / service.
This method returns a downscaled version of the provided image.
It downscaled to the size
provided eg: with size = 400
and an image of 1920x1080
it will be resized to 400x225
.
Why is that useful?
Downsampling the image manually instead of letting UIKit handle this at display time drastically reduces the memory footprint of the image in memory, thus it is recommended to use this method whenever you use "global" images of a substantial size for extended periods of time.
(If the size
provided is negativ, it will implicitely function as thumbnailForImage:withMinSize:scale:
, thus the short side of the resulting image will be at least size
long)
Returns the average / mean color of an image.
Do note that this function will return an array of colors with an unspecified count, so you will have to check for the array count manually and you might have to specify different behaviour within your project depending on the number of colors returned.
The rest of these functions should be self-explanatory though, but if you do have a question, just ask me on Twitter or something.
If you want to ask me anything about my library or if you happen to find a typo feel free to contact me on Twitter or ping me on a discord server, if you find me (no friend-requests or message-requests).