Loading...
Searching...
No Matches
NCFloorSelectorView.mm
Go to the documentation of this file.
1//
2// NCFloorSelectorView.mm
3// Floor selector widget for navigation view
4//
5
8#import "NCWidgetStyles.h"
9
10@implementation LevelInfo
11
12+ (instancetype) itemWithLevelId:(NSString *)levelId sublocationId:(int32_t)sublocationId {
13 LevelInfo *item = [[LevelInfo alloc] init];
14 item.levelId = levelId;
15 item.sublocationId = sublocationId;
16 return item;
17}
18@end
19
20@implementation NCFloorSelectorView {
21 NCFloorSelectorCallback _onSublocationSelected;
23}
24
25- (instancetype)initWithOrigin:(CGPoint)origin onSublocationSelected:(NCFloorSelectorCallback)onSublocationSelected {
26 return [self initWithOrigin:origin onSublocationSelected:onSublocationSelected config:nil];
27}
28
29- (instancetype)initWithOrigin:(CGPoint)origin
30 onSublocationSelected:(NCFloorSelectorCallback)onSublocationSelected
31 config:(NCFloorSelectorViewConfig *)config {
32 self = [super init];
33 if (self) {
34 _defaultPosition = origin;
35 _onSublocationSelected = [onSublocationSelected copy];
36 _config = config;
37 [self setFloors: @[]];
38 [self commonInit];
39 }
40 return self;
41}
42
43- (void)setFloors:(NSArray<LevelInfo *> *)floors {
44 _floors = [floors copy] ?: @[];
45
46 // Reset selection if out of bounds
47 if (_selectedFloorIndex < 0 || _selectedFloorIndex >= _floors.count) {
48 _selectedFloorIndex = _floors.count > 0 ? 0 : NSNotFound;
49 }
50
51 // Update frame
52 [self updateFrame];
53
54 // Reload table and buttons
55 if (_tableView) {
56 [_tableView reloadData];
57 [self showButtonsIfNeeded];
58 }
59}
60
61- (void)commonInit {
62 self.backgroundColor = [UIColor clearColor];
63
64 self.translatesAutoresizingMaskIntoConstraints = NO;
65 self.layer.shadowOpacity = kNCShadowOpacity;
66 self.layer.shadowRadius = kNCShadowRadius;
67 self.layer.shadowOffset = kNCShadowOffset;
68 self.layer.shadowColor = kNCShadowColor().CGColor;
69
70 // Setup table view
71 _tableView = [[UITableView alloc] init];
72 _tableView.delegate = self;
73 _tableView.dataSource = self;
74 _tableView.backgroundColor = [UIColor clearColor];
75 _tableView.showsVerticalScrollIndicator = NO;
76 _tableView.separatorInset = UIEdgeInsetsZero;
77 _tableView.layer.cornerRadius = kNCBorderRadius;
78 _tableView.clipsToBounds = YES;
79 _tableView.bounces = NO;
80 _tableView.directionalLayoutMargins = NSDirectionalEdgeInsetsMake(0, 0, 0, 0);
81 [self addSubview:_tableView];
82
83 [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"FloorCell"];
84
85 // Setup buttons
86 [self setupButtons];
87
88 // Initial button visibility
89 [self showButtonsIfNeeded];
90}
91
92- (void)setupButtons {
93 // Top button
94 _topButton = [UIButton buttonWithType:UIButtonTypeSystem];
95 _topButton.frame = CGRectMake(0, 0, kNCStandardButtonWidth, kNCFloorRowHeight);
96 [_topButton setTitle:@"▲" forState:UIControlStateNormal];
97 [_topButton addTarget:self action:@selector(topButtonPressed) forControlEvents:UIControlEventTouchUpInside];
98 _topButton.backgroundColor = kNCButtonBackgroundColor();
99 _topButton.tintColor = kNCBaseBlackColor();
100 _topButton.layer.cornerRadius = kNCBorderRadius;
101 _topButton.layer.maskedCorners = kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner;
102 _topButton.hidden = YES;
103 [self addSubview:_topButton];
104
105 // Bottom button
106 _bottomButton = [UIButton buttonWithType:UIButtonTypeSystem];
107 _bottomButton.frame = CGRectMake(0, self.bounds.size.height - kNCFloorRowHeight, kNCStandardButtonWidth, kNCFloorRowHeight);
108 [_bottomButton setTitle:@"▼" forState:UIControlStateNormal];
109 [_bottomButton addTarget:self action:@selector(bottomButtonPressed) forControlEvents:UIControlEventTouchUpInside];
110 _bottomButton.backgroundColor = kNCButtonBackgroundColor();
111 _bottomButton.tintColor = kNCBaseBlackColor();
112 _bottomButton.layer.cornerRadius = kNCBorderRadius;
113 _bottomButton.layer.maskedCorners = kCALayerMinXMaxYCorner | kCALayerMaxXMaxYCorner;
114 _bottomButton.hidden = YES;
115 [self addSubview:_bottomButton];
116}
117
118- (void)updateFrame {
119 CGFloat height = 0;
120 if (_floors.count == 0) {
121 height = 0;
122 } else if (_floors.count == 1) {
123 _tableView.hidden = YES;
124 _topButton.hidden = YES;
125 _bottomButton.hidden = YES;
126 height = 0;
127 return;
128 } else if (_floors.count >= kNCMaxVisibleFloors) {
129 height = kNCFloorSelectorMaxHeight - 1;
130 _tableView.hidden = NO;
131 _tableView.scrollEnabled = YES;
132 } else {
133 height = _floors.count * kNCFloorRowHeight - 1;
134 _tableView.hidden = NO;
135 _tableView.scrollEnabled = NO;
136 }
137
138 CGFloat x = self.defaultPosition.x;
139 CGFloat y = self.defaultPosition.y;
140 if (_config && !UIEdgeInsetsEqualToEdgeInsets(_config.insets, UIEdgeInsetsZero)) {
141 x += _config.insets.left;
142 y += _config.insets.top;
143 }
144 self.frame = CGRectMake(x, y, kNCStandardButtonWidth, height);
145 _tableView.frame = self.bounds;
146 [self updateButtonsPosition];
147}
148
149- (void)updateButtonsPosition {
150 _topButton.frame = CGRectMake(0, 0, kNCStandardButtonWidth, kNCFloorRowHeight);
151 _bottomButton.frame = CGRectMake(0, self.bounds.size.height - kNCFloorRowHeight, kNCStandardButtonWidth, kNCFloorRowHeight);
152}
153
154#pragma mark - Button Actions
155
156- (void)topButtonPressed {
157 NSArray<NSIndexPath *> *visiblePaths = [_tableView indexPathsForVisibleRows];
158 if (visiblePaths.count == 0) return;
159
160 NSIndexPath *topPath = visiblePaths.firstObject;
161 NSInteger scrollToRow = MAX(0, topPath.row - 4);
162 NSIndexPath *indexPath = [NSIndexPath indexPathForRow:scrollToRow inSection:0];
163 [_tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
164}
165
166- (void)bottomButtonPressed {
167 NSArray<NSIndexPath *> *visiblePaths = [_tableView indexPathsForVisibleRows];
168 if (visiblePaths.count == 0) return;
169
170 NSIndexPath *bottomPath = visiblePaths.lastObject;
171 NSInteger scrollToRow = MIN(_floors.count - 1, bottomPath.row + 4);
172 NSIndexPath *indexPath = [NSIndexPath indexPathForRow:scrollToRow inSection:0];
173 [_tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
174}
175
176#pragma mark - Button Visibility Logic
177
178- (void)showButtonsIfNeeded {
179 if (_floors.count <= kNCMaxVisibleFloors || _floors.count <= 1) {
180 _topButton.hidden = YES;
181 _bottomButton.hidden = YES;
182 return;
183 }
184
185 [self updateButtonsPosition];
186
187 CGFloat contentOffsetY = _tableView.contentOffset.y;
188 CGFloat contentHeight = _tableView.contentSize.height;
189 CGFloat viewHeight = _tableView.bounds.size.height;
190
191 // Show top button if scrolled down
192 _topButton.hidden = (contentOffsetY <= 0);
193
194 // Show bottom button if not at the bottom
195 CGFloat distanceFromBottom = contentHeight - contentOffsetY - viewHeight;
196 _bottomButton.hidden = (distanceFromBottom <= 0);
197}
198
199#pragma mark - UITableViewDataSource & UITableViewDelegate
200
201- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
202 return _floors.count;
203}
204
205- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
206 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"FloorCell" forIndexPath:indexPath];
207 cell.selectionStyle = UITableViewCellSelectionStyleNone;
208 cell.textLabel.textAlignment = NSTextAlignmentCenter;
209
210 LevelInfo *levelInfo = _floors[indexPath.row];
211 cell.textLabel.text = [self displayStringForLevelId:levelInfo.levelId];
212
213 BOOL isSelected = (indexPath.row == _selectedFloorIndex);
215 UIColor *accentColor = c.accentColor ?: kNCBaseBlueColor();
216 UIColor *textColor = c.textColor ?: kNCBaseBlackColor();
217 cell.backgroundColor = isSelected ? accentColor : [UIColor whiteColor];
218 cell.textLabel.textColor = isSelected ? [UIColor whiteColor] : textColor;
219 cell.textLabel.font = [UIFont systemFontOfSize:kNCFloorSelectorFontSize];
220
221 return cell;
222}
223
224- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
225 if (_selectedFloorIndex == indexPath.row) return;
226
227 _selectedFloorIndex = indexPath.row;
228 [tableView reloadData];
229 LevelInfo *levelInfo = _floors[indexPath.row];
230
231 if (_onSublocationSelected) {
232 _onSublocationSelected(levelInfo.sublocationId);
233 }
234}
235
236- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
237 return kNCFloorRowHeight;
238}
239
240- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
241 [self showButtonsIfNeeded];
242}
243
244#pragma mark - Helpers
245
246- (NSString *)displayStringForLevelId:(NSString *)levelId {
247 if (levelId.length < 5) {
248 return levelId;
249 }
250 return [[levelId substringToIndex:5] stringByAppendingString:@"..."];
251}
252
253#pragma mark - Public Methods
254
255- (void)applyConfig:(NCFloorSelectorViewConfig *)config {
256 _config = config;
257 if (_tableView) {
258 [_tableView reloadData];
259 }
260}
261
262- (void)setSublocationId:(int32_t)sublocationId {
263 NSInteger newSelectedIndex = NSNotFound;
264
265 for (NSInteger i = 0; i < _floors.count; i++) {
266 LevelInfo *info = _floors[i];
267 if (info.sublocationId == sublocationId) {
268 newSelectedIndex = i;
269 break;
270 }
271 }
272
273 if (newSelectedIndex == NSNotFound) {
274 return;
275 }
276
277 if (_selectedFloorIndex == newSelectedIndex && _tableView) {
278 [_tableView reloadData];
279 [self showButtonsIfNeeded];
280 return;
281 }
282
283 _selectedFloorIndex = newSelectedIndex;
284
285 if (_tableView) {
286 [_tableView reloadData];
287
288 if (_floors.count > 0) {
289 NSIndexPath *indexPath = [NSIndexPath indexPathForRow:newSelectedIndex inSection:0];
290
291 if (_floors.count > kNCMaxVisibleFloors) {
292 [_tableView scrollToRowAtIndexPath:indexPath
293 atScrollPosition:UITableViewScrollPositionMiddle
294 animated:YES];
295 } else {
296 [_tableView scrollToRowAtIndexPath:indexPath
297 atScrollPosition:UITableViewScrollPositionNone
298 animated:NO];
299 }
300 }
301
302 [self showButtonsIfNeeded];
303 }
304}
305
306@end