可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm designing an iOS app with a UICollectionView, and I'd like users to be able to select multiple items within this view. It seems there's a standard style of checkmark Apple uses in this kind of situation. For example, in the image below you can see it when selecting multiple photos in a share sheet.
According to the documentation, you are responsible for updating the UI of your cells to reflect their selection state. I know on a UITableViewCell you can set the accessoryType property to add a checkmark, but I can't seem to find any equivalent for a UICollectionViewCell.
Is there a way Apple provides to use this checkmark in my app, besides trying to rip this icon out of a screenshot?
回答1:
I ended up recreating the checkmarks using PaintCode. Here's what they look like:
They're drawn with vector graphics, so they'll look great at whatever size you want. These are 30x30. I also included an option to use a grayed-out checkmark instead of the open circle when an item is not selected.
To use these, copy the following class into your project. Then, add a UIView to your storyboard or xib, and set its custom class to SSCheckMark.
SSCheckMark.h
#import <UIKit/UIKit.h>
typedef NS_ENUM( NSUInteger, SSCheckMarkStyle )
{
SSCheckMarkStyleOpenCircle,
SSCheckMarkStyleGrayedOut
};
@interface SSCheckMark : UIView
@property (readwrite) bool checked;
@property (readwrite) SSCheckMarkStyle checkMarkStyle;
@end
SSCheckMark.m
#import "SSCheckMark.h"
@implementation SSCheckMark
- (void) drawRect:(CGRect)rect
{
[super drawRect:rect];
if ( self.checked )
[self drawRectChecked:rect];
else
{
if ( self.checkMarkStyle == SSCheckMarkStyleOpenCircle )
[self drawRectOpenCircle:rect];
else if ( self.checkMarkStyle == SSCheckMarkStyleGrayedOut )
[self drawRectGrayedOut:rect];
}
}
- (void) setChecked:(bool)checked
{
_checked = checked;
[self setNeedsDisplay];
}
- (void) setCheckMarkStyle:(SSCheckMarkStyle)checkMarkStyle
{
_checkMarkStyle = checkMarkStyle;
[self setNeedsDisplay];
}
- (void) drawRectChecked: (CGRect) rect
{
//// General Declarations
CGContextRef context = UIGraphicsGetCurrentContext();
//// Color Declarations
UIColor* checkmarkBlue2 = [UIColor colorWithRed: 0.078 green: 0.435 blue: 0.875 alpha: 1];
//// Shadow Declarations
UIColor* shadow2 = [UIColor blackColor];
CGSize shadow2Offset = CGSizeMake(0.1, -0.1);
CGFloat shadow2BlurRadius = 2.5;
//// Frames
CGRect frame = self.bounds;
//// Subframes
CGRect group = CGRectMake(CGRectGetMinX(frame) + 3, CGRectGetMinY(frame) + 3, CGRectGetWidth(frame) - 6, CGRectGetHeight(frame) - 6);
//// Group
{
//// CheckedOval Drawing
UIBezierPath* checkedOvalPath = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(CGRectGetMinX(group) + floor(CGRectGetWidth(group) * 0.00000 + 0.5), CGRectGetMinY(group) + floor(CGRectGetHeight(group) * 0.00000 + 0.5), floor(CGRectGetWidth(group) * 1.00000 + 0.5) - floor(CGRectGetWidth(group) * 0.00000 + 0.5), floor(CGRectGetHeight(group) * 1.00000 + 0.5) - floor(CGRectGetHeight(group) * 0.00000 + 0.5))];
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, shadow2Offset, shadow2BlurRadius, shadow2.CGColor);
[checkmarkBlue2 setFill];
[checkedOvalPath fill];
CGContextRestoreGState(context);
[[UIColor whiteColor] setStroke];
checkedOvalPath.lineWidth = 1;
[checkedOvalPath stroke];
//// Bezier Drawing
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(CGRectGetMinX(group) + 0.27083 * CGRectGetWidth(group), CGRectGetMinY(group) + 0.54167 * CGRectGetHeight(group))];
[bezierPath addLineToPoint: CGPointMake(CGRectGetMinX(group) + 0.41667 * CGRectGetWidth(group), CGRectGetMinY(group) + 0.68750 * CGRectGetHeight(group))];
[bezierPath addLineToPoint: CGPointMake(CGRectGetMinX(group) + 0.75000 * CGRectGetWidth(group), CGRectGetMinY(group) + 0.35417 * CGRectGetHeight(group))];
bezierPath.lineCapStyle = kCGLineCapSquare;
[[UIColor whiteColor] setStroke];
bezierPath.lineWidth = 1.3;
[bezierPath stroke];
}
}
- (void) drawRectGrayedOut: (CGRect) rect
{
//// General Declarations
CGContextRef context = UIGraphicsGetCurrentContext();
//// Color Declarations
UIColor* grayTranslucent = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 0.6];
//// Shadow Declarations
UIColor* shadow2 = [UIColor blackColor];
CGSize shadow2Offset = CGSizeMake(0.1, -0.1);
CGFloat shadow2BlurRadius = 2.5;
//// Frames
CGRect frame = self.bounds;
//// Subframes
CGRect group = CGRectMake(CGRectGetMinX(frame) + 3, CGRectGetMinY(frame) + 3, CGRectGetWidth(frame) - 6, CGRectGetHeight(frame) - 6);
//// Group
{
//// UncheckedOval Drawing
UIBezierPath* uncheckedOvalPath = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(CGRectGetMinX(group) + floor(CGRectGetWidth(group) * 0.00000 + 0.5), CGRectGetMinY(group) + floor(CGRectGetHeight(group) * 0.00000 + 0.5), floor(CGRectGetWidth(group) * 1.00000 + 0.5) - floor(CGRectGetWidth(group) * 0.00000 + 0.5), floor(CGRectGetHeight(group) * 1.00000 + 0.5) - floor(CGRectGetHeight(group) * 0.00000 + 0.5))];
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, shadow2Offset, shadow2BlurRadius, shadow2.CGColor);
[grayTranslucent setFill];
[uncheckedOvalPath fill];
CGContextRestoreGState(context);
[[UIColor whiteColor] setStroke];
uncheckedOvalPath.lineWidth = 1;
[uncheckedOvalPath stroke];
//// Bezier Drawing
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(CGRectGetMinX(group) + 0.27083 * CGRectGetWidth(group), CGRectGetMinY(group) + 0.54167 * CGRectGetHeight(group))];
[bezierPath addLineToPoint: CGPointMake(CGRectGetMinX(group) + 0.41667 * CGRectGetWidth(group), CGRectGetMinY(group) + 0.68750 * CGRectGetHeight(group))];
[bezierPath addLineToPoint: CGPointMake(CGRectGetMinX(group) + 0.75000 * CGRectGetWidth(group), CGRectGetMinY(group) + 0.35417 * CGRectGetHeight(group))];
bezierPath.lineCapStyle = kCGLineCapSquare;
[[UIColor whiteColor] setStroke];
bezierPath.lineWidth = 1.3;
[bezierPath stroke];
}
}
- (void) drawRectOpenCircle: (CGRect) rect
{
//// General Declarations
CGContextRef context = UIGraphicsGetCurrentContext();
//// Shadow Declarations
UIColor* shadow = [UIColor blackColor];
CGSize shadowOffset = CGSizeMake(0.1, -0.1);
CGFloat shadowBlurRadius = 0.5;
UIColor* shadow2 = [UIColor blackColor];
CGSize shadow2Offset = CGSizeMake(0.1, -0.1);
CGFloat shadow2BlurRadius = 2.5;
//// Frames
CGRect frame = self.bounds;
//// Subframes
CGRect group = CGRectMake(CGRectGetMinX(frame) + 3, CGRectGetMinY(frame) + 3, CGRectGetWidth(frame) - 6, CGRectGetHeight(frame) - 6);
//// Group
{
//// EmptyOval Drawing
UIBezierPath* emptyOvalPath = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(CGRectGetMinX(group) + floor(CGRectGetWidth(group) * 0.00000 + 0.5), CGRectGetMinY(group) + floor(CGRectGetHeight(group) * 0.00000 + 0.5), floor(CGRectGetWidth(group) * 1.00000 + 0.5) - floor(CGRectGetWidth(group) * 0.00000 + 0.5), floor(CGRectGetHeight(group) * 1.00000 + 0.5) - floor(CGRectGetHeight(group) * 0.00000 + 0.5))];
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, shadow2Offset, shadow2BlurRadius, shadow2.CGColor);
CGContextRestoreGState(context);
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, shadow.CGColor);
[[UIColor whiteColor] setStroke];
emptyOvalPath.lineWidth = 1;
[emptyOvalPath stroke];
CGContextRestoreGState(context);
}
}
@end
回答2:
One possibility is creating a UIView that draws concentric circles, then a checkmark character from your font of choice. To find checkmark characters, go to Edit > Special Characters within Xcode (or any other application with that menu item), and search for "check". When you select one of the search results, you'll see font variations at the bottom right.
回答3:
Swift 3 version of Chris Vasellis' nice solution:
import UIKit
enum SSCheckMarkStyle : UInt {
case OpenCircle
case GrayedOut
}
class SSCheckMark: UIView {
private var checkedBool: Bool = false
// choose whether you like open or grayed out non-selected items
private var checkMarkStyleReal: SSCheckMarkStyle=SSCheckMarkStyle.GrayedOut
var checked: Bool {
get {
return self.checkedBool
}
set(checked) {
self.checkedBool = checked
self.setNeedsDisplay()
}
}
var checkMarkStyle: SSCheckMarkStyle {
get {
return self.checkMarkStyleReal
}
set(checkMarkStyle) {
self.checkMarkStyleReal = checkMarkStyle
self.setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
super.draw(rect)
if self.checked {
self.drawRectChecked(rect: rect)
} else {
if self.checkMarkStyle == SSCheckMarkStyle.OpenCircle {
self.drawRectOpenCircle(rect: rect)
} else if self.checkMarkStyle == SSCheckMarkStyle.GrayedOut {
self.drawRectGrayedOut(rect: rect)
}
}
}
func drawRectChecked(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
let checkmarkBlue2 = UIColor(red: 0.078, green: 0.435, blue: 0.875, alpha: 1)
let shadow2 = UIColor.black
let shadow2Offset = CGSize(width: 0.1, height: -0.1)
let shadow2BlurRadius = 2.5
let frame = self.bounds
let group = CGRect(x: frame.minX + 3, y: frame.minY + 3, width: frame.width - 6, height: frame.height - 6)
let checkedOvalPath = UIBezierPath(ovalIn: CGRect(x: group.minX + floor(group.width * 0.00000 + 0.5), y: group.minY + floor(group.height * 0.00000 + 0.5), width: floor(group.width * 1.00000 + 0.5) - floor(group.width * 0.00000 + 0.5), height: floor(group.height * 1.00000 + 0.5) - floor(group.height * 0.00000 + 0.5)))
context!.saveGState()
context!.setShadow(offset: shadow2Offset, blur: CGFloat(shadow2BlurRadius), color: shadow2.cgColor)
checkmarkBlue2.setFill()
checkedOvalPath.fill()
context!.restoreGState()
UIColor.white.setStroke()
checkedOvalPath.lineWidth = 1
checkedOvalPath.stroke()
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: group.minX + 0.27083 * group.width, y: group.minY + 0.54167 * group.height))
bezierPath.addLine(to: CGPoint(x: group.minX + 0.41667 * group.width, y: group.minY + 0.68750 * group.height))
bezierPath.addLine(to: CGPoint(x: group.minX + 0.75000 * group.width, y: group.minY + 0.35417 * group.height))
bezierPath.lineCapStyle = CGLineCap.square
UIColor.white.setStroke()
bezierPath.lineWidth = 1.3
bezierPath.stroke()
}
func drawRectGrayedOut(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
let grayTranslucent = UIColor(red: 1, green: 1, blue: 1, alpha: 0.6)
let shadow2 = UIColor.black
let shadow2Offset = CGSize(width: 0.1, height: -0.1)
let shadow2BlurRadius = 2.5
let frame = self.bounds
let group = CGRect(x: frame.minX + 3, y: frame.minY + 3, width: frame.width - 6, height: frame.height - 6)
let uncheckedOvalPath = UIBezierPath(ovalIn: CGRect(x: group.minX + floor(group.width * 0.00000 + 0.5), y: group.minY + floor(group.height * 0.00000 + 0.5), width: floor(group.width * 1.00000 + 0.5) - floor(group.width * 0.00000 + 0.5), height: floor(group.height * 1.00000 + 0.5) - floor(group.height * 0.00000 + 0.5)))
context!.saveGState()
context!.setShadow(offset: shadow2Offset, blur: CGFloat(shadow2BlurRadius), color: shadow2.cgColor)
grayTranslucent.setFill()
uncheckedOvalPath.fill()
context!.restoreGState()
UIColor.white.setStroke()
uncheckedOvalPath.lineWidth = 1
uncheckedOvalPath.stroke()
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: group.minX + 0.27083 * group.width, y: group.minY + 0.54167 * group.height))
bezierPath.addLine(to: CGPoint(x: group.minX + 0.41667 * group.width, y: group.minY + 0.68750 * group.height))
bezierPath.addLine(to: CGPoint(x: group.minX + 0.75000 * group.width, y: group.minY + 0.35417 * group.height))
bezierPath.lineCapStyle = CGLineCap.square
UIColor.white.setStroke()
bezierPath.lineWidth = 1.3
bezierPath.stroke()
}
func drawRectOpenCircle(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
let shadow = UIColor.black
let shadowOffset = CGSize(width: 0.1, height: -0.1)
let shadowBlurRadius = 0.5
let shadow2 = UIColor.black
let shadow2Offset = CGSize(width: 0.1, height: -0.1)
let shadow2BlurRadius = 2.5
let frame = self.bounds
let group = CGRect(x: frame.minX + 3, y: frame.minY + 3, width: frame.width - 6, height: frame.height - 6)
let emptyOvalPath = UIBezierPath(ovalIn: CGRect(x: group.minX + floor(group.width * 0.00000 + 0.5), y: group.minY + floor(group.height * 0.00000 + 0.5), width: floor(group.width * 1.00000 + 0.5) - floor(group.width * 0.00000 + 0.5), height: floor(group.height * 1.00000 + 0.5) - floor(group.height * 0.00000 + 0.5)))
context!.saveGState()
context!.setShadow(offset: shadow2Offset, blur: CGFloat(shadow2BlurRadius), color: shadow2.cgColor)
context!.restoreGState()
context!.saveGState()
context!.setShadow(offset: shadowOffset, blur: CGFloat(shadowBlurRadius), color: shadow.cgColor)
UIColor.white.setStroke()
emptyOvalPath.lineWidth = 1
emptyOvalPath.stroke()
context!.restoreGState()
}
}
To use it in your code, create a file called SSCheckMark.swift with the above contents, and assign it to your View. I use it in CollectionViewCells, for which I have created a custom class (code simplified heavily):
class myCollectionViewCell: UICollectionViewCell {
var checkmarkView: SSCheckMark!
override init(frame: CGRect) {
super.init(frame: frame)
checkmarkView = SSCheckMark(frame: CGRect(x: frame.width-40, y: 10, width: 35, height: 35))
checkmarkView.backgroundColor = UIColor.white
myView.addSubview(checkmarkView)
}
}
And in my UIViewController, I have this:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell:ProductCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellID", for: indexPath) as! myCollectionViewCell
cell.checkmarkView.checked = myBoolValue
return cell
}
回答4:
C# version of the same code
public class CheckMarkView : UIView
{
private bool _checked;
private CheckMarkStyle _checkMarkStyle;
public CheckMarkView()
{
Opaque = false;
}
public bool Checked
{
get
{
return _checked;
}
set
{
_checked = value;
SetNeedsDisplay();
}
}
public CheckMarkStyle CheckMarkStyle
{
get
{
return _checkMarkStyle;
}
set
{
_checkMarkStyle = value;
SetNeedsDisplay();
}
}
public override void Draw(CGRect rect)
{
if (Checked)
DrawRectChecked(rect);
else if (CheckMarkStyle == CheckMarkStyle.OpenCircle)
DrawRectOpenCircle(rect);
else if (CheckMarkStyle == CheckMarkStyle.GrayedOut)
DrawRectGrayedOut(rect);
}
private void DrawRectChecked(CGRect rect)
{
var context = UIGraphics.GetCurrentContext();
var checkmarkBlue2 = UIColor.FromRGBA(0.078f, 0.435f, 0.875f, 1f);
// Shadow Declarations
var shadow2 = UIColor.Brown;
var shadow2Offset = new CGSize(0.1, -0.1);
nfloat shadow2BlurRadius = 2.5f;
var frame = Bounds;
// Subframes
var group = new CGRect(frame.GetMinX() + 3, frame.GetMinY() + 3, frame.Width - 6, frame.Height - 6);
// CheckedOval Drawing
var checkedOvalPath = UIBezierPath.FromOval(new CGRect(group.GetMinX() + Math.Floor(group.Width * 0.00000 + 0.5), group.GetMinY() + Math.Floor(group.Height * 0.00000 + 0.5), Math.Floor(group.Width * 1.00000 + 0.5) - Math.Floor(group.Width * 0.00000 + 0.5), Math.Floor(group.Height * 1.00000 + 0.5) - Math.Floor(group.Height * 0.00000f + 0.5f)));
context.SaveState();
context.SetShadow(shadow2Offset, shadow2BlurRadius, shadow2.CGColor);
checkmarkBlue2.SetFill();
checkedOvalPath.Fill();
context.RestoreState();
UIColor.White.SetStroke();
checkedOvalPath.LineWidth = 1;
checkedOvalPath.Stroke();
// Bezier Drawing
var bezierPath = new UIBezierPath();
bezierPath.MoveTo(new CGPoint(group.GetMinX() + 0.27083f * group.Width, group.GetMinY() + 0.54167f * group.Height));
bezierPath.AddLineTo(new CGPoint(group.GetMinX() + 0.41667f * group.Width, group.GetMinY() + 0.68750f * group.Height));
bezierPath.AddLineTo(new CGPoint(group.GetMinX() + 0.75000f * group.Width, group.GetMinY() + 0.35417f * group.Height));
bezierPath.LineCapStyle = CGLineCap.Square;
UIColor.White.SetStroke();
bezierPath.LineWidth = 1.3f;
bezierPath.Stroke();
}
private void DrawRectGrayedOut(CGRect rect)
{
var context = UIGraphics.GetCurrentContext();
var grayTranslucent = UIColor.FromRGBA(1, 1, 1, 0.6f);
// Shadow Declarations
var shadow2 = UIColor.Black;
var shadow2Offset = new CGSize(0.1, -0.1);
nfloat shadow2BlurRadius = 2.5f;
var frame = Bounds;
// Subframes
var group = new CGRect(frame.GetMinX() + 3, frame.GetMinY() + 3, frame.Width - 6, frame.Height - 6);
// UncheckedOval Drawing
var uncheckedOvalPath = UIBezierPath.FromOval(new CGRect(group.GetMinX() + Math.Floor(group.Width * 0.00000 + 0.5), group.GetMinY() + Math.Floor(group.Height * 0.00000 + 0.5), Math.Floor(group.Width * 1.00000 + 0.5) - Math.Floor(group.Width * 0.00000 + 0.5), Math.Floor(group.Height * 1.00000 + 0.5) - Math.Floor(group.Height * 0.00000 + 0.5)));
context.SaveState();
context.SetShadow(shadow2Offset, shadow2BlurRadius, shadow2.CGColor);
grayTranslucent.SetFill();
uncheckedOvalPath.Fill();
context.RestoreState();
UIColor.White.SetStroke();
uncheckedOvalPath.LineWidth = 1f;
uncheckedOvalPath.Stroke();
// Bezier Drawing
var bezierPath = new UIBezierPath();
bezierPath.MoveTo(new CGPoint(group.GetMinX() + 0.27083 * group.Width, group.GetMinY() + 0.54167 * group.Height));
bezierPath.AddLineTo(new CGPoint(group.GetMinX() + 0.41667 * group.Width, group.GetMinY() + 0.68750 * group.Height));
bezierPath.AddLineTo(new CGPoint(group.GetMinX() + 0.75000 * group.Width, group.GetMinY() + 0.35417 * group.Height));
bezierPath.LineCapStyle = CGLineCap.Square;
UIColor.White.SetStroke();
bezierPath.LineWidth = 1.3f;
bezierPath.Stroke();
}
private void DrawRectOpenCircle(CGRect rect)
{
var context = UIGraphics.GetCurrentContext();
// Shadow Declarations
var shadow = UIColor.Black;
var shadowOffset = new CGSize(0.1, -0.1);
nfloat shadowBlurRadius = 0.5f;
var shadow2 = UIColor.Black;
var shadow2Offset = new CGSize(0.1, -0.1);
nfloat shadow2BlurRadius = 2.5f;
var frame = Bounds;
// Subframes
var group = new CGRect(frame.GetMinX() + 3, frame.GetMinY() + 3, frame.Width - 6, frame.Height - 6);
// EmptyOval Drawing
var emptyOvalPath = UIBezierPath.FromOval(new CGRect(group.GetMinX() + Math.Floor(group.Width * 0.00000 + 0.5), group.GetMinY() + Math.Floor(group.Height * 0.00000 + 0.5), Math.Floor(group.Width * 1.00000 + 0.5) - Math.Floor(group.Width * 0.00000 + 0.5), Math.Floor(group.Height * 1.00000 + 0.5) - Math.Floor(group.Height * 0.00000 + 0.5)));
context.SaveState();
context.SetShadow(shadow2Offset, shadow2BlurRadius, shadow2.CGColor);
context.RestoreState();
context.SaveState();
context.SetShadow(shadowOffset, shadowBlurRadius, shadow.CGColor);
UIColor.White.SetStroke();
emptyOvalPath.LineWidth = 1;
emptyOvalPath.Stroke();
context.RestoreState();
}
}
public enum CheckMarkStyle
{
OpenCircle,
GrayedOut
}