SBIconView: Difference between revisions

From iPhone Development Wiki
No edit summary
m (SBIconViewLocation for iOS 9+.)
 
(14 intermediate revisions by 6 users not shown)
Line 1: Line 1:
'''SBIconView''' is a [[UIView]] subclass introduced in iOS 5 that is used to display icons in [[SpringBoard.app|SpringBoard]]. In iOS 5 Apple split up the [[SBIcon]], creating a separate class for the view, and just leaving the [[SBIcon]] with behind the scenes stuff.
'''SBIconView''' is a very versatile [[UIView]] subclass introduced in iOS 5 that is used to display icons in [[SpringBoard.app|SpringBoard]]. In iOS 5 Apple split up icons into [[SBIcon]] (an abstract class) and SBIconView, introducing some degree of sanity with it. SBIconView conforms to the SBIconObserver protocol, and in turn exposes a protocol of its own: SBIconViewObserver. A lot of SpringBoard's home screen functionality can be modified by hooking the methods in SBIconView. One of the most magical methods in SBIconView is -setIcon:. It takes an instance of SBIcon and uses the information therein to create a semi-functional icon view.
=== Creating an Instance ===
 
== Creating an Instance ==
 
<source lang="objc">
// iOS 5
SBIcon *icon = [[%c(SBIconModel) sharedInstance] applicationIconForDisplayIdentifier:@"com.apple.MobileSafari"];
 
// iOS 6
SBIconModel *model = [[%c(SBIconController) sharedInstance] model];
SBIcon *appIcon = [model expectedIconForDisplayIdentifier:@"com.apple.MobileSafari"]; // expectedIconForDisplayIdentifier: has a larger scope than -applicationIconForDisplayIdentifier:
SBIconView *view = [[%c(SBIconView) alloc] initWithDefaultSize];
view.icon = appIcon;
view.delegate = <an object conforming to SBIconViewDelegate>; // SBIconController conforms to this protocol, and SBIconViewMap conforms to SBIconViewObserver.
 
// iOS 7
SBApplication *app = [[%c(SBApplicationController) sharedInstance] applicationWithDisplayIdentifier:@"com.apple.MobileSafari"];
SBApplicationIcon *appIcon = [[%c(SBApplicationIcon) alloc] initWithApplication:app];
SBIconView *iconView = [[%c(SBIconView) alloc] initWithDefaultSize];
iconView.icon = appIcon;
iconView.delegate = <an object conforming to SBIconViewDelegate>; // [%c(SBIconController) sharedInstance]
 
// iOS 8
SBApplication *app = [[%c(SBApplicationController) sharedInstance] applicationWithBundleIdentifier:@"com.apple.MobileSafari"];
SBApplicationIcon *appIcon = [[%c(SBApplicationIcon) alloc] initWithApplication:app];
SBIconView *iconView = [[%c(SBIconView) alloc] initWithDefaultSize];
iconView.icon = appIcon;
iconView.delegate = <an object conforming to SBIconViewDelegate>; // [%c(SBIconController) sharedInstance]
 
// iOS 9
SBApplication *app = [[%c(SBApplicationController) sharedInstance] applicationWithBundleIdentifier:@"com.apple.MobileSafari"];
SBApplicationIcon *appIcon = [[%c(SBApplicationIcon) alloc] initWithApplication:app];
SBIconView *iconView = [[%c(SBIconView) alloc] initWithContentType:0]; // Apparently, zero for default icon size, one for anywhere else
iconView.icon = appIcon;
iconView.delegate = <an object conforming to SBIconViewDelegate>; // [%c(SBIconController) sharedInstance];
 
</source>
 
== Getting a home screen icon view ==
 
Generally, creating a new instance of SBIconView is <i>not</i> what you'd want. To manipulate the icon views in an [[SBIconListView]], you need to use [[SBIconViewMap]] to get a specific instance of an icon view.
 
<source lang="objc">
// For iOS 6.0-9.2
SBIcon *icon = [[%c(SBIconController) sharedInstance].model expectedIconForDisplayIdentifier:@"com.apple.AppStore"];
SBIconView *iconView = [[%c(SBIconViewMap) homescreenMap] mappedIconViewForIcon:icon];
 
// For iOS 9.3+
SBIcon *icon = [[%c(SBIconController) sharedInstance].model expectedIconForDisplayIdentifier:@"com.apple.AppStore"];
SBIconView *iconView = [[[%c(SBIconController) sharedInstance] homescreenIconViewMap] mappedIconViewForIcon:icon];
 
 
// Beware of icon view recycling. If `icon` is more than one page away from the current page on SB, this method will return nil
// This is not recommended, but to bypass this, use -[SBIconViewMap iconViewForIcon:], which creates a new icon view if one doesn't exist, adds it to its backing store, and returns it.
// This icon view will not have a superview since the SBIconListView instance for it simply doesn't exist!
</source>
 
== SBIconViewLocation ==
 
<source lang="objc">
<source lang="objc">
  SBIcon *sbicon = [[objc_getClass("SBIconModel") sharedInstance] applicationIconForDisplayIdentifier:@"com.apple.MobileSafari"];
// pre iOS 7
  SBIconView *view = [[objc_getClass("SBIconView") alloc]initWithDefaultSize];
typedef enum {
     // set an SBIcon to get the display name and icon image
SBIconViewLocationNormal  = 0,
    [view setIcon:sbicon];
SBIconViewLocationDock     = 1,
    view.delegate = self;
SBIconViewLocationSwitcher = 2
} SBIconViewLocation;


// iOS 7
typedef enum {
SBIconViewLocationNormal  = 0,
SBIconViewLocationStark    = 1,
SBIconViewLocationDock    = 2,
SBIconViewLocationSwitcher = 3,
SBIconViewLocationFolder  = 4
} SBIconViewLocation;
// iOS 8.0 - 8.3
typedef enum {
SBIconViewLocationNormal    = 0,
SBIconViewLocationStark    = 1,
SBIconViewLocationDock      = 2,
SBIconViewLocationSwitcher  = 3,
SBIconViewLocationSwitcherLandscape = 4,
SBIconViewLocationFolder    = 5
} SBIconViewLocation;
// iOS 8.4
typedef enum {
SBIconViewLocationNormal    = 1,
SBIconViewLocationStark    = 2,
SBIconViewLocationDock      = 3,
SBIconViewLocationSwitcher  = 4,
SBIconViewLocationSwitcherLandscape = 5,
SBIconViewLocationFolder    = 6
} SBIconViewLocation;
// iOS  9.0+
typedef enum {
SBIconViewLocationNormal    = 1,
SBIconViewLocationStark    = 2,
SBIconViewLocationDock      = 3,
    /* SBIconViewLocationUnknown  = 4, */
SBIconViewLocationSwitcher  = 5,
SBIconViewLocationSwitcherLandscape = 6,
SBIconViewLocationFolder    = 7
} SBIconViewLocation;
@property (nonatomic, assign) SBIconViewLocation location;
</source>
</source>


=== Delegate Methods ===
SBIconView uses an enum for its location property. There are up to 6 kinds of locations (these are not the names Apple uses):
The location is used by SBIconView to decide what kind of shadow to use, badge text (handled by SBIcon's badgeTextForLocation: (pre iOS 7) or accessoryTextForLocation: (iOS 7+)), and a few other things.
 
== Ghosting (iOS 6 and earlier) ==


SBIconView also handles all the "ghosting" that is done when a folder or the switcher is opened. The way ghosting works is as follows:
<source lang="objc">
<source lang="objc">
- (BOOL)iconAllowJitter:(SBIconView *)arg1 {
SBIconView *iconView = ...;
   
[iconView prepareGhostlyImageIfNeeded]; // creates grayscale image used
    return YES;
[iconView prepareGhostlyImageView]; // prepares a UIImageView with the grayscale image which is the same size as the current icon view image. Grayscale images are cached in NSTemporaryDirectory()
[iconView setGhostly:YES requester:kGhostlyRequesterFolder /* 1 */];
// Or, to fade icons out
[iconView setPartialGhostly:/* CGFloat from 0.0 to 1.0 */ requester:/* a requester */];
</source>
The last two calls in the above code place the ghostly image view under the current icon image view, and the main icon image view and badge (if one exists) are faded out.
All methods for ghosting usually take an integer argument, <b>requester</b>. This is for the cases when the switcher is opened when there is already an open folder. Both the folder and switcher need the icon to be ghosted, and that's where the requester comes in. SBIconView uses requesters to keep track of how many clients have asked for it to be ghosted. In SpringBoard, there are only 2:
folders (requester id 1) and the switcher (requester id 2). Use either one based on your needs.
 
== Delegate Methods ==
 
<source lang="objc">
- (BOOL)iconPositionIsEditable:(id)iconView {  
return NO;
}
}
- (BOOL)iconPositionIsEditable:(id)arg1 {
- (BOOL)iconShouldAllowTap:(id)iconView {
   
return YES;
    return NO;
}
}
- (void)iconHandleLongPress:(SBIconView *)arg1 {
- (void)iconTapped:(id)iconView {
   
[iconView.icon launchFromViewSwitcher];
    [arg1 setIsJittering:YES];
}
}
- (void)iconTouchBegan:(SBIconView *)arg1 {
- (void)iconHandleLongPress:(id)iconView {
    [arg1 setHighlighted:YES];
[iconView setIsJittering:YES];
   
   
}
}
- (void)icon:(id)arg1 touchMovedWithEvent:(id)arg2 {
- (void)iconTouchBegan:(id)arg1 {
   
[iconView setHighlighted:YES];
   
}
}
- (void)icon:(SBIconView *)arg1 touchEnded:(BOOL)arg2 {
- (void)icon:(id)iconView touchMovedWithEvent:(id)event{
    [arg1 setHighlighted:NO delayUnhighlight:NO];
return;
 
}
}
- (BOOL)iconShouldAllowTap:(id)arg1 {
- (void)icon:(id)iconView touchEnded:(BOOL)touchEnded {
    return YES;
[iconView setHighlighted:NO delayUnhighlight:NO];
}
}
- (void)iconTapped:(SBIconView *)arg1 {
- (BOOL)icon:(id)iconView canReceiveGrabbedIcon:(id)iconView {
    [arg1.icon launchFromViewSwitcher];
return NO;
}
}
- (BOOL)icon:(id)arg1 canReceiveGrabbedIcon:(id)arg2 {
- (int)closeBoxTypeForIcon:(id)iconView {
    return NO;
return 1;
}
}
 
- (void)iconCloseBoxTapped:(id)iconView {
- (int)closeBoxTypeForIcon:(id)arg1 {
return;
   
    return 1;
}
}
- (void)iconCloseBoxTapped:(id)arg1 {
- (BOOL)iconAllowJitter:(id)iconView {
   
return YES;
}
}
- (BOOL)iconShouldPrepareGhostlyImage:(id)arg1 {
- (BOOL)iconShouldPrepareGhostlyImage:(id)iconView {
   
return YES;
    return YES;
}
}
- (BOOL)iconViewDisplaysBadges:(id)arg1 {
- (BOOL)iconViewDisplaysBadges:(id)iconView {
   
return NO;
    return NO;
}
}
</source>
</source>


=== Creating Modified Versions ===
== Creating Modified Versions ==


The '''SBIconView''' class can be easily modified by subclassing.
The '''SBIconView''' class can be easily modified by subclassing.


These custom classes can be inserted into SpringBoard by hooking the following method in any class that conforms to the SBIconViewMapDelegate protocol:
These custom classes can be inserted into SpringBoard by hooking the following method in any class that conforms to the SBIconViewMapDelegate protocol:
<source lang="objc">
<source lang="objc">
- (Class)viewMap:(id)arg1 iconViewClassForIcon:(id)arg2
- (Class)viewMap:(id)viewMap iconViewClassForIcon:(id)icon {
{
return objc_getClass("MyClass");
    return [objc_getClass("MyClass") class];
}
}


</source>
</source>
== References ==
<references/>


{{occlass|library=SpringBoard.app|navbox=1}}
{{occlass|library=SpringBoard.app|navbox=1}}

Latest revision as of 05:03, 4 August 2016

SBIconView is a very versatile UIView subclass introduced in iOS 5 that is used to display icons in SpringBoard. In iOS 5 Apple split up icons into SBIcon (an abstract class) and SBIconView, introducing some degree of sanity with it. SBIconView conforms to the SBIconObserver protocol, and in turn exposes a protocol of its own: SBIconViewObserver. A lot of SpringBoard's home screen functionality can be modified by hooking the methods in SBIconView. One of the most magical methods in SBIconView is -setIcon:. It takes an instance of SBIcon and uses the information therein to create a semi-functional icon view.

Creating an Instance

// iOS 5
SBIcon *icon = [[%c(SBIconModel) sharedInstance] applicationIconForDisplayIdentifier:@"com.apple.MobileSafari"];

// iOS 6
SBIconModel *model = [[%c(SBIconController) sharedInstance] model];
SBIcon *appIcon = [model expectedIconForDisplayIdentifier:@"com.apple.MobileSafari"]; // expectedIconForDisplayIdentifier: has a larger scope than -applicationIconForDisplayIdentifier:
SBIconView *view = [[%c(SBIconView) alloc] initWithDefaultSize];
view.icon = appIcon;
view.delegate = <an object conforming to SBIconViewDelegate>; // SBIconController conforms to this protocol, and SBIconViewMap conforms to SBIconViewObserver.

// iOS 7
SBApplication *app = [[%c(SBApplicationController) sharedInstance] applicationWithDisplayIdentifier:@"com.apple.MobileSafari"];
SBApplicationIcon *appIcon = [[%c(SBApplicationIcon) alloc] initWithApplication:app];
SBIconView *iconView = [[%c(SBIconView) alloc] initWithDefaultSize];
iconView.icon = appIcon;
iconView.delegate = <an object conforming to SBIconViewDelegate>; // [%c(SBIconController) sharedInstance]

// iOS 8
SBApplication *app = [[%c(SBApplicationController) sharedInstance] applicationWithBundleIdentifier:@"com.apple.MobileSafari"];
SBApplicationIcon *appIcon = [[%c(SBApplicationIcon) alloc] initWithApplication:app];
SBIconView *iconView = [[%c(SBIconView) alloc] initWithDefaultSize];
iconView.icon = appIcon;
iconView.delegate = <an object conforming to SBIconViewDelegate>; // [%c(SBIconController) sharedInstance]

// iOS 9
SBApplication *app = [[%c(SBApplicationController) sharedInstance] applicationWithBundleIdentifier:@"com.apple.MobileSafari"];
SBApplicationIcon *appIcon = [[%c(SBApplicationIcon) alloc] initWithApplication:app];
SBIconView *iconView = [[%c(SBIconView) alloc] initWithContentType:0]; // Apparently, zero for default icon size, one for anywhere else
iconView.icon = appIcon;
iconView.delegate = <an object conforming to SBIconViewDelegate>; // [%c(SBIconController) sharedInstance];

Getting a home screen icon view

Generally, creating a new instance of SBIconView is not what you'd want. To manipulate the icon views in an SBIconListView, you need to use SBIconViewMap to get a specific instance of an icon view.

// For iOS 6.0-9.2
SBIcon *icon = [[%c(SBIconController) sharedInstance].model expectedIconForDisplayIdentifier:@"com.apple.AppStore"];
SBIconView *iconView = [[%c(SBIconViewMap) homescreenMap] mappedIconViewForIcon:icon];

// For iOS 9.3+
SBIcon *icon = [[%c(SBIconController) sharedInstance].model expectedIconForDisplayIdentifier:@"com.apple.AppStore"];
SBIconView *iconView = [[[%c(SBIconController) sharedInstance] homescreenIconViewMap] mappedIconViewForIcon:icon];


// Beware of icon view recycling. If `icon` is more than one page away from the current page on SB, this method will return nil
// This is not recommended, but to bypass this, use -[SBIconViewMap iconViewForIcon:], which creates a new icon view if one doesn't exist, adds it to its backing store, and returns it.
// This icon view will not have a superview since the SBIconListView instance for it simply doesn't exist!

SBIconViewLocation

// pre iOS 7
typedef enum {
	SBIconViewLocationNormal   = 0,
	SBIconViewLocationDock     = 1,
	SBIconViewLocationSwitcher = 2
} SBIconViewLocation;

// iOS 7
typedef enum {
	SBIconViewLocationNormal   = 0,
	SBIconViewLocationStark    = 1,
	SBIconViewLocationDock     = 2,
	SBIconViewLocationSwitcher = 3,
	SBIconViewLocationFolder   = 4
} SBIconViewLocation;

// iOS 8.0 - 8.3
typedef enum {
	SBIconViewLocationNormal    = 0,
	SBIconViewLocationStark     = 1,
	SBIconViewLocationDock      = 2,
	SBIconViewLocationSwitcher  = 3,
	SBIconViewLocationSwitcherLandscape = 4,
	SBIconViewLocationFolder    = 5
} SBIconViewLocation;

// iOS 8.4
typedef enum {
	SBIconViewLocationNormal    = 1,
	SBIconViewLocationStark     = 2,
	SBIconViewLocationDock      = 3,
	SBIconViewLocationSwitcher  = 4,
	SBIconViewLocationSwitcherLandscape = 5,
	SBIconViewLocationFolder    = 6
} SBIconViewLocation;

// iOS  9.0+
typedef enum {
	SBIconViewLocationNormal    = 1,
	SBIconViewLocationStark     = 2,
	SBIconViewLocationDock      = 3,
     /* SBIconViewLocationUnknown   = 4, */
	SBIconViewLocationSwitcher  = 5,
	SBIconViewLocationSwitcherLandscape = 6,
	SBIconViewLocationFolder    = 7
} SBIconViewLocation;

@property (nonatomic, assign) SBIconViewLocation location;

SBIconView uses an enum for its location property. There are up to 6 kinds of locations (these are not the names Apple uses): The location is used by SBIconView to decide what kind of shadow to use, badge text (handled by SBIcon's badgeTextForLocation: (pre iOS 7) or accessoryTextForLocation: (iOS 7+)), and a few other things.

Ghosting (iOS 6 and earlier)

SBIconView also handles all the "ghosting" that is done when a folder or the switcher is opened. The way ghosting works is as follows:

SBIconView *iconView = ...;
[iconView prepareGhostlyImageIfNeeded]; // creates grayscale image used 
[iconView prepareGhostlyImageView]; // prepares a UIImageView with the grayscale image which is the same size as the current icon view image. Grayscale images are cached in NSTemporaryDirectory()
[iconView setGhostly:YES requester:kGhostlyRequesterFolder /* 1 */];
// Or, to fade icons out
[iconView setPartialGhostly:/* CGFloat from 0.0 to 1.0 */ requester:/* a requester */];

The last two calls in the above code place the ghostly image view under the current icon image view, and the main icon image view and badge (if one exists) are faded out. All methods for ghosting usually take an integer argument, requester. This is for the cases when the switcher is opened when there is already an open folder. Both the folder and switcher need the icon to be ghosted, and that's where the requester comes in. SBIconView uses requesters to keep track of how many clients have asked for it to be ghosted. In SpringBoard, there are only 2: folders (requester id 1) and the switcher (requester id 2). Use either one based on your needs.

Delegate Methods

- (BOOL)iconPositionIsEditable:(id)iconView { 
	return NO;
}
- (BOOL)iconShouldAllowTap:(id)iconView {
	return YES;
}
- (void)iconTapped:(id)iconView {
	[iconView.icon launchFromViewSwitcher];
}
- (void)iconHandleLongPress:(id)iconView {
	[iconView setIsJittering:YES];
}
- (void)iconTouchBegan:(id)arg1 {
	[iconView setHighlighted:YES];
}
- (void)icon:(id)iconView touchMovedWithEvent:(id)event{
	return;
}
- (void)icon:(id)iconView touchEnded:(BOOL)touchEnded {
	[iconView setHighlighted:NO delayUnhighlight:NO];
}
- (BOOL)icon:(id)iconView canReceiveGrabbedIcon:(id)iconView {
	return NO;
}
- (int)closeBoxTypeForIcon:(id)iconView {
	return 1;
}
- (void)iconCloseBoxTapped:(id)iconView {
	return;
}
- (BOOL)iconAllowJitter:(id)iconView {
	return YES;
}
- (BOOL)iconShouldPrepareGhostlyImage:(id)iconView {
	return YES;
}
- (BOOL)iconViewDisplaysBadges:(id)iconView {
	return NO;
}

Creating Modified Versions

The SBIconView class can be easily modified by subclassing.

These custom classes can be inserted into SpringBoard by hooking the following method in any class that conforms to the SBIconViewMapDelegate protocol:

- (Class)viewMap:(id)viewMap iconViewClassForIcon:(id)icon {
	return objc_getClass("MyClass");
}

References