Files
BVGNearby/BVGNearby/JourneyViewController.m
T

553 lines
24 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#import "JourneyViewController.h"
#import "JourneyDetailViewController.h"
#import "SearchViewController.h"
#import "GuidanceManager.h"
#import "GuidanceViewController.h"
#import "BVGAPI.h"
#import "BVGSettings.h"
#import "ChipBar.h"
#import <CoreLocation/CoreLocation.h>
#pragma mark - Zelle fuer eine Verbindung
@interface JourneyCell : CardCell
@property (nonatomic, strong) UILabel *timeLabel;
@property (nonatomic, strong) UILabel *durationLabel;
@property (nonatomic, strong) UIImageView *linesView;
@property (nonatomic, strong) UILabel *infoLabel;
@end
@implementation JourneyCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
if (self) {
_timeLabel = [[UILabel alloc] init];
_timeLabel.font = [BVGTheme digits:17 weight:UIFontWeightSemibold];
_timeLabel.textColor = [BVGTheme textPrimary];
_timeLabel.adjustsFontSizeToFitWidth = YES;
_timeLabel.minimumScaleFactor = 0.8;
[self.contentView addSubview:_timeLabel];
_durationLabel = [[UILabel alloc] init];
_durationLabel.font = [BVGTheme digits:14 weight:UIFontWeightMedium];
_durationLabel.textColor = [BVGTheme textSecondary];
_durationLabel.textAlignment = NSTextAlignmentRight;
[self.contentView addSubview:_durationLabel];
_linesView = [[UIImageView alloc] init];
_linesView.contentMode = UIViewContentModeLeft;
[self.contentView addSubview:_linesView];
_infoLabel = [[UILabel alloc] init];
_infoLabel.font = [BVGTheme font:12 weight:UIFontWeightRegular];
_infoLabel.textColor = [BVGTheme textSecondary];
[self.contentView addSubview:_infoLabel];
self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat w = self.contentView.bounds.size.width;
self.timeLabel.frame = CGRectMake(14, 9, w - 110, 21);
self.durationLabel.frame = CGRectMake(w - 92, 10, 84, 19);
self.linesView.frame = CGRectMake(14, 34, w - 22, 22);
self.infoLabel.frame = CGRectMake(14, 60, w - 22, 15);
}
@end
#pragma mark - Routenplaner
typedef NS_ENUM(NSInteger, JourneySection) {
JourneySectionDate, // Datumsauswahl (nur bei "Abfahrt um" / "Ankunft um")
JourneySectionSearch, // Suchen-Knopf + Statuszeile
JourneySectionQuick, // Schnellziele (wenn noch keine Ergebnisse)
JourneySectionResult, // je eine Karte pro Verbindung
};
@interface JourneyViewController () <CLLocationManagerDelegate>
@property (nonatomic, strong) NSDictionary *from; // nil = Mein Standort, sonst {id, name} oder {name, latitude, longitude}
@property (nonatomic, strong) NSDictionary *to;
@property (nonatomic, strong) ChipBar *timeControl; // Jetzt / Abfahrt um / Ankunft um
@property (nonatomic, strong) UIDatePicker *datePicker;
@property (nonatomic, strong) NSArray *journeys;
@property (nonatomic, strong) CLLocationManager *locationManager;
@property (nonatomic, assign) BOOL searching;
@property (nonatomic, copy) NSString *errorText;
@end
@implementation JourneyViewController
- (instancetype)initWithStyle:(UITableViewStyle)style {
return [super initWithStyle:UITableViewStyleGrouped];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.largeTitle = @"Verbindung";
self.journeys = @[];
__weak JourneyViewController *weakSelf = self;
self.timeControl = [[ChipBar alloc] initWithItems:@[@"Jetzt", @"Abfahrt um", @"Ankunft um"]];
self.timeControl.onChange = ^(NSInteger idx) { [weakSelf timeModeChanged]; };
self.datePicker = [[UIDatePicker alloc] init];
self.datePicker.datePickerMode = UIDatePickerModeDateAndTime;
self.datePicker.locale = [NSLocale localeWithLocaleIdentifier:@"de_DE"];
self.datePicker.minuteInterval = 1;
self.datePicker.date = [NSDate dateWithTimeIntervalSinceNow:15 * 60];
[self buildHeader];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:@selector(updateNaviButton) name:GuidanceChangedNotification object:nil];
[nc addObserver:self selector:@selector(settingsChanged) name:BVGSettingsChangedNotification object:nil];
[self updateNaviButton];
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)settingsChanged {
[self.tableView reloadData];
}
#pragma mark - Kopfbereich: Von / Nach + Zeit
- (UIButton *)endpointButton:(BOOL)isFrom frame:(CGRect)frame {
UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom];
b.frame = frame;
b.autoresizingMask = UIViewAutoresizingFlexibleWidth;
b.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
b.contentEdgeInsets = UIEdgeInsetsMake(0, 12, 0, 8);
[b setBackgroundImage:[BVGTheme imageWithColor:[BVGTheme cardPressed]] forState:UIControlStateHighlighted];
b.titleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
NSString *label = isFrom ? @"Von " : @"Nach ";
NSString *value;
BOOL placeholder = NO;
if (isFrom) value = self.from ? [BVGAPI shortName:self.from[@"name"]] : @"Mein Standort";
else if (self.to) value = [BVGAPI shortName:self.to[@"name"]];
else { value = @"Ziel eingeben"; placeholder = YES; }
NSMutableAttributedString *t = [[NSMutableAttributedString alloc] initWithString:label attributes:@{
NSFontAttributeName: [BVGTheme font:13 weight:UIFontWeightRegular],
NSForegroundColorAttributeName: [BVGTheme textSecondary] }];
[t appendAttributedString:[[NSAttributedString alloc] initWithString:value ?: @"" attributes:@{
NSFontAttributeName: [BVGTheme font:15 weight:(placeholder ? UIFontWeightRegular : UIFontWeightSemibold)],
NSForegroundColorAttributeName: placeholder ? [BVGTheme textTertiary] : [BVGTheme textPrimary] }]];
[b setAttributedTitle:t forState:UIControlStateNormal];
b.accessibilityLabel = [NSString stringWithFormat:@"%@ %@", isFrom ? @"Start" : @"Ziel", value];
b.tag = isFrom ? 0 : 1;
[b addTarget:self action:@selector(endpointTapped:) forControlEvents:UIControlEventTouchUpInside];
return b;
}
- (void)buildHeader {
CGFloat w = self.view.bounds.size.width;
CGFloat inset = [BVGTheme cardInset];
CGFloat cardW = w - 2 * inset;
CGFloat rowH = 42;
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, 0, w, 2 * rowH + 46)];
// weisse Karte mit zwei Zeilen und Tausch-Knopf rechts
UIView *card = [[UIView alloc] initWithFrame:CGRectMake(inset, 0, cardW, 2 * rowH)];
card.autoresizingMask = UIViewAutoresizingFlexibleWidth;
card.backgroundColor = [UIColor whiteColor];
card.layer.cornerRadius = [BVGTheme cardRadius];
card.clipsToBounds = YES;
[v addSubview:card];
[card addSubview:[self endpointButton:YES frame:CGRectMake(0, 0, cardW - 46, rowH)]];
[card addSubview:[self endpointButton:NO frame:CGRectMake(0, rowH, cardW - 46, rowH)]];
UIView *line = [[UIView alloc] initWithFrame:CGRectMake(12, rowH, cardW - 58, 1.0 / [UIScreen mainScreen].scale)];
line.backgroundColor = [BVGTheme separator];
line.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[card addSubview:line];
UIButton *swap = [UIButton buttonWithType:UIButtonTypeCustom];
swap.frame = CGRectMake(cardW - 42, rowH - 16, 32, 32);
swap.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
[swap setBackgroundImage:[BVGTheme roundedImageWithColor:[BVGTheme background] radius:16] forState:UIControlStateNormal];
[swap setBackgroundImage:[BVGTheme roundedImageWithColor:[BVGTheme cardPressed] radius:16] forState:UIControlStateHighlighted];
swap.titleLabel.font = [BVGTheme font:16 weight:UIFontWeightSemibold];
[swap setTitle:@"⇅" forState:UIControlStateNormal];
[swap setTitleColor:[BVGTheme textPrimary] forState:UIControlStateNormal];
swap.accessibilityLabel = @"Start und Ziel tauschen";
[swap addTarget:self action:@selector(swap) forControlEvents:UIControlEventTouchUpInside];
[card addSubview:swap];
// Zeit-Chips darunter
[self.timeControl removeFromSuperview];
self.timeControl.frame = CGRectMake(0, 2 * rowH + 2, w, 44);
self.timeControl.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[v addSubview:self.timeControl];
self.headerAccessory = v;
}
- (void)endpointTapped:(UIButton *)b {
[self chooseEndpoint:(b.tag == 0)];
}
- (void)updateNaviButton {
self.navigationItem.rightBarButtonItem = [GuidanceManager shared].active
? [[UIBarButtonItem alloc] initWithTitle:@"Navigation ›" style:UIBarButtonItemStyleDone target:self action:@selector(openGuidance)]
: nil;
}
- (void)openGuidance {
[self.navigationController pushViewController:[[GuidanceViewController alloc] initWithStyle:UITableViewStyleGrouped]
animated:YES];
}
- (void)timeModeChanged {
if (self.timeControl.selectedIndex > 0 && [self.datePicker.date timeIntervalSinceNow] < -3600) {
self.datePicker.date = [NSDate dateWithTimeIntervalSinceNow:15 * 60];
}
[self.tableView reloadData];
}
- (void)swap {
if (!self.to) return;
NSDictionary *oldFrom = self.from;
self.from = self.to;
self.to = oldFrom; // nil (Mein Standort) als Ziel ist nicht erlaubt
if (!self.to) {
self.to = self.from;
self.from = nil;
}
self.journeys = @[];
self.errorText = nil;
[self buildHeader];
[self.tableView reloadData];
}
#pragma mark - Auswahl von Start/Ziel: direkt die Suche mit Schnellauswahl
- (void)chooseEndpoint:(BOOL)isFrom {
__weak JourneyViewController *weakSelf = self;
SearchViewController *search = [[SearchViewController alloc] initWithStyle:UITableViewStylePlain];
search.allowAddresses = YES;
search.offerSavedPlaces = YES;
search.offerCurrentLocation = isFrom;
search.pickTitle = isFrom ? @"Start" : @"Ziel";
search.pickHandler = ^(NSDictionary *place) {
if (!isFrom && !place) return; // "Mein Standort" nur als Start
[weakSelf setEndpoint:place isFrom:isFrom];
};
[self.navigationController pushViewController:search animated:YES];
}
- (void)setEndpoint:(NSDictionary *)place isFrom:(BOOL)isFrom {
if (isFrom) self.from = place; else self.to = place;
self.journeys = @[];
self.errorText = nil;
[self buildHeader];
[self.tableView reloadData];
if (self.to) [self startSearch]; // Ziel steht -> gleich suchen (wie in der BVG-App)
}
#pragma mark - Schnellziele
- (NSString *)quickKey:(NSInteger)idx {
return idx == 0 ? @"home" : @"work";
}
- (void)quickPlaceTapped:(NSInteger)idx {
NSDictionary *place = [BVGSettings placeForKey:[self quickKey:idx]];
if (!place) { [self editQuickPlace:idx]; return; }
self.from = nil; // ab meinem Standort
self.timeControl.selectedIndex = 0;
[self setEndpoint:place isFrom:NO];
}
- (void)editQuickPlace:(NSInteger)idx {
NSString *key = [self quickKey:idx];
__weak JourneyViewController *weakSelf = self;
SearchViewController *search = [[SearchViewController alloc] initWithStyle:UITableViewStylePlain];
search.allowAddresses = YES;
search.pickTitle = idx == 0 ? @"Zuhause festlegen" : @"Arbeit festlegen";
search.pickHandler = ^(NSDictionary *place) {
if (!place) return;
[BVGSettings setPlace:place forKey:key];
[weakSelf.tableView reloadData];
};
[self.navigationController pushViewController:search animated:YES];
}
- (void)quickLongPress:(UILongPressGestureRecognizer *)g {
if (g.state != UIGestureRecognizerStateBegan) return;
NSIndexPath *ip = [self.tableView indexPathForRowAtPoint:[g locationInView:self.tableView]];
if (ip && [self kindOfSection:ip.section] == JourneySectionQuick) [self editQuickPlace:ip.row];
}
#pragma mark - Suche
+ (NSString *)param:(NSString *)prefix place:(NSDictionary *)place {
if (place[@"id"]) return [NSString stringWithFormat:@"%@=%@", prefix, [BVGAPI encode:place[@"id"]]];
return [NSString stringWithFormat:@"%@.latitude=%f&%@.longitude=%f&%@.address=%@",
prefix, [place[@"latitude"] doubleValue], prefix, [place[@"longitude"] doubleValue],
prefix, [BVGAPI encode:place[@"name"] ?: @"Ort"]];
}
- (void)startSearch {
if (!self.to) { [self chooseEndpoint:NO]; return; }
if (self.searching) return;
self.searching = YES;
self.errorText = nil;
self.journeys = @[];
[self.tableView reloadData];
if (self.from) {
[self queryWithFromParams:[JourneyViewController param:@"from" place:self.from]];
return;
}
CLAuthorizationStatus st = [CLLocationManager authorizationStatus];
if (st == kCLAuthorizationStatusNotDetermined) {
[self.locationManager requestWhenInUseAuthorization];
}
[self.locationManager requestLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
if (!self.searching) return;
CLLocationCoordinate2D c = locations.lastObject.coordinate;
[self queryWithFromParams:[NSString stringWithFormat:@"from.latitude=%.6f&from.longitude=%.6f&from.address=%@",
c.latitude, c.longitude, [BVGAPI encode:@"Mein Standort"]]];
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
if (!self.searching) return;
self.searching = NO;
self.errorText = @"Standort nicht verfügbar. Tippe auf „Von“ und wähle eine Haltestelle.";
[self.tableView reloadData];
}
- (void)queryWithFromParams:(NSString *)fromParams {
// stopovers=true: noetig fuer die Aussteige-Erinnerung in der Navigation
NSMutableString *path = [NSMutableString stringWithFormat:@"/journeys?%@&%@&results=5&stopovers=true&remarks=true&transferTime=%ld",
fromParams, [JourneyViewController param:@"to" place:self.to], (long)[BVGSettings transferMinutes]];
NSInteger mode = self.timeControl.selectedIndex;
if (mode > 0) {
[path appendFormat:@"&%@=%@", mode == 1 ? @"departure" : @"arrival",
[BVGAPI encode:[BVGAPI isoFromDate:self.datePicker.date]]];
}
[BVGAPI getJSON:path completion:^(id json, NSError *error) {
NSArray *list = nil;
if ([json isKindOfClass:[NSDictionary class]] && [json[@"journeys"] isKindOfClass:[NSArray class]]) list = json[@"journeys"];
dispatch_async(dispatch_get_main_queue(), ^{
self.searching = NO;
if (!list) {
NSLog(@"Journey-Fehler: %@ %@", error, json);
self.errorText = @"Keine Verbindung gefunden oder Server nicht erreichbar.";
}
// Sicherheitsnetz: nur Verbindungen mit mind. X min pro Umstieg zeigen
NSMutableArray *ok = [NSMutableArray array];
for (NSDictionary *j in list ?: @[]) {
id legs = NN(j[@"legs"]);
if ([legs isKindOfClass:[NSArray class]] && [GuidanceManager minTransferSlack:legs] >= [BVGSettings transferMinutes] * 60) [ok addObject:j];
}
if (list.count && !ok.count) self.errorText = [NSString stringWithFormat:@"Keine Verbindung mit mind. %ld min Umstiegszeit gefunden.", (long)[BVGSettings transferMinutes]];
self.journeys = ok;
[self.tableView reloadData];
});
}];
}
#pragma mark - Auswertung einer Verbindung
- (void)configureCell:(JourneyCell *)cell journey:(NSDictionary *)j {
NSArray *legs = NN(j[@"legs"]);
if (![legs isKindOfClass:[NSArray class]] || !legs.count) return;
NSDate *dep = [GuidanceManager legDeparture:legs.firstObject];
NSDate *arr = [GuidanceManager legArrival:legs.lastObject];
NSInteger mins = (dep && arr) ? (NSInteger)round([arr timeIntervalSinceDate:dep] / 60.0) : 0;
NSMutableArray *lines = [NSMutableArray array];
BOOL cancelled = NO;
NSInteger firstDelay = -1;
NSString *firstStop = nil;
NSDate *firstTransitDep = nil;
for (NSDictionary *leg in legs) {
if ([NN(leg[@"cancelled"]) boolValue]) cancelled = YES;
if ([NN(leg[@"walking"]) boolValue] || !NN(leg[@"line"])) continue;
[lines addObject:leg[@"line"]];
if (firstDelay < 0) {
firstDelay = [NN(leg[@"departureDelay"]) integerValue] / 60;
firstStop = [BVGAPI shortName:NN(NN(leg[@"origin"])[@"name"])];
firstTransitDep = [GuidanceManager legDeparture:leg];
}
}
cell.timeLabel.text = [NSString stringWithFormat:@"%@ – %@", [BVGAPI hhmm:dep], [BVGAPI hhmm:arr]];
cell.durationLabel.text = mins >= 60 ? [NSString stringWithFormat:@"%ld:%02ld h", (long)(mins / 60), (long)(mins % 60)]
: [NSString stringWithFormat:@"%ld min", (long)mins];
cell.linesView.image = [BVGAPI badgeStripForLines:lines];
NSMutableArray *info = [NSMutableArray array];
NSInteger transfers = MAX((NSInteger)lines.count - 1, 0);
if (transfers == 0) [info addObject:@"direkt"];
else [info addObject:[NSString stringWithFormat:@"%ld× umsteigen", (long)transfers]];
if (firstStop && firstTransitDep) {
NSInteger leave = (NSInteger)floor([firstTransitDep timeIntervalSinceNow] / 60.0);
[info addObject:leave > 0 ? [NSString stringWithFormat:@"in %ld min ab %@", (long)leave, firstStop]
: [NSString stringWithFormat:@"ab %@", firstStop]];
}
UIColor *color = [BVGTheme textSecondary];
if (firstDelay > 0) { [info insertObject:[NSString stringWithFormat:@"+%ld min", (long)firstDelay] atIndex:0]; color = [BVGTheme red]; }
if (cancelled) { [info insertObject:@"⚠︎ Ausfall" atIndex:0]; color = [BVGTheme red]; }
cell.infoLabel.text = [info componentsJoinedByString:@" · "];
cell.infoLabel.textColor = color;
cell.accessibilityLabel = [NSString stringWithFormat:@"%@ bis %@, %ld Minuten, %@",
[BVGAPI hhmm:dep], [BVGAPI hhmm:arr], (long)mins, cell.infoLabel.text];
}
#pragma mark - Table
- (NSArray *)sectionKinds {
NSMutableArray *k = [NSMutableArray array];
if (self.timeControl.selectedIndex > 0) [k addObject:@(JourneySectionDate)];
[k addObject:@(JourneySectionSearch)];
if (!self.journeys.count && !self.searching) [k addObject:@(JourneySectionQuick)];
for (NSUInteger i = 0; i < self.journeys.count; i++) [k addObject:@(JourneySectionResult)];
return k;
}
- (JourneySection)kindOfSection:(NSInteger)section {
NSArray *k = [self sectionKinds];
return section < (NSInteger)k.count ? [k[section] integerValue] : JourneySectionResult;
}
- (NSDictionary *)journeyForSection:(NSInteger)section {
NSInteger first = [[self sectionKinds] indexOfObject:@(JourneySectionResult)];
NSInteger i = section - first;
return (first != NSNotFound && i >= 0 && i < (NSInteger)self.journeys.count) ? self.journeys[i] : nil;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [self sectionKinds].count;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
JourneySection kind = [self kindOfSection:section];
if (kind == JourneySectionQuick) return @"Schnellziele";
if (kind == JourneySectionResult && section > 0 && [self kindOfSection:section - 1] != JourneySectionResult) {
return @"Verbindungen";
}
return nil;
}
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
JourneySection kind = [self kindOfSection:section];
if (kind == JourneySectionSearch) {
if (self.searching) return @"Suche Verbindungen …";
if (self.errorText) return self.errorText;
return [NSString stringWithFormat:@"Umstiege immer mit mind. %ld min Puffer.", (long)[BVGSettings transferMinutes]];
}
if (kind == JourneySectionQuick) return @"Gedrückt halten zum Ändern.";
return nil;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self kindOfSection:section] == JourneySectionQuick ? 2 : 1;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
switch ([self kindOfSection:indexPath.section]) {
case JourneySectionDate: return 180;
case JourneySectionSearch: return 44;
case JourneySectionQuick: return 50;
case JourneySectionResult: return 84;
}
return 52;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
JourneySection kind = [self kindOfSection:indexPath.section];
if (kind == JourneySectionResult) {
JourneyCell *cell = [tableView dequeueReusableCellWithIdentifier:@"journey"];
if (!cell) cell = [[JourneyCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"journey"];
[self configureCell:cell journey:[self journeyForSection:indexPath.section]];
return cell;
}
CardCell *cell = [[CardCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
if (kind == JourneySectionDate) {
cell.selectionStyle = UITableViewCellSelectionStyleNone;
[self.datePicker removeFromSuperview];
self.datePicker.frame = CGRectMake(0, 0, tableView.bounds.size.width - 2 * [BVGTheme cardInset], 180);
self.datePicker.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[cell.contentView addSubview:self.datePicker];
} else if (kind == JourneySectionSearch) {
// dunkler Hauptknopf
cell.textLabel.text = self.searching ? @"Suche …" : @"Verbindungen suchen";
cell.textLabel.textAlignment = NSTextAlignmentCenter;
cell.textLabel.font = [BVGTheme font:16 weight:UIFontWeightSemibold];
cell.textLabel.textColor = [UIColor whiteColor];
cell.cardColor = [BVGTheme textPrimary];
cell.accessibilityTraits = UIAccessibilityTraitButton;
} else {
NSArray *defs = @[@[@"home", @"⌂", @"Nach Hause"], @[@"work", @"✱", @"Zur Arbeit"]];
NSArray *d = defs[indexPath.row];
NSDictionary *place = [BVGSettings placeForKey:d[0]];
cell.imageView.image = [JourneyViewController roundIcon:d[1]];
cell.textLabel.text = d[2];
cell.textLabel.font = [BVGTheme font:15 weight:UIFontWeightSemibold];
cell.detailTextLabel.font = [BVGTheme font:12 weight:UIFontWeightRegular];
cell.detailTextLabel.text = place ? place[@"name"] : @"Antippen zum Festlegen";
cell.detailTextLabel.textColor = place ? [BVGTheme textSecondary] : [BVGTheme link];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
if (!cell.gestureRecognizers.count) {
[cell addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(quickLongPress:)]];
}
}
return cell;
}
+ (UIImage *)roundIcon:(NSString *)text {
CGSize size = CGSizeMake(28, 28);
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
[[BVGTheme accentSoft] setFill];
[[UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 28, 28)] fill];
NSDictionary *attrs = @{ NSFontAttributeName: [UIFont systemFontOfSize:14 weight:UIFontWeightSemibold],
NSForegroundColorAttributeName: [BVGTheme textPrimary] };
CGSize t = [text sizeWithAttributes:attrs];
[text drawAtPoint:CGPointMake((28 - t.width) / 2, (28 - t.height) / 2) withAttributes:attrs];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
switch ([self kindOfSection:indexPath.section]) {
case JourneySectionSearch: [self startSearch]; break;
case JourneySectionQuick: [self quickPlaceTapped:indexPath.row]; break;
case JourneySectionResult: {
NSDictionary *j = [self journeyForSection:indexPath.section];
if (!j) break;
JourneyDetailViewController *vc = [[JourneyDetailViewController alloc] initWithJourney:j];
[self.navigationController pushViewController:vc animated:YES];
break;
}
default: break;
}
}
@end