Показаны сообщения с ярлыком Objective C. Показать все сообщения
Показаны сообщения с ярлыком Objective C. Показать все сообщения

27 апреля 2013 г.

Смена ориентации экрана в модальном окне iOS 5/6

Случилось мне недавно делать приложение для iOS, которое работает в портретной ориентации экрана и только одно вью (которое отображалось модально) нужно было отображать только в ландшафтной ориентации экрана. Что ж, решение для iOS 5.х довольно простое: в методе - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation модального окна просто прописываем нужный разворот устройства

-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
    return (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight);
}


Для iOS 6.x все оказалось немного сложнее:

1. В классе вью контроллера модального окна определяем обработчики методов

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
 return UIInterfaceOrientationLandscapeRight;
}

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscape;
}

- (BOOL)shouldAutorotate
{
    return YES;
}


2. Сразу после вызова показа модального окна устанавливаем положение статус бара:

 [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO];


3. Перед тем, как убрать модальное окно, возвращаем статус бар в нужное положение:

 [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:NO];


Как обычно пример на github-е.

16 декабря 2012 г.

UIDatePicker как клавиатура для UITextField

Цель: сделать удобной выбор даты в UITextField, будем использовать UIDatePicker
Алгоритм: перехватим событие - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField у UITextField и вместо показа системной клавиатуры, покажем наше вью.

Думаю не стоит описывать процесс создания и назначения делегата у UITextField, это можно сделать как в Interface Builder-е так и динамически. Сразу смотрим метод делегата -(void)textFieldDidBeginEditing:(UITextField *)textField:

- (void)textFieldDidBeginEditing:(UITextField *)aTextField
{
 [aTextField resignFirstResponder];
 
 [_pickerViewPopup release];
 _pickerViewPopup = [[UIActionSheet alloc] initWithTitle:nil delegate:nil cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil];
 
 [_pickerView release];
 _pickerView = [[UIDatePicker alloc] initWithFrame:CGRectMake(0.0, 44.0, 0.0, 0.0)];
 _pickerView.datePickerMode = UIDatePickerModeDate;
 _pickerView.hidden = NO;
 _pickerView.date = [NSDate date];
 [_pickerView addTarget:self action:@selector(dateChanged) forControlEvents:UIControlEventValueChanged];
 
 UIToolbar *pickerToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 44.0)];
 pickerToolbar.barStyle = UIBarStyleBlackOpaque;
 [pickerToolbar sizeToFit];
 
 NSMutableArray *barItems = [[NSMutableArray alloc] init];
 
 UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
 [barItems addObject:flexSpace];
 [flexSpace release];
 
 UIBarButtonItem *doneBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(doneButtonPressed:)];
 [barItems addObject:doneBtn];
 [doneBtn release];
 
 UIBarButtonItem *cancelBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelButtonPressed:)];
 [barItems addObject:cancelBtn];
 [cancelBtn release];
 
 [pickerToolbar setItems:barItems animated:YES];
 [barItems release];
 
 [_pickerViewPopup addSubview:pickerToolbar];
 [_pickerViewPopup addSubview:_pickerView];
 [_pickerViewPopup showInView:self.window];
 [_pickerViewPopup setBounds:CGRectMake(0.0, 0.0, 320.0, 464.0)];
 [pickerToolbar release];
}


тут мы создаем модальное окно, на основе UIActionSheet и в него вставляем UIDatePicker и UIToolbar с кнопками "Ok" и "Cancel".

И события нажатия на эти кнопки:

- (void)doneButtonPressed:(id)sender
{
 self.dt = [_pickerView date];
 
 [_pickerViewPopup dismissWithClickedButtonIndex:1 animated:YES];
 [_pickerView removeTarget:self action:@selector(dateChanged) forControlEvents:UIControlEventValueChanged];
}

- (void)cancelButtonPressed:(id)sender
{
 [_pickerViewPopup dismissWithClickedButtonIndex:1 animated:YES];
 [_pickerView removeTarget:self action:@selector(dateChanged) forControlEvents:UIControlEventValueChanged];
}


В обоих случаях скрываем наше "модельное" окно и отписываемся от события получения нотификации о изменении даты на UIDatePicker. А при нажатии на "Ok" так-же сохраняем выбранную дату на "барабане".

И событие изменения даты на UIDatePicker для изменения даты так-же в текстовом поле:

- (void)dateChanged
{
 NSDateFormatter *df = [[NSDateFormatter alloc] init];
 [df setDateFormat:@"dd.MM.yy"];
 _tf.text = [df stringFromDate:[_pickerView date]];
}


Проект на github-е. Утащено с stackoverflow.

9 декабря 2012 г.

Применение изображения-маски на UIView

После длительного перерыва решил опубликовать новый пост в блоге.

Традиционно все UIView в iOS имеют прямоугольную форму. И с помощью полупрозрачного цвета (так-же изображений с alpha каналом) и порядком (z-order) наложения вью друг на друга можно добиваться практически всего, что можно вообразить. Но вот понадобился мне полупрозрачный свой UISwitch в котором был предопределен порядок вьюшек.

И что б этот пост не был постом об одном методе, опишу процесс создания компоненты.

Есть изображения:

1. подложка компоненты

2. маска под эту подложку

3. включенное и выключенное состояния

Наследоваться будем от UIView.

В методе init будем создавать вью которые нам и понадобятся.

  _backgroundImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"butt_bg"]];
  [self addSubview:_backgroundImage];


создаем подложку компоненты и добавляем ее на родительское вью

  UIView *maskedView = [[UIView alloc] initWithFrame:self.bounds];
  maskedView.backgroundColor = [UIColor clearColor];
  [self addSubview:maskedView];


создаем вью, к которому будет примененна маска, на него будем ложить дальше компоненты для того, что б они не вылазили за пределы внутреннего "овала"

  _backgroundView = [[UIScrollView alloc] initWithFrame:self.bounds];
  _backgroundView.backgroundColor = [UIColor clearColor];
  _backgroundView.clipsToBounds = YES;
  _backgroundView.delegate = self;
  _backgroundView.clearsContextBeforeDrawing = NO;
  _backgroundView.pagingEnabled = YES;
  _backgroundView.showsVerticalScrollIndicator = NO;
  _backgroundView.showsHorizontalScrollIndicator = NO;
  _backgroundView.scrollsToTop = NO;
  _backgroundView.contentSize = CGSizeMake(105.0, _backgroundView.bounds.size.height);
  _backgroundView.bounces = NO;
  [maskedView addSubview:_backgroundView];
  
  _onLabel = [[UILabel alloc] initWithFrame:CGRectZero];
  _onLabel.backgroundColor = [UIColor clearColor];
  _onLabel.opaque = NO;
  _onLabel.textColor = [UIColor blackColor];
  _onLabel.font = [UIFont boldSystemFontOfSize:16.0];
  _onLabel.textAlignment = UITextAlignmentCenter;
  _onLabel.text = NSLocalizedString(@"ON", nil);
  _onLabel.frame = CGRectMake(0.0, 0.0, 43.0, _backgroundView.frame.size.height);
  [_backgroundView addSubview:_onLabel];

  UIImage *img = [UIImage imageNamed:@"r_butt_on"];
  _ballImage0 = [[UIImageView alloc] initWithImage:img];
  _ballImage0.frame = CGRectMake((_backgroundView.contentSize.width-img.size.width)/2.0,
           (_backgroundView.contentSize.height-img.size.height)/2.0,
           img.size.width, img.size.height);
  [_backgroundView addSubview:_ballImage0];
  
  img = [UIImage imageNamed:@"r_butt_off"];
  _ballImage1 = [[UIImageView alloc] initWithImage:img];
  _ballImage1.frame = _ballImage0.frame;
  _ballImage1.alpha = 0.0;
  [_backgroundView addSubview:_ballImage1];
  
  _offLabel = [[UILabel alloc] initWithFrame:CGRectZero];
  _offLabel.backgroundColor = [UIColor clearColor];
  _offLabel.opaque = NO;
  _offLabel.textColor = [UIColor blackColor];
  _offLabel.font = [UIFont boldSystemFontOfSize:16.0];
  _offLabel.textAlignment = UITextAlignmentCenter;
  _offLabel.text = NSLocalizedString(@"OFF", nil);
  _offLabel.frame = CGRectMake(60.0, 0.0, 43.0, _backgroundView.frame.size.height);
  [_backgroundView addSubview:_offLabel];


тут _backgroundView - скролл который будет анимированно двигаться, котда будем прикасаться к нему, на нем лежит две текстовые метки: _onLabel и _offLabel которые просто отображают текст и вьюшки с включенной и выключенной лампочками (_ballImage0 и _ballImage1).

А вот и подобрались к самому главному, создаем слой маски из изображения и применяем его к вью:

  UIImage *_maskingImage = [UIImage imageNamed:@"butt_mask"];
  CALayer *_maskingLayer = [CALayer layer];
  _maskingLayer.frame = CGRectMake(1.0, 1.0, self.bounds.size.width-2.0, self.bounds.size.height-2.0);
  [_maskingLayer setContents:(id)[_maskingImage CGImage]];
  maskedView.layer.mask = _maskingLayer;



Регистрируем слушателя на касание внутри нашей компоненты:

UITapGestureRecognizer *singleFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)]; [self addGestureRecognizer:singleFingerTap]; [singleFingerTap release];

и обработчик этого одиночного касания (до этого мы все делали в методе init):

- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer
{
 [self setOn:!_on animated:YES];
}


так-же методы установки/получения состояния компоненты:

- (void)setOn:(BOOL)on animated:(BOOL)animated
{
 _on = on;
 _scrollingAnimation = animated;
 [_backgroundView setContentOffset:CGPointMake(on?0.0:_backgroundView.contentSize.width-self.bounds.size.width, 0.0) animated:animated];
 _ballImage0.alpha = _on?1.0f:.0f;
 _ballImage1.alpha = _on?.0f:1.0f;
}

- (void)setOn:(BOOL)on
{
 [self setOn:on animated:NO];
}

- (BOOL)on
{
 return _on;
}



тут в зависимости от состояния меняем прозрачность наших изображений _ballImage0 и _ballImage1 ну и храним состояние в переменной _on

и обработчики перемещения UIScrollView пользователем:

- (void)scrollViewDidScroll:(UIScrollView *)sender
{
 CGFloat koof = sender.contentOffset.x/(sender.contentSize.width-sender.bounds.size.width);
 _ballImage0.alpha = 1.0f-koof;
 _ballImage1.alpha = koof;
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
 if (!_scrollingAnimation)
 {
  self.on = _backgroundView.contentOffset.x<1.0;
 }
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
 if (!decelerate && !_scrollingAnimation)
 {
  self.on = _backgroundView.contentOffset.x<1.0;
 }
}


Вот и все, проект можно посмотреть на github-е, в нем специально закомментирована строка _backgroundView.bounces = NO;, для более наглядной демонстрации эффекта.

ЗЫ: Вот еще одно интересное применение маски.

6 марта 2012 г.

Перехват двойного тапа у UIWebView

В классе, который будет обрабатывать двойной тап по UIWebView регистрируем обработчик двойного тапа, если это UIViewController то, например, в методе viewDidLoad:
- (void)viewDidLoad
{
    UITapGestureRecognizer *doubleFingerTap = [[UITapGestureRecognizer alloc]
                                                initWithTarget:self action:@selector(handleDoubleTapGesture:)];
    doubleFingerTap.numberOfTapsRequired = 2;
    doubleFingerTap.delegate = self;
    [self.webView addGestureRecognizer:doubleFingerTap];
    [doubleFingerTap release];
}

и
#pragma mark Gesture recognizer delegate

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer 
{
    return YES;
}

так-же и сам метод, который будет вызываться при двойном тапе:
- (void) handleDoubleTapGesture:(UITapGestureRecognizer *) gestureRecognizer
{
    if (gestureRecognizer.state == UIGestureRecognizerStateEnded)
 {
...
 }
}

Ну и на последок, в *.h-файл нашего класса, добавляем описание поддержки протокола
<UIGestureRecognizerDelegate>

1 октября 2011 г.

UIGestureRecognizer перехват одиночного касания и двойного на одном UIView

Для навешивания событий для одиночных и двойных касаний очень удобо использовать класс UIGestureRecognizer. Но если нужно перехватывать события которые пересекаются (например одиночное касание и двойное), то может возникнуть ситуация, что всегда будут выполнятся два перехваченных события (при двойном тапе будет всегда вызываться событие одиночного тапа).

В этом случае нужно запретить в UIGestureRecognizer одиночного тапа посылку события при двойном тапе. Делается просто методом - (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer.

Пример:
        UIGestureRecognizer *doubleTapGestureRecognizer = [[UIGestureRecognizer alloc]
                                initWithTarget:self action:@selector(handleDoubleTap:)];
        doubleTapGestureRecognizer.numberOfTapsRequired = 2;
        [self addGestureRecognizer:doubleTapGestureRecognizer];

        UIGestureRecognizer *singleTapGestureRecognizer = [[UIGestureRecognizer alloc]
                                                              initWithTarget:self action:@selector(handleSingleTap:)];
        singleTapGestureRecognizer.numberOfTapsRequired = 1;
        [singleTapGestureRecognizer requireGestureRecognizerToFail:doubleTapGestureRecognizer];
        [self addGestureRecognizer:singleTapGestureRecognizer];

8 сентября 2011 г.

Отключение ARC в xCode 4.2

Нужно всего лишь в качестве компилятора в свойствах проекта выбрать "LLVM GCC 4.2" вместо установленного по умолчанию "Apple LLVM compiler 3.0".



PS: По совету твиттер пользователя @iLeNsTR можно обойтись флагами компилятора:

17 июня 2011 г.

Запрет лока экрана при запущенном приложении в iOS

[UIApplication sharedApplication].idleTimerDisabled = YES;

Рисование под статус баром в iOS

Полезно иногда просматривать официальную документацию.

В моем приложении, так-же как и в стандартном Photos необходимо убирать статус бар при тапе на экран, но так, что б вью на экране не "прыгало". Думал уже реализовывать кастомным вью и непосредственно добавлять его на UIWindow, но нашел замечательное свойство wantsFullScreenLayout у UIViewController, которое все проблеммы решило.

9 мая 2011 г.

Размытый текст в UILabel

Я не привык пользоваться Interface Builder в xCode. Все компоненты создаю динамически и для меня это вполне удобно. И вот недавно столкнулся с проблемой отображения текста в компоненте UILabel, а поскольку это "корневая" компонента входящая в такие компоненты как UIButton и UITableViewCell, то проблема оказалась серьезной.

Итак, на скриншоте ниже отображены UILabel с одним шрифтом и одним и тем-же размером.



Вторая метка выглядит размытой. Вот код, которым я создавал эти UILabel.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    
 CGRect screenRect = [[UIScreen mainScreen] applicationFrame];
    
    UILabel *normal = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 30.0, screenRect.size.width, 50.0)];
    normal.text = @"Test label1";
    normal.textAlignment = UITextAlignmentCenter;
    normal.font = [UIFont systemFontOfSize:16.0];
    normal.backgroundColor = [UIColor clearColor];
    [self.window addSubview:normal];
    [normal release];
    
    
    UILabel *fuzzy = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 60.55, screenRect.size.width, 50.0)];
    fuzzy.text = @"Test label2";
    fuzzy.textAlignment = UITextAlignmentCenter;
    fuzzy.font = [UIFont systemFontOfSize:16.0];
    fuzzy.backgroundColor = [UIColor clearColor];
    [self.window addSubview:fuzzy];
    [fuzzy release];
    
    
    [self.window makeKeyAndVisible];
    return YES;
}
Решилось все просто, оказывается UILabel не дружит с нецелыми координатами (60.5 - в CGRectMake(0.0, 60.5, screenRect.size.width, 50.0))

23 апреля 2011 г.

Куки в iOS (UIWebView)

Для каждого приложения в iOS существует свое хранилище http-cookies. Класс, который отвечает за куки - NSHTTPCookieStorage. Класс имеет простой интерфейс доступа к кукам, например прочитать все куки можно так:
for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies])
{
NSLog(@"%@", cookie);
}
Так-же есть методы утановки куков (- setCookie:), и чтения для конкретного урла (– cookiesForURL:)

19 апреля 2011 г.

Скрытие статус бара в iOS

Статус бар - верхняя панелька в iOS со значком оператора, часами и зарядом батареи.

Программно можно скрыть статус бар так:
[[UIApplication sharedApplication] setStatusBarHidden:YES];
Можно так-же отредактировав Info.plist приложения, добавив ключ UIStatusBarHidden

8 апреля 2011 г.

31 декабря 2010 г.

Конвертирование "человеческого" адреса в широту и долготу

Что б отобразить что-то на карте (MKMapView) нужно знать координаты (широту и долготу) точки в которой мы хотим отобразить элемент. У меня возникла ситуация когда в качестве координаты у меня есть только почтовый адрес. Сервисов преобразования почтового адреса в широту и долготу довольно много, я использовал "Yahoo! Maps Web Services - Geocoding API"

Функция конвертации выглядит так:
- (void) convertAddress:(NSString *) address
{
NSError *err = nil;
NSString *url = [NSString stringWithFormat:@"http://api.maps.yahoo.com/ajax/geocode?appid=onestep&qt=1&id=m&qs=%@",
[self convertChars:address]];
NSString *str = [NSString stringWithContentsOfURL:[NSURL URLWithString:url]
encoding:NSUTF8StringEncoding
error:&err];
NSLog(@"%@", str);
CGFloat lat = [[self getAttr:@"Lat" fromString:str] floatValue];
CGFloat lon = [[self getAttr:@"Lon" fromString:str] floatValue];
if (fabs(lat)>.0001 || fabs(lon)>.0001)
{
MKCoordinateRegion region;
region.center.latitude = lat;
region.center.longitude = lon;
region.span.latitudeDelta = 0.1;
region.span.longitudeDelta = 0.1;
[mapView setRegion:region];
}
}


Для начала конвертируем символы адреса, так как адрес передаем в GET запросе:

- (NSString *) convertChars:(NSString *) str
{
NSMutableString *rv = [str mutableCopy];
[rv replaceOccurrencesOfString:@" "
withString:@"+"
options:0
range:NSMakeRange(0, [rv length])];
[rv replaceOccurrencesOfString:@","
withString:@""
options:0
range:NSMakeRange(0, [rv length])];
[rv replaceOccurrencesOfString:@"."
withString:@""
options:0
range:NSMakeRange(0, [rv length])];
return [rv autorelease];
}


Результатом запроса будет javascript строка, и из нее выделяем долготу и широту:

- (NSString *) getAttr:(NSString *) attr fromString:(NSString *) str
{
NSString *rv = nil;
NSRange rng = [str rangeOfString:[NSString stringWithFormat:@"\"%@\":", attr]];
if (rng.location != NSNotFound)
{
rng.location = rng.location +rng.length;
rng.length = 0;
unichar ch = [str characterAtIndex:rng.location+rng.length];
while ((rng.location+rng.length)<[str length] && (ch!=',' && ch!='}'))
{
rng.length++;
ch = [str characterAtIndex:rng.location+rng.length];
}
rv = [str substringWithRange:rng];
}
return rv;
}


В качестве примера обновил проект на github

Так же смотрите:
- MapKit в iOS

11 декабря 2010 г.

Шрифты в iOS

Точно помню, что где-то видел такой пример, где в таблице показываются все доступные шрифты в iOS. Искать было лень, а написать пример отображения всех шрифтов заняло думаю меньше времени, чем написание этого поста.

Пример можно скачать.

Работает все просто. У класса UIFont есть методы familyNames: и fontNamesForFamilyName:. Первый возвращает массив имен семейств шрифтов в системе, а второй массив имен шрифтов по семейству шрифта.

28 ноября 2010 г.

Кастомные заголовки и подписи в секциях UITableView

Иногда необходимо что б в заголовке секции таблицы UITableView отображался не только текст, но и картинка, или может даже кнопка. Компонента UITableView позволяет не только задавать текстовую строку как заголовок к каждой секции но и устанавливать свое UIView как элемент заголовка.

UITableView поддерживает отсылку событий по двум протоколам UITableViewDataSource и UITableViewDelegate. Первый отвечает за получение данных которые отображаются в компоненте, второй за их внешний вид. В протоколе UITableViewDataSource есть методы – tableView:titleForHeaderInSection: и – tableView:titleForFooterInSection: которые должны возвращать строки заголовка и подписи для каждой секции в таблице. Но в протоколе UITableViewDelegate можно реализовать методы – tableView:viewForHeaderInSection: и – tableView:heightForHeaderInSection: для установки вашего вью на заголовок секции (методы – tableView:viewForFooterInSection: и – tableView:heightForFooterInSection: для подписи секции)

Реализуем простой пример использования (скачать можно отсюда)

У нас есть UIViewController на котором находится UITableView. Создаем вью которое будем использовать как заголовок секции таблицы.

  1.   CGRect rct;
  2.   headerView = [[UIView alloc] initWithFrame:CGRectZero];
  3.   UIImage *img = [UIImage imageNamed:@"apple.png"];
  4.   UIImageView *tmpImgView = [[UIImageView alloc] initWithImage:img];
  5.   rct = CGRectMake(screenRect.size.width*.05, 0.0, img.size.width, img.size.height);
  6.   tmpImgView.frame = rct;
  7.   [headerView addSubview:tmpImgView];
  8.   rct.origin.x = 0.0;
  9.   rct.size = CGSizeMake(screenRect.size.width, rct.size.height);
  10.   headerView.frame = rct;
  11.   [tmpImgView release];


тут headerView - имеет тип UIView, его то мы и будем отображать в таблице.

А теперь опишем функции отображение этого вью как заголовка первой секции таблицы:

  1. - (UIView *) tableView:(UITableView *) tableView viewForHeaderInSection:(NSInteger) section
  2. {
  3.   if (section==0)
  4.   {
  5.     return headerView;
  6.   }
  7.   return nil;
  8. }
  9. - (CGFloat) tableView:(UITableView *) tableView heightForHeaderInSection:(NSInteger) section
  10. {
  11.   if (section==0)
  12.   {
  13.     return headerView.frame.size.height;
  14.   }
  15.   return 0.0;
  16. }


В первой функции мы возвращаем вью которое должно отображаться в таблице, во второй - высоту этого вью.

Так-же в компоненте UITableView можно использовать свойства tableHeaderView  и tableFooterView  для задания вью как заголовка и подписи всей таблицы.

20 ноября 2010 г.

Изменение шрифта в компоненте UISegmentedControl

Если вам не хватает стандартных стилей компоненты UISegmentedControl, то изменение шрифта надписей, это то место, которое не описано в iOS Reference Library. Код для изменения шрифта в компоненте UISegmentedControl взят с форума www.iphonedevsdk.com:

  1. void changeUISegmentFont(UIView* aView)
  2. {
  3.   NSString* typeName = [[aView class] className];
  4.   if ([typeName compare:@"UISegmentLabel" options:NSLiteralSearch] == NSOrderedSame)
  5.   {
  6.     UILabel* label = (UILabel*)aView;
  7.     [label setTextAlignment:UITextAlignmentCenter];
  8.     [label setFont:[UIFont boldSystemFontOfSize:14]];
  9.   }
  10.   NSArray* subs = [aView subviews];
  11.   NSEnumerator* iter = [subs objectEnumerator];
  12.   UIView* subView;
  13.   while (subView = [iter nextObject])
  14.   {
  15.     changeUISegmentFont(subView);
  16.   }
  17. }


параметром этой функции и есть экземпляр обьекта UISegmentedControl.

2 ноября 2010 г.

Ссылка на AppStore reviews приложения из приложения

В магазине приложений AppStore сделано так, что боольшинство оценок которыми оценивают ваше приложение появляется при удалении приложения с устройсва. А пользователь удаляет приложение если оно ему не понравилось, т.е. большинство оценок приложению достаются низкие.

Можно попробовать решить сделав переход из вашего приложения на страничку написания ревью к вашему приложению. Как это Вы реализуете: ссылкой в "О программе" или алертом при каждом запуске приложения с просьбой оценить приложение - не важно. Я здесь привожу лишь код сотавления урл-аддреса и открытие его:

  1. NSString *str = @"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa";
  2. str = [NSString stringWithFormat:@"%@/wa/viewContentsUserReviews?", str];
  3. str = [NSString stringWithFormat:@"%@type=Purple+Software&id=", str];
  4. // Здесь добавляем AppleID вашего приложения из iTunesConnect
  5. str = [NSString stringWithFormat:@"%@348529788", str];
  6. [[UIApplication sharedApplication] openURL:[NSURL URLWithString:str]];


В результате переходим в приложение AppStore на сраничку Write review приложения (не забудьте поменять id в коде)



Спасибо memention за инструкцию.

2 октября 2010 г.

Добавление изображений в Фотоальбом

Этот пост посвящен единственной функции из фраемворка UIKit, а именно UIImageWriteToSavedPhotosAlbum. В параметры функции передается изображение класса UIImage, а так-же функция-селектор которая вызовется после добавления изображения в Фотоальбом, либо при возникновении ощибки добавления.

Скачать пример.

23 сентября 2010 г.

"Привязываем" UISearchBar к UITableView

У меня возникла задача использовать фильтр к большому списку однотипных элементов (проще говоря поиск). Раньше я решал такую задачу кидая на UINavigationBar элемент UITextField и обрабатывал его события, но выглядело это очень некрасиво. UISearchBar хоть и занимает дополнительное место на экране, но выглядит приятно и вписывается в интерфейс, хотя при редактировании в UISearchBar можно скрывать UINavigationBar для освобождения пространства на экране.

Как я реализовывал.
Есть класс наследник от UIViewController в котором находятся визуальные компоненты (UISearchBar и UITableView). Так-же этот класс содержит два массива: массив со всеми элементами которые могут присутствовать в списке, второй с элементами которые удовлетворяют условиям поиска. Изначально массивы равны. Второй массив необходимо отображать в UITableView и применять к нему фильтр при изменении текста в UISearchBar. Просто?

Начнем с функции поиска, в моем случае я у меня строковые массивы (itemsList - со всеми элементами, searched - с элементами которые удовлетворяют условию из UISearchBar). Единственным параметр функции - строка из UISearchBar:

  1. - (void) search:(NSString *) matchString
  2. {
  3.   NSString *upString = [matchString uppercaseString];
  4.   if (searched)
  5.     [searched release];
  6.   searched = [[NSMutableArray alloc] init];
  7.   for (NSString *line in itemsList)
  8.   {
  9.     if ([matchString length] == 0)
  10.     {
  11.       [searched addObject:line];
  12.       continue;
  13.     }
  14.     NSRange range = [[line uppercaseString] rangeOfString:upString];
  15.     if (range.location != NSNotFound)
  16.       [searched addObject:line];
  17.   }
  18.   [tv reloadData]; // UITableView
  19. }


я тут не боролся за оптимальность поиска. Можно было б не пересоздавать при каждом вызове массив searched, а так-же учитывать предыдущий поиск.

Функция search: должна вызываться при каждом изменении в UISearchBar, для которого нужно определить делегат с методом searchBar:textDidChange:

  1. - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
  2. {
  3.   [self search:searchText];
  4. }


так-же для иницилизации массива searched метод search: нужно вызвать при загрузке вью конттроллера, например в методе loadView

  1. - (void)loadView
  2. {
  3.   [self search:@""];
  4. }


и осталось отобразить массив searched в UITableView

  1. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  2. {
  3.   UITableViewCell *rv = nil;
  4.   NSString *cellID = @"cell_ID";
  5.   rv = [tableView dequeueReusableCellWithIdentifier:cellID];
  6.   if (rv==nil)
  7.   {
  8.     rv = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:cellID] autorelease];
  9.   }
  10.   rv.textLabel.text = [searched objectAtIndex:indexPath.row];
  11.   return rv;
  12. }


В статье не описано, что нужно обрабатывать появление клавиатуры, а так-же можно скрывать UINavigationBar при фокусе на UISearchBar, что есть в примере.