먼저 TableViewController의 경우, NSFetchedResultController를 사용하면 가장 효율적으로 관리할 수 있습니다.
특정 개수만 로딩 시켜서, 화면에 표시하므로 모든 데이터를 가져올 필요가 없게 됩니다.
하지만, 특정 경우, 지금 그 Entity의 개수가 몇개인지 알아야 할 경우가 있습니다.
이런때, 해당 Entity가 데이터가 크면, 그 개수 만큼의 NSManagedObject가 만들어지고, 데이터도 다 로딩 되므로, 순간 메모리 사용량이 커지게 됩니다.
이 경우, NSFetchRequest의 setIncludeSubentities를 NO로 해서 subentity들이 로딩 되지 않도록 하고,
context의 countForFetchRequest를 사용해서 해당되는 개수만 읽어 올 수 있게 됩니다.
참조: StackOverflow: Cocoa Core Data efficient way to count entities!
David Bae의 iOS 개발 이야기 (@davidbae) 재미있는 앱 개발을 위하여 개발 자료를 정리합니다. 필요한 것은 댓글로 알려주세요.
2015/01/20
[iOS] unwind 함수를 코딩으로 호출하기.
Storyboard에서 Exit할 수 있는 Unwind segue를 만든다.
- 버튼에서 Exit로 Ctrl-Drag에서 만들 수도 있고
- 해당 View Controller의 아이콘에서 Exit로 Ctrl-Drag해서 Unwind segue를 만든다.
왼쪽 리스트에서 Unwind Segue from XXViewController를 선택하고, 오른쪽 Attribute Inspector에서 Identifier를 설정할 수 있다.
그러면, 코드상에서 performSegueWithIdentifier로 호출할 수 있다.
[참고: stackoverflow - How to perform Unwind segue programmatically?]
- 버튼에서 Exit로 Ctrl-Drag에서 만들 수도 있고
- 해당 View Controller의 아이콘에서 Exit로 Ctrl-Drag해서 Unwind segue를 만든다.
왼쪽 리스트에서 Unwind Segue from XXViewController를 선택하고, 오른쪽 Attribute Inspector에서 Identifier를 설정할 수 있다.
그러면, 코드상에서 performSegueWithIdentifier로 호출할 수 있다.
[참고: stackoverflow - How to perform Unwind segue programmatically?]
2014/12/26
[iOS] 원형의 그라디언트 이미지를 그려봅시다. (Rendering a radial gradient)
Radial Gradient
원형으로 배경을 은은하게 채워야하는 경우에, 아래와 같이 그림이 필요합니다.이미지를 만들어서 넣으면 좋겠지만, Core Graphics를 사용해서 그릴 수 있습니다.
![]() |
만들 이미지 |
- (void)drawRadialGradient:(UIColor *)startColor
endColor:(UIColor *)endColor
startPoint:(CGPoint)startPoint
startRadius:(CGFloat)startRadius
endPoint:(CGPoint)endPoint
endRadius:(CGFloat)endRadius
context:(CGContextRef)context
{
CGColorRef colorRef = startColor.CGColor;
CGColorRef endColorRef = endColor.CGColor;
NSArray *marrColors=[NSArray arrayWithObjects:
(__bridge id)colorRef, //start color
(__bridge id)endColorRef, //end color
nil];
CFArrayRef colors =(__bridge CFArrayRef)(marrColors);
CGColorSpaceRef colorSpc = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColors(colorSpc, colors, Nil);
// generates Radial Gradient
CGContextDrawRadialGradient(context, gradient,
startPoint, startRadius,
endPoint, endRadius,
0);
CGColorSpaceRelease(colorSpc);
CGGradientRelease(gradient);
}
이 함수를 이용해서, 중심에 그리면, 위 만들이미지와 같은 효과를 얻을 수 있습니다.
Drawing a radial gradient like Lens
그러면, Gradient에 이미지를 추가하면 중간에 이미지가 추가가 됩니다.
source code
- (void)drawRadialGradient:(UIColor *)startColor
midColor:(UIColor *)midColor
endColor:(UIColor *)endColor
startPoint:(CGPoint)startPoint
startRadius:(CGFloat)startRadius
endPoint:(CGPoint)endPoint
endRadius:(CGFloat)endRadius
context:(CGContextRef)context
{
CGColorRef colorRef = startColor.CGColor;
CGColorRef midColorRef = midColor.CGColor;
CGColorRef endColorRef = endColor.CGColor;
NSArray *marrColors=[NSArray arrayWithObjects:(__bridge id)colorRef,
(__bridge id)midColorRef,
(__bridge id)endColorRef, nil];
CFArrayRef colors =(__bridge CFArrayRef)(marrColors);
CGColorSpaceRef colorSpc = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColors(colorSpc, colors, Nil);
// generates Radial Gradient
CGContextDrawRadialGradient(context, gradient,
startPoint, startRadius,
endPoint, endRadius,
0);
CGColorSpaceRelease(colorSpc);
CGGradientRelease(gradient);
}
호출하는 소스
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// Drawing code
UIColor *whiteColor = [UIColor whiteColor];
UIColor *blueColor = [UIColor blueColor];
UIColor *greenColor = [UIColor greenColor];
CGSize size = self.frame.size;
CGPoint center = CGPointMake(size.width/2, size.height/2);
/*/
[self drawRadialGradient:greenColor endColor:self.backgroundColor
startPoint:center startRadius:0
endPoint:center endRadius:size.width/2
context:context];
/*/
[self drawRadialGradient:greenColor midColor:blueColor endColor:whiteColor
startPoint:center startRadius:0
endPoint:center endRadius:size.width/2
context:context];
CGPoint point2 = CGPointMake(size.width/2-15, size.height/2-15);
[self drawRadialGradient:whiteColor endColor:greenColor
startPoint:point2 startRadius:0
endPoint:point2 endRadius:5
context:context];
point2 = CGPointMake(size.width/2+15, size.height/2+15);
[self drawRadialGradient:whiteColor endColor:greenColor
startPoint:point2 startRadius:0
endPoint:point2 endRadius:2
context:context];
//*/
}
결과물
![]() |
3가지 색으로만 |
![]() |
3가지 색에, 점 2개 찍은것 |
나름 랜즈와 비슷한 것이 나온것 같군요.
2014/12/11
[iOS] NSString으로 된 값을 파일로 직접 저장하자.
현재 App의 디렉토리 Path를 읽어 오고, 거기에 파일 이름을 추가합니다.
source code
이름을 정하고 나면, NSString에서 바로 저장합니다.
파일로부터 데이터를 읽어 올 경우도, NSString의 카테고리를 이용합니다.
만약, 문자열이 아닌 Binary데이터를 저장하려면, NSString대신, NSData의 카테고리 함수를 이용하면, 똑같이 사용이 가능합니다.
source code
- (NSURL *)urlForFilename:(NSString *)filename {
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *urls = [fm URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask];
NSURL *directoryURL = urls[0];
NSURL *fileURL = [directoryURL URLByAppendingPathComponent:filename];
return fileURL;
}
이름을 정하고 나면, NSString에서 바로 저장합니다.
BOOL status = [string writeToFile:[pathUrl.path stringByAppendingPathExtension:@"txt"]
atomically:YES
encoding:NSUTF8StringEncoding
error:&error];
if (error != nil) {
NSLog(@"save error: %@", [error description]);
}
if (status == NO) {
NSLog(@"save error");
}
파일로부터 데이터를 읽어 올 경우도, NSString의 카테고리를 이용합니다.
NSString *string = [NSString stringWithContentsOfURL:pathUrl
encoding:NSUTF8StringEncoding
error:&error];
if (error != nil || string == nil) {
NSLog(@"Can't load file: %@, error:%@", pathUrl.path, [error description]);
}
만약, 문자열이 아닌 Binary데이터를 저장하려면, NSString대신, NSData의 카테고리 함수를 이용하면, 똑같이 사용이 가능합니다.
2014/10/10
[iOS] User Defined Runtime Attributes
User Defined Runtime Attributes란?
Xcode의 Interface Builder에서 특정한 UI 객체에 사용자가 정의하는 값을 바로 입력하는 기능을 말합니다.Storyboard에 정의된 객체에서 값을 설정할 수 있는 것으로, Identity Inspector tab에서 설정할 수 있습니다.
![]() |
InterfaceBuilder의 User Defined Runtime Attributes 설정 화면과 시뮬레이터 표시 내용. |
위의 그림처럼 Interface Builder에서 UIView를 넣고, Identity Inspector Tab에서 값을 입력하게 되면, m파일에서 UIView의 객체를 연결해서, 직접 입력한 것처럼 동작하게 됩니다.
입력 할 수 있는 값은 어떤 것이 있을까요?
Boolean - BOOLNumber - NSNumber *
String - NSString *
Point - CGPoint
Size - CGSize
Rect - CGRect
Range - NSRange
Color - UIColor *
LocalizedString - NSString *
(Localizable.string 파일에서 Key를 입력하고, 해당 locale에 맞는 string을 입력)
Image - UIImage*
Xcode 6에서는 총 10가지를 입력할 수 있습니다.
위에 첫번째 이미지에서 layer.borderColor는 바로 설정할 수가 없습니다.
전달되는 객채는 UIColor이고, layer.borderColor는 CGColorRef를 사용하기 때문입니다.
가능한 방법은
CALayer의 카테고리를 생성하고, 그 카테고리에 setBorderColorFromUIColor 함수를 만들어 줍니다.
source code
#import "CALayer+extension.h" @implementation CALayer (extension) - (void)setBorderColorFromUIColor:(UIColor *)color { self.borderColor = color.CGColor; } @end
h파일에도 함수를 선언해 주어야 합니다.
이제, InterfaceBuilder에서 layer.borderColorFromUIColor 를 사용해서, 색상을 설정할 수 있습니다.
위의 시뮬레이터에서 테두리 색상이 설정된 것을 볼 수 있습니다.
[참고]
- iOS-Blog : User Defined Runtime Attributes- ATOMIC SPIN : Expanding User-Defined Runtime Attributes in Xcode with Objective-C
2014/09/12
[iOS] 물방울 모양의 UIView를 만들자.
UIView의 모양을 물방울 모양으로 변형을 해보겠습니다.
1. UIBezierPath를 이용해서, 물방울 모양의 Path를 만듭니다.
source code
2. Path로 부터 현재 뷰의 layer의 mask를 설정합니다.
source code
3. 결과물

위 소스의 sp, cp, b1, c1, c2, c3 위치를 왼쪽에 표시하였습니다.
1. UIBezierPath를 이용해서, 물방울 모양의 Path를 만듭니다.
source code
- (UIBezierPath *)waterDropPath:(CGRect)frame { //NSLog(@"makeWaterDropPathIn: %@", NSStringFromCGRect(frame) ); float x = frame.origin.x; float y = frame.origin.y; float hW = frame.size.width/2.0f; float H = frame.size.height; CGPoint sp = CGPointMake(x+hW, y); //Start point CGPoint cp = CGPointMake(x+hW, y+H-hW); //Center point CGPoint b1 = CGPointMake(x, y+H-hW); //WaterDrop left point CGPoint c1 = CGPointMake(x+hW, y+hW+hW/2); //왼쪽 내려오는 부분에서 사용 CGPoint c2 = CGPointMake(x, y+H-hW-hW); CGPoint c3 = CGPointMake(x+hW+hW, y+H-hW-hW); //오른쪽 올라가는 부분에서 사용 UIBezierPath *path = [[UIBezierPath alloc] init]; [path setLineWidth:0.5f]; [path moveToPoint:sp]; //시작점으로 이동 [path addCurveToPoint:b1 controlPoint1:c1 controlPoint2:c2]; //왼쪽 물방울 내려오는 부분 [path addArcWithCenter:cp radius:hW//-0.5f startAngle:DEGREE_TO_RADIAN(180) endAngle:DEGREE_TO_RADIAN(0) clockwise:NO]; //물방울 아래부분 [path addCurveToPoint:sp controlPoint1:c3 controlPoint2:c1]; //오른쪽 올라가는 부분 return path; }
2. Path로 부터 현재 뷰의 layer의 mask를 설정합니다.
source code
- (void) setWaterDropClippingArea:(CGRect)frame { //클립핑 영역을 만든다. [self setClippingAreaFromPath:[self waterDropPath:frame]]; } - (void)setClippingAreaFromPath:(UIBezierPath *)path { CAShapeLayer *mask = [CAShapeLayer layer]; mask.path = path.CGPath; self.layer.mask = mask; }
3. 결과물

위 소스의 sp, cp, b1, c1, c2, c3 위치를 왼쪽에 표시하였습니다.
라벨:
iOS,
mask,
UIBezierPath
2014/07/13
[iOS] 크기에 맞게 Font의 크기 줄이기.
Storyboard에서 문자열을 표시를 해야 되는데...
단어나, 길지 않은 문자열은 크기를 200포인트로 표시하고, 만약 길이가 길다면, 그보다 작은 크기로 크기가 맞을 때까지 폰트의 크기를 줄이는 기능이 필요합니다.
위 소스에서
1: 대상이 되는 문자열과, 그 문자열에 적용되어 있는 Font, 그리고 어떤 너비에서 계산을 해야 되는지 파라미터로 넘겨 줍니다.
2 : while문을 통해서, 크기가 작어질 때까지 적용 합니다.
3 : NSString의 boundingRectWithSize를 이용해서, 대략적인 rect를 구합니다.
여기에서, 너비의 90%정도를 고려해서 넣습니다. 100% 크기를 하면 옆으로 너무 딱 붙어서 계산이 되어서, 90%크기로 적용해 줍니다.
4 : 계산된 크기가, 대상 높이의 70%보다 크면, 줄여 줍니다. 70%는 대략적으로 생각한 것으로 높의 70%이하가 되는 것이 적당하였습니다. (적용할 때, 변경해서 보시면 됩니다.)
5 : 아직 커서 줄여야 되면, 현재 font 의 name과 크기의 90%를 적용해서 폰트를 다시 만들고 적용해 줍니다.
이 폰트를 가지고 while문 안에서 다시 계산을 하게 됩니다.
감사합니다.
단어나, 길지 않은 문자열은 크기를 200포인트로 표시하고, 만약 길이가 길다면, 그보다 작은 크기로 크기가 맞을 때까지 폰트의 크기를 줄이는 기능이 필요합니다.
//1 + (UIFont *) spq_getFontForLabel:(NSString *)string font:(UIFont *)font size:(CGSize)size { BOOL isEnough = NO; UIFont *afont = font; //2 while (!isEnough) { //3 CGRect aboutRect = [string //높이를 구할 NSString boundingRectWithSize:CGSizeMake(size.width*0.9, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:font} context:nil]; //4 if (aboutRect.size.height > (size.height*0.7)) { //5 font = [UIFont fontWithName:font.fontName size:font.pointSize*0.9]; }else{ isEnough = YES; afont = font; } } return afont; }
위 소스에서
1: 대상이 되는 문자열과, 그 문자열에 적용되어 있는 Font, 그리고 어떤 너비에서 계산을 해야 되는지 파라미터로 넘겨 줍니다.
2 : while문을 통해서, 크기가 작어질 때까지 적용 합니다.
3 : NSString의 boundingRectWithSize를 이용해서, 대략적인 rect를 구합니다.
여기에서, 너비의 90%정도를 고려해서 넣습니다. 100% 크기를 하면 옆으로 너무 딱 붙어서 계산이 되어서, 90%크기로 적용해 줍니다.
4 : 계산된 크기가, 대상 높이의 70%보다 크면, 줄여 줍니다. 70%는 대략적으로 생각한 것으로 높의 70%이하가 되는 것이 적당하였습니다. (적용할 때, 변경해서 보시면 됩니다.)
5 : 아직 커서 줄여야 되면, 현재 font 의 name과 크기의 90%를 적용해서 폰트를 다시 만들고 적용해 줍니다.
이 폰트를 가지고 while문 안에서 다시 계산을 하게 됩니다.
감사합니다.
피드 구독하기:
글 (Atom)