デバッガを起動するとObjective-Cのソースコードの代わりにアセンブリコードが表示されるようになった。設定をいじった覚えは無いのだが。。
ネットで調べてみるとどうもそういうモードがあるらしい。
Xcodeのデバッガは:なぜ私だけにアセンブラを示し、それを何ですか?
この "Show Disassembly When Debugging" がそのスイッチらしい。これをオフにすると
治った。
一時はデバッグがまともにできないしどうしようかとも思った。やれやれ。
参考まで。
iOS/iPhone/iPad/MacOSX プログラミング, Objective-C, Cocoaなど
2011年8月12日金曜日 | Published in Tips, Xcode 4 | 2 コメント
2011年8月8日月曜日 | Published in Core Graphics, ios 4.3, サンプルあり | 0 コメント
CALayer* layer = self.imageView1.layer;
layer.shadowOffset = CGSizeMake(2.5, 2.5);
layer.shadowColor = [[UIColor blackColor] CGColor];
layer.shadowOpacity = 0.5;@property CGPathRef shadowPath;もしかしてこれを使って任意の場所や形で影が落とせないか。
CALayer* subLayer = imageView.layer;
UIBezierPath* path = [UIBezierPath bezierPathWithRect:
CGRectMake(-10.0, -10.0, subLayer.bounds.size.width+10.0, 10.0)];
subLayer.shadowOffset = CGSizeMake(2.5, 2.5);
subLayer.shadowColor = [[UIColor blackColor] CGColor];
subLayer.shadowOpacity = 0.5;
subLayer.shadowPath = [path CGPath];すると影が落ちた(わかりやすいように画像を縮小してある)。CALayer* subLayer = [CALayer layer];
subLayer.frame = imageView.bounds;
[imageView.layer addSublayer:subLayer];
subLayer.masksToBounds = YES;
UIBezierPath* path = [UIBezierPath bezierPathWithRect:
CGRectMake(-10.0, -10.0, subLayer.bounds.size.width+10.0, 10.0)];
subLayer.shadowOffset = CGSizeMake(2.5, 2.5);
subLayer.shadowColor = [[UIColor blackColor] CGColor];
subLayer.shadowOpacity = 0.5;
subLayer.shadowPath = [path CGPath];- (void)_addDropShadowToView2:(UIView*)toView
{
CALayer* subLayer = [CALayer layer];
subLayer.frame = toView.bounds;
[toView.layer addSublayer:subLayer];
subLayer.masksToBounds = YES;
CGSize size = subLayer.bounds.size;
CGFloat x = -10.0;
CGFloat y = -10.0;
CGMutablePathRef pathRef = CGPathCreateMutable();
CGPathMoveToPoint(pathRef, NULL, x, y);
x += size.width + 10.0;
CGPathAddLineToPoint(pathRef, NULL, x, y);
y += 10.0;
CGPathAddLineToPoint(pathRef, NULL, x, y);
x -= size.width;
CGPathAddLineToPoint(pathRef, NULL, x, y);
y += size.height;
CGPathAddLineToPoint(pathRef, NULL, x, y);
x -= 5.0; // (*)10
CGPathAddLineToPoint(pathRef, NULL, x, y);
y -= size.height; // (*)size.height+10
CGPathAddLineToPoint(pathRef, NULL, x, y);
CGPathCloseSubpath(pathRef);
subLayer.shadowOffset = CGSizeMake(2.5, 2.5);
subLayer.shadowColor = [[UIColor blackColor] CGColor];
subLayer.shadowOpacity = 0.5;
subLayer.shadowPath = pathRef;
CGPathRelease(pathRef);
}実は逆L字型は少し歪んだ形をしている。(*)のついている2行はコメントに記載した値が正しいのだが、これを使うと下図の様にコンテンツ全体に薄い影がかかってしまった。2011年8月1日月曜日 | Published in ios 4.3, ライブラリ開発 | 4 コメント
@protocol LKNumberPadViewDelegate@optional - (void)didTouchNumberPadView:(LKNumberPadView*)numberPadView touchedString:(NSString*)string; - (void)didTouchNumberPadView:(LKNumberPadView*)numberPadView withSequentialString:(NSString*)string; @end
view.enabledSet = [NSSet setWithObjects:
[NSNumber numberWithInt:1],
[NSNumber numberWithInt:2], nil];- (void)- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect frame;
CGColorRef shadowColorRef;
UIColor* drawColor;
// (1) draw keyboard
frame = CGRectMake(0, 0,
self.bounds.size.width / LKNUMBERPADVIEW_KEYBOARD_NUM,
self.bounds.size.height);
:
}enum {
LKNumberPadViewImageLeft = 0,
LKNumberPadViewImageInner,
LKNumberPadViewImageRight,
LKNumberPadViewImageMax
};
@interface LKNumberPadLayer : CALayer {
CGImageRef keytopImages_[LKNumberPadViewImageMax];
}
@property (nonatomic, copy) NSString* character;
@property (nonatomic, assign) int imageKind;
- (CGImageRef)createKeytopImageWithKind:(int)kind;
@endポップアップは左はじ、右はじ、それ以外の3種類の表示が必要になる。あらかじめ3種類の画像を描画しておき、配列 keytopImages_[] に取っておく。- (CGImageRef)createKeytopImageWithKind:(int)kind
{
CGMutablePathRef path = CGPathCreateMutable();
CGPoint p = CGPointMake(LKNUMBERPADVIEW_PADDING_X, LKNUMBERPADVIEW_PADDING_Y);
CGPoint p1 = CGPointZero;
CGPoint p2 = CGPointZero;
p.x += LKNUMBERPADVIEW_PAN_UPPER_RADIUS;
CGPathMoveToPoint(path, NULL, p.x, p.y);
p.x += LKNUMBERPADVIEW_PAN_UPPDER_WIDTH;
CGPathAddLineToPoint(path, NULL, p.x, p.y);
p.y += LKNUMBERPADVIEW_PAN_UPPER_RADIUS;
CGPathAddArc(path, NULL,
p.x, p.y,
LKNUMBERPADVIEW_PAN_UPPER_RADIUS,
3.0*M_PI/2.0,
4.0*M_PI/2.0,
false);
:- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSUInteger index = [self _indexWithEvent:event];
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue
forKey:kCATransactionDisableActions];
[self _updateWithIndex:index];
[CATransaction commit];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSUInteger index = [self _indexWithEvent:event];
if (index != self.touchedIndex) {
[self _updateWithIndex:index];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
self.touchedIndex = -1;
self.numberPadLayer.opacity = 0.0;
[self setNeedsDisplay];
}- (void)_updateWithIndex:(NSUInteger)index
{
self.touchedIndex = index;
NSUInteger number = [self _numberWithIndex:index];
self.touchedString = [NSString stringWithFormat:@"%d", number];
:
[self setNeedsDisplay];
[self.numberPadLayer setNeedsDisplay]; // ポップアップ表示
:
}- (void)drawInContext:(CGContextRef)context
{
CGColorRef shadowColorRef = [[UIColor colorWithWhite:0.1 alpha:1.0] CGColor];
CGContextSetShadowWithColor(context,
CGSizeMake(LKNUMBERPADVIEW_PAN_SHADOW_OFFSET_X,
LKNUMBERPADVIEW_PAN_SHADOW_OFFSET_Y),
LKNUMBERPADVIEW_PAN_SHADOW_BLUR,
shadowColorRef
);
CGImageRef imageRef = keytopImages_[self.imageKind];
CGRect imageFrame = CGRectMake(0, 0,
CGImageGetWidth(imageRef),
CGImageGetHeight(imageRef));
CGContextDrawImage(context, imageFrame, imageRef);
// draw text
CGContextSelectFont(context, "Helvetica Bold", 44, kCGEncodingMacRoman);
CGContextSetTextDrawingMode(context, kCGTextFill);
CGContextSetFillColorWithColor(context, [[UIColor blackColor] CGColor]);
CGContextSetTextMatrix(context, CGAffineTransformMakeScale(1.0, -1.0));
shadowColorRef = [[UIColor whiteColor] CGColor];
CGContextSetShadowWithColor(context,
CGSizeMake(0.0,
1.0),
1.0,
shadowColorRef
);
CGContextShowTextAtPoint(context, 28, 55,
[self.character UTF8String],
[self.character length]);
}2011年7月8日金曜日 | Published in ios 4.3 | 0 コメント
SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [@"xcatsan.com" UTF8String]);この後、接続性をチェックする為に SCNetworkReachabilityGetFlags() を呼び出すとブロックするケースがあった、というのがその記事の内容だったのだが、その理由は DNSによる名前解決に時間がかかっていた為と思われる。
iPhone --◯--> Wifiルータ --×--> 3Gとなっていた場合、iPhoneから見るとネット(Wifi)接続OKだが実際にはインターネットにつながっていない。地下鉄で利用しているとこんなケースがありうる。
iPhone --×--> Wifiルータ --×--> 3G
SCNetworkReachability.h
SCNetworkReachabilityRef
SCNetworkReachabilityCreateWithAddress (
CFAllocatorRef allocator,
const struct sockaddr *address
) __OSX_AVAILABLE_STARTING(__MAC_10_3,__IPHONE_2_0);この関数を使うとDNS名前解決が発生しないので、それを原因とする SCNetworkReachabilityGetFlags() におけるブロッキングが発生しない(はず)。使い方はこんな感じ。struct sockaddr_in sockaddr;
bzero(&sockaddr, sizeof(sockaddr));
sockaddr.sin_len = sizeof(sockaddr);
sockaddr.sin_family = AF_INET;
inet_aton("0.0.0.0", &sockaddr.sin_addr);
reachability_ =
SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *) &sockaddr);
なおこの IPアドレスは接続性のチェックには使われていないようで適当なもの例えば 0.0.0.0 でも動作する。a) iPhoneのWifi設定で DNS設定あり ホスト名ベース => 接続 OK IPアドレスベース => 接続 OK b) iPhoneのWifi設定で DNS設定なし(設定項目を空) ホスト名ベース => 接続 NG IPアドレスベース => 接続 OK
・デバイスの接続状態のみを判断する ・3G 接続できていれば、接続可能 ・Wifi接続できていれば、接続可能(その場合、3Gより優先される) ・指定したホストに ping(ICMP)は飛ばしていない ・APIへホスト名を渡した場合、デバイスが接続可能なら名前解決を実行する ・名前解決に時間がかかると SCNetworkReachabilityGetFlags() はブロックする (名前解決のタイムアウトは実測値で 60秒程度) ・APIへIPアドレスを渡す場合、IPアドレスが実在しなくても接続チェックに影響を与えない。 (例)0.0.0.0
2011年7月7日木曜日 | Published in ios 4.3, UIKit | 7 コメント
- (void)viewDidLoad
{
:
self.tableView.tableHeaderView = self.headerView;
}
ただこのままだと初期表示でこのヘッダが表示されてしまう。- (void)_setHeaderViewHidden:(BOOL)hidden animated:(BOOL)animated
{
CGFloat topOffset = 0.0;
if (hidden) {
topOffset = -self.headerView.frame.size.height;
}
if (animated) {
[UIView animateWithDuration:0.2
animations:^{
self.tableView.contentInset = UIEdgeInsetsMake(topOffset, 0, 0, 0);
}];
} else {
self.tableView.contentInset = UIEdgeInsetsMake(topOffset, 0, 0, 0);
}
}上記を適切なタイミングで呼んでやる。なおアニメーションを制御の対象にしているのは一番最初の初期表示時にはアニメーションを使わないため。typedef enum {
HeaderViewStateHidden = 0,
HeaderViewStatePullingDown,
HeaderViewStateOveredThreshold,
HeaderViewStateStopping
} HeaderViewState;
@interface HeaderView : UIView {
}
@property (nonatomic, retain) IBOutlet UILabel* textLabel;
@property (nonatomic, retain) IBOutlet UILabel* detailTextLabel;
@property (nonatomic, retain) IBOutlet UIImageView* imageView;
@property (nonatomic, retain) IBOutlet UIActivityIndicatorView* activityIndicatorView;
@property (nonatomic, assign) HeaderViewState state;
- (void)setUpdatedDate:(NSDate*)date;
@endラベルなどの表示項目の保持の他、矢印画像のアニメーション、UIActivityIndicatorView の表示制御をここで行わせる。またプルダウンの状態管理もここで行う。プルダウンの状態は4つ定義している。typedef enum {
HeaderViewStateHidden = 0, // ヘッダが隠れた状態
HeaderViewStatePullingDown, // プルダウン状態1(ただし閾値は超えていない)
HeaderViewStateOveredThreshold, // プルダウン状態2(閾値を越えている)
HeaderViewStateStopping // プルダウン停止状態(処理中)
} HeaderViewState;状態毎にラベルの表示内容、矢印画像の向き、UIActivityIndicatorView の表示・非表示を決める。state プロパティのセッターメソッドを自前で用意してこの処理を書いておく。- (void)setState:(HeaderViewState)state
{
switch (state) {
case HeaderViewStateHidden:
[self.activityIndicatorView stopAnimating];
self.imageView.hidden = YES;
break;
case HeaderViewStatePullingDown:
[self.activityIndicatorView stopAnimating];
self.imageView.hidden = NO;
self.textLabel.text = @"引き下げて...";
if (state_ != HeaderViewStatePullingDown) {
[self _animateImageForHeadingUp:NO];
}
break;
case HeaderViewStateOveredThreshold:
[self.activityIndicatorView stopAnimating];
self.imageView.hidden = NO;
self.textLabel.text = @"指をはなすと更新";
if (state_ == HeaderViewStatePullingDown) {
[self _animateImageForHeadingUp:YES];
}
break;
case HeaderViewStateStopping:
[self.activityIndicatorView startAnimating];
self.textLabel.text = @"更新中...";
self.imageView.hidden = YES;
break;
}
state_ = state;
}画像の回転は前回のブログで紹介した。- (void)_animateImageForHeadingUp:(BOOL)headingUp
{
CGFloat startAngle = headingUp ? 0 : M_PI + 0.00001;
CGFloat endAngle = headingUp ? M_PI + 0.00001 : 0;
self.imageView.transform = CGAffineTransformMakeRotation(startAngle);
[UIView animateWithDuration:0.1
animations:^{
self.imageView.transform =
CGAffineTransformMakeRotation(endAngle);
}];
}headingUp によって上→下、下→上のアニメーションを切り替えている。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (self.headerView.state == HeaderViewStateStopping) {
return;
}
CGFloat threshold = self.headerView.frame.size.height;
if (PULLDOWN_MARGIN <= scrollView.contentOffset.y &&
scrollView.contentOffset.y < threshold) {
self.headerView.state = HeaderViewStatePullingDown;
} else if (scrollView.contentOffset.y < PULLDOWN_MARGIN) {
self.headerView.state = HeaderViewStateOveredThreshold;
} else {
self.headerView.state = HeaderViewStateHidden;
}
}図にすると多分わかりやすい。- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (self.headerView.state == HeaderViewStateOveredThreshold) {
self.headerView.state = HeaderViewStateStopping;
[self _setHeaderViewHidden:NO animated:YES];
[self performSelector:@selector(_taskFinished) withObject:nil afterDelay:2.0];
}
}scrollViewDidEndDragging:willDelegate: は UIScrollView を指でドラッグした後、指を離したときに呼び出される。このメソッドが呼び出された時(=指を離した時)にプルダウン状態が HeaderViewStateOveredThreshold ならヘッダを表示状態(位置固定)にしている。サンプルでは2秒のディレイを入れていて、これが終わったら再びヘッダを閉じる処理を入れている。- (void)_taskFinished
{
self.headerView.state = HeaderViewStateHidden;
[self.headerView setUpdatedDate:[NSDate date]];
[self _setHeaderViewHidden:YES animated:YES];
}なお UIScrollViewDelegate でプルダウンを検出するアイディアは下記の本がヒントになった。 2011年7月6日水曜日 | Published in ios 4.3, UIKit | 0 コメント
- (IBAction)start:(id)sender {
self.imageView.transform = CGAffineTransformMakeRotation(0);
[UIView animateWithDuration:0.2
animations:^{
self.imageView.transform =
CGAffineTransformMakeRotation(2*M_PI*180/360.0-0.000001);
}];
}最初に transform を CGAffineTransformMakeRotation(0)(角度 0)で初期化し、アニメーションブロックで transform を180度回転させた値を設定している。CGAffineTransformMakeRotation の引数の単位はラジアンなので 2π x 度/360 で角度変換する。最後の -0000001 は右側で回転させる為のおまじない。 これが無いと左側での回転となる。こうやってアニメーションブロックに入れておくと回転中の画像が自動的に作られてスムーズなアニメーションが行える。- (void)fire:(NSTimer*)timer
{
angle_ += 30.0;
self.imageView.transform = CGAffineTransformMakeRotation(2*M_PI*angle_/360.0);
if (angle_ >= 180.0) {
angle_ = 0;
self.button.enabled = YES;
[timer invalidate];
}
}
- (IBAction)start:(id)sender {
[NSTimer scheduledTimerWithTimeInterval:0.05
target:self
selector:@selector(fire:)
userInfo:nil
repeats:YES];
}上記は 30度単位で回しているがカクカク感が多少ある。UIView のアニメーションを使うとこの当たりの調整が不要で簡単にアニメーションができるので非常に便利。画像も1種類用意しておけば使いまわせるのもいい。2011年6月29日水曜日 | Published in ios 4.3, UIKit | 0 コメント
- (void)viewDidLoad
{
[super viewDidLoad];
:
UISwipeGestureRecognizer* swipeGesture =
[[UISwipeGestureRecognizer alloc]
initWithTarget:self action:@selector(didSwipeCell:)];
swipeGesture.direction = UISwipeGestureRecognizerDirectionRight;
[self.tableView addGestureRecognizer:swipeGesture];
[swipeGesture release];
swipeGesture =
[[UISwipeGestureRecognizer alloc]
initWithTarget:self action:@selector(didSwipeCell:)];
swipeGesture.direction = UISwipeGestureRecognizerDirectionLeft;
[self.tableView addGestureRecognizer:swipeGesture];
[swipeGesture release];
}ハンドラはこう。- (void)didSwipeCell:(UISwipeGestureRecognizer*)swipeRecognizer
{
CGPoint loc = [swipeRecognizer locationInView:self.tableView];
NSIndexPath* indexPath = [self.tableViewindexPathForRowAtPoint:loc];
CustomCell* cell = (CustomCell*)[self.tableViewcellForRowAtIndexPath:indexPath];
if ([openedIndexPath_ isEqual:indexPath]) {
if (swipeRecognizer.direction == UISwipeGestureRecognizerDirectionLeft) {
// close cell
[cell setSlideOpened:NO animated:YES];
openedIndexPath_ = nil;
}
} else if (swipeRecognizer.direction == UISwipeGestureRecognizerDirectionRight) {
if (openedIndexPath_) {
// close previous opened cell
NSArray* visibleIndexPaths = [self.tableView indexPathsForVisibleRows];
if ([visibleIndexPaths containsObject:openedIndexPath_]) {
CustomCell* openedCell =
(CustomCell*)[self.tableView cellForRowAtIndexPath:openedIndexPath_];
[openedCell setSlideOpened:NO animated:YES];
}
}
// open new cell
[cell setSlideOpened:YES animated:YES];
openedIndexPath_ = indexPath;
}
}アーキテクチャで説明した②③④、②’③’を実行する。開けるのは右方向のスワイプの時だけ、また閉める時は左スワイプの時だけ有効にしてある。スワイプの結果開いた状態のセルが存在する場合はその場所を openedIndexPath_ へ取っておく。@interface CustomCell : UITableViewCell {
BOOL slideOpened_;
}
:
- (void)setSlideOpened:(BOOL)slideOpened animated:(BOOL)animated;無駄な開け閉めを防ぐ為、現在の状態を slideOpened_ にとっておいてある。実装はこんな感じ。- (void)setSlideOpened:(BOOL)slideOpened animated:(BOOL)animated
{
if (slideOpened == slideOpened_) {
return;
}
slideOpened_ = slideOpened;
if (animated) {
if (slideOpened_) {
// open slide
[UIViewanimateWithDuration:0.2
animations:^{
CGRect frame = self.baseView.frame;
frame.origin.x += frame.size.width;
self.baseView.frame = frame;
}];
} else {
// close slide
[UIViewanimateWithDuration:0.1
animations:^{
CGRect frame = self.baseView.frame;
frame.origin.x = 0;
self.baseView.frame = frame;
}];
}
} else {
CGRect frame = self.baseView.frame;
if (slideOpened_) {
// open slide
frame.origin.x += frame.size.width;
} else {
// close slide
frame.origin.x = 0;
}
self.baseView.frame = frame;
}
}わざわざアニメーション有り・無しのケースを用意しているのは、セルを初期表示する場合などアニメーションが不要なケースがあるから。アニメーション処理自体は UIViewanimateWithDuration:animations: を使いビューの位置を変えているだけの単純なもの。開くときと閉じるときでかかる時間を変えてみた。RootViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
:
// Configure the cell.
:
if ([openedIndexPath_ isEqual:indexPath]) {
[cell setSlideOpened:YES animated:NO];
} else {
[cell setSlideOpened:NO animated:NO];
}
return cell;
}文字や画像の設定の他、スライドの開閉状態を反映しておく。その判断にスワイプジェスチャの処理で取っておいた openedIndexPath_ を使う。@interface BaseView : UIView {
}
@property (nonatomic, assign) BOOL selected;
@end
@implementation BaseView
@synthesize selected;- (void)drawRect:(CGRect)rect
{
// draw
if (selected) {
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGFloat components[] = { 0.9f, 0.9f, 0.9f, 0.9f,
0.7f, 0.7f, 0.7f, 0.7f };
size_t count = sizeof(components)/ (sizeof(CGFloat)* 4);
CGContextAddRect(context, self.frame);
CGRect frame = self.bounds;
CGPoint startPoint = frame.origin;
CGPoint endPoint = frame.origin;
endPoint.y = frame.origin.y + frame.size.height;
CGGradientRef gradientRef =
CGGradientCreateWithColorComponents(colorSpaceRef, components, NULL, count);
CGContextDrawLinearGradient(context,
gradientRef,
startPoint,
endPoint,
kCGGradientDrawsAfterEndLocation);
CGGradientRelease(gradientRef);
CGColorSpaceRelease(colorSpaceRef);
}
}
@endなおSelection を Noneにすると押している間のハイライト状態の表示が変わらなくなるのでこれも手当しておく。- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
UIColor* selectedColor = [UIColor whiteColor]; // default color
if (highlighted) {
selectedColor = [UIColor lightGrayColor];
}
self.baseView.backgroundColor = selectedColor;
[super setHighlighted:highlighted animated:animated];
}これで選択状態はこうなった。@interface SlideView : UIView {
}
@end
@implementation SlideView
#define CUSTOMCELL_OBJECT_LENGTH 10.0
#define CUSTOMCELL_SHADOW_OFFSET 5.0
#define CUSTOMCELL_SHADOW_BLUR 5.0
- (void)drawRect:(CGRect)rect
{
// draw edge shadow
CGRect frame = self.bounds;
frame.origin.x -= CUSTOMCELL_OBJECT_LENGTH;
frame.origin.y -= CUSTOMCELL_OBJECT_LENGTH;
frame.size.width += CUSTOMCELL_OBJECT_LENGTH;
frame.size.height = CUSTOMCELL_OBJECT_LENGTH;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetShadow(context,CGSizeMake(
CUSTOMCELL_SHADOW_OFFSET, CUSTOMCELL_SHADOW_OFFSET),
CUSTOMCELL_SHADOW_BLUR);
[[UIColorwhiteColor] setFill];
CGContextFillRect(context, frame);
}
@end左上の描画枠外に矩形を描きその影の部分だけ表示するようにするといいあんばいとなる。