#1208 (iOS 13 support)

- Replaced the non-functional multi-select segmented control in notification popover with four buttons, that are drawn similar to an iOS 13 segmented control.
This commit is contained in:
David Sinclair 2019-09-23 14:52:50 -07:00
parent 165ec98ceb
commit 18f301556f
5 changed files with 61 additions and 306 deletions

View file

@ -7,18 +7,17 @@
//
#import <UIKit/UIKit.h>
#import "MultiSelectSegmentedControl.h"
@class NewsBlurAppDelegate;
@interface NotificationFeedCell : UITableViewCell <MultiSelectSegmentedControlDelegate> {
NewsBlurAppDelegate *appDelegate;
}
@interface NotificationFeedCell : UITableViewCell
@property (nonatomic) NewsBlurAppDelegate *appDelegate;
@property (nonatomic) NSString *feedId;
@property (nonatomic) UISegmentedControl * filterControl;
@property (nonatomic) MultiSelectSegmentedControl * notificationTypeControl;
@property (nonatomic) UISegmentedControl *filterControl;
@property (nonatomic) UIButton *emailNotificationTypeButton;
@property (nonatomic) UIButton *webNotificationTypeButton;
@property (nonatomic) UIButton *iOSNotificationTypeButton;
@property (nonatomic) UIButton *androidNotificationTypeButton;
@end

View file

@ -11,10 +11,6 @@
@implementation NotificationFeedCell
@synthesize appDelegate;
@synthesize filterControl;
@synthesize notificationTypeControl;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {
@ -43,38 +39,46 @@
[self.filterControl setTitle:@"Focus Stories" forSegmentAtIndex:1];
[self.filterControl setImage:[UIImage imageNamed:@"unread_yellow.png"] forSegmentAtIndex:0];
[self.filterControl setImage:[UIImage imageNamed:@"unread_green.png"] forSegmentAtIndex:1];
[self.filterControl setWidth:CGRectGetWidth(self.frame)*0.44 forSegmentAtIndex:0];
[self.filterControl setWidth:CGRectGetWidth(self.frame)*0.44 forSegmentAtIndex:1];
[self.filterControl setTitleTextAttributes:controlAttrs forState:UIControlStateNormal];
self.filterControl.frame = CGRectMake(36, 38, CGRectGetWidth(self.frame), 28);
[self.contentView addSubview:self.filterControl];
self.notificationTypeControl = [[MultiSelectSegmentedControl alloc] initWithItems:@[@"Email",
@"Web",
@"iOS",
@"Android"]];
self.notificationTypeControl.delegate = self;
self.notificationTypeControl.tintColor = UIColorFromRGB(0x8F918B);
[self.notificationTypeControl.subviews objectAtIndex:0].accessibilityLabel = @"Email";
[self.notificationTypeControl.subviews objectAtIndex:1].accessibilityLabel = @"Web";
[self.notificationTypeControl.subviews objectAtIndex:2].accessibilityLabel = @"iOS";
[self.notificationTypeControl.subviews objectAtIndex:3].accessibilityLabel = @"Android";
[self.notificationTypeControl setTitle:@"Email" forSegmentAtIndex:0];
[self.notificationTypeControl setTitle:@"Web" forSegmentAtIndex:1];
[self.notificationTypeControl setTitle:@"iOS" forSegmentAtIndex:2];
[self.notificationTypeControl setTitle:@"Android" forSegmentAtIndex:3];
[self.notificationTypeControl setWidth:CGRectGetWidth(self.frame)*0.22 forSegmentAtIndex:0];
[self.notificationTypeControl setWidth:CGRectGetWidth(self.frame)*0.22 forSegmentAtIndex:1];
[self.notificationTypeControl setWidth:CGRectGetWidth(self.frame)*0.22 forSegmentAtIndex:2];
[self.notificationTypeControl setWidth:CGRectGetWidth(self.frame)*0.22 forSegmentAtIndex:3];
[self.notificationTypeControl setTitleTextAttributes:controlAttrs forState:UIControlStateNormal];
self.notificationTypeControl.frame = CGRectMake(36, 76, CGRectGetWidth(self.frame), 28);
[self.contentView addSubview:self.notificationTypeControl];
CGFloat offset = 0;
[[ThemeManager themeManager] updateSegmentedControl:self.filterControl];
self.emailNotificationTypeButton = [self makeNotificationTypeControlWithTitle:@"Email" offset:&offset];
self.webNotificationTypeButton = [self makeNotificationTypeControlWithTitle:@"Web" offset:&offset];
self.iOSNotificationTypeButton = [self makeNotificationTypeControlWithTitle:@"iOS" offset:&offset];
self.androidNotificationTypeButton = [self makeNotificationTypeControlWithTitle:@"Android" offset:&offset];
}
return self;
}
- (UIButton *)makeNotificationTypeControlWithTitle:(NSString *)title offset:(CGFloat *)offset {
CGRect frame = CGRectMake(36 + *offset, 76, CGRectGetWidth(self.frame) * 0.25, 28);
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = frame;
button.layer.borderColor = UIColorFromLightDarkRGB(0xe7e6e7, 0x3b3b3d).CGColor;
button.layer.borderWidth = 1.5;
button.layer.cornerRadius = 8.0;
[button setTitleColor:UIColorFromLightDarkRGB(0x0, 0xffffff) forState:UIControlStateNormal];
button.titleLabel.font = [UIFont systemFontOfSize:14];
[button setTitle:title forState:UIControlStateNormal];
[button addTarget:self action:@selector(changeNotification:) forControlEvents:UIControlEventTouchUpInside];
*offset += CGRectGetWidth(button.bounds);
[self.contentView addSubview:button];
return button;
}
- (void)layoutSubviews {
[super layoutSubviews];
@ -118,23 +122,28 @@
self.textLabel.frame = textLabelFrame;
}
NSDictionary *feed = [appDelegate.dictFeeds objectForKey:self.feedId];
NSDictionary *feed = [self.appDelegate.dictFeeds objectForKey:self.feedId];
if ([[feed objectForKey:@"notification_filter"] isEqualToString:@"focus"]) {
self.filterControl.selectedSegmentIndex = 1;
} else {
self.filterControl.selectedSegmentIndex = 0;
}
NSMutableIndexSet *types = [NSMutableIndexSet indexSet];
NSArray *notificationTypes = [feed objectForKey:@"notification_types"];
if ([notificationTypes containsObject:@"email"]) [types addIndex:0];
if ([notificationTypes containsObject:@"web"]) [types addIndex:1];
if ([notificationTypes containsObject:@"ios"]) [types addIndex:2];
if ([notificationTypes containsObject:@"android"]) [types addIndex:3];
[self.notificationTypeControl setSelectedSegmentIndexes:types];
[self updateNotificationTypeButton:self.emailNotificationTypeButton forCondition:[notificationTypes containsObject:@"email"]];
[self updateNotificationTypeButton:self.webNotificationTypeButton forCondition:[notificationTypes containsObject:@"web"]];
[self updateNotificationTypeButton:self.iOSNotificationTypeButton forCondition:[notificationTypes containsObject:@"ios"]];
[self updateNotificationTypeButton:self.androidNotificationTypeButton forCondition:[notificationTypes containsObject:@"android"]];
}
- (void)multiSelect:(MultiSelectSegmentedControl *)multiSelectSegmendedControl didChangeValue:(BOOL)value atIndex:(NSUInteger)index {
- (void)updateNotificationTypeButton:(UIButton *)button forCondition:(BOOL)on {
button.selected = on;
button.backgroundColor = on ? UIColorFromLightDarkRGB(0xffffff, 0x6f6f75) : UIColorFromLightDarkRGB(0xe7e6e7, 0x3b3b3d);
}
- (void)changeNotification:(UIButton *)button {
[self updateNotificationTypeButton:button forCondition:!button.selected];
[self saveNotifications];
}
@ -142,25 +151,25 @@
NSMutableArray *notificationTypes = [NSMutableArray array];
NSString *notificationFilter = self.filterControl.selectedSegmentIndex == 0 ? @"unread": @"focus";
if ([self.notificationTypeControl.selectedSegmentIndexes containsIndex:0])
if (self.emailNotificationTypeButton.selected) {
[notificationTypes addObject:@"email"];
if ([self.notificationTypeControl.selectedSegmentIndexes containsIndex:1])
}
if (self.webNotificationTypeButton.selected) {
[notificationTypes addObject:@"web"];
if ([self.notificationTypeControl.selectedSegmentIndexes containsIndex:2])
}
if (self.iOSNotificationTypeButton.selected) {
[notificationTypes addObject:@"ios"];
if ([self.notificationTypeControl.selectedSegmentIndexes containsIndex:3])
}
if (self.androidNotificationTypeButton.selected) {
[notificationTypes addObject:@"android"];
}
NSMutableDictionary *params = [NSMutableDictionary dictionary];
[params setObject:self.feedId forKey:@"feed_id"];
NSMutableArray *notifications = [NSMutableArray array];
for (NSString *notificationType in notificationTypes) {
[notifications addObject:notificationType];
}
[params setObject:notifications forKey:@"notification_types"];
[params setObject:notificationTypes forKey:@"notification_types"];
[params setObject:notificationFilter forKey:@"notification_filter"];
[appDelegate updateNotifications:params feed:self.feedId];
[self.appDelegate updateNotifications:params feed:self.feedId];
}
@end

View file

@ -369,7 +369,6 @@
FF5ACC241DE5F0C000FBD044 /* NotificationsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5ACC231DE5F0C000FBD044 /* NotificationsViewController.m */; };
FF5ACC271DE5FA3E00FBD044 /* NotificationFeedCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5ACC261DE5FA3E00FBD044 /* NotificationFeedCell.m */; };
FF5ACC2B1DE6088B00FBD044 /* menu_icn_notifications@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FF5ACC291DE6088B00FBD044 /* menu_icn_notifications@2x.png */; };
FF5ACC2E1DE63C6D00FBD044 /* MultiSelectSegmentedControl.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5ACC2D1DE63C6D00FBD044 /* MultiSelectSegmentedControl.m */; };
FF5D4017179A00B900349659 /* traverse_send.png in Resources */ = {isa = PBXBuildFile; fileRef = FF5D4015179A00B900349659 /* traverse_send.png */; };
FF5D4018179A00B900349659 /* traverse_send@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FF5D4016179A00B900349659 /* traverse_send@2x.png */; };
FF5D401B179A03E700349659 /* traverse_previous_off.png in Resources */ = {isa = PBXBuildFile; fileRef = FF5D4019179A03E700349659 /* traverse_previous_off.png */; };
@ -1132,8 +1131,6 @@
FF5ACC251DE5FA3E00FBD044 /* NotificationFeedCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NotificationFeedCell.h; sourceTree = "<group>"; };
FF5ACC261DE5FA3E00FBD044 /* NotificationFeedCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NotificationFeedCell.m; sourceTree = "<group>"; };
FF5ACC291DE6088B00FBD044 /* menu_icn_notifications@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menu_icn_notifications@2x.png"; sourceTree = "<group>"; };
FF5ACC2C1DE63C6D00FBD044 /* MultiSelectSegmentedControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MultiSelectSegmentedControl.h; path = "Other Sources/MultiSelectSegmentedControl.h"; sourceTree = "<group>"; };
FF5ACC2D1DE63C6D00FBD044 /* MultiSelectSegmentedControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MultiSelectSegmentedControl.m; path = "Other Sources/MultiSelectSegmentedControl.m"; sourceTree = "<group>"; };
FF5D4015179A00B900349659 /* traverse_send.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = traverse_send.png; sourceTree = "<group>"; };
FF5D4016179A00B900349659 /* traverse_send@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "traverse_send@2x.png"; sourceTree = "<group>"; };
FF5D4019179A03E700349659 /* traverse_previous_off.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = traverse_previous_off.png; sourceTree = "<group>"; };
@ -1623,8 +1620,6 @@
FF8D1EDA1BAA3CCD00725D8A /* JNWThrottledBlock.m */,
43A4C3C315B00966008787B5 /* MBProgressHUD.h */,
43A4C3C415B00966008787B5 /* MBProgressHUD.m */,
FF5ACC2C1DE63C6D00FBD044 /* MultiSelectSegmentedControl.h */,
FF5ACC2D1DE63C6D00FBD044 /* MultiSelectSegmentedControl.m */,
43A4C3CC15B00966008787B5 /* NSString+HTML.h */,
43A4C3CD15B00966008787B5 /* NSString+HTML.m */,
43A4C3D015B00966008787B5 /* StringHelper.h */,
@ -3335,7 +3330,6 @@
43ABBCAA15B53A1400EA3111 /* InteractionCell.m in Sources */,
FF34FD611E9D93CB0062F8ED /* IASKAppSettingsWebViewController.m in Sources */,
FF8D1ECF1BAA311000725D8A /* SBJson4StreamTokeniser.m in Sources */,
FF5ACC2E1DE63C6D00FBD044 /* MultiSelectSegmentedControl.m in Sources */,
FF8D1ED81BAA33BA00725D8A /* NSObject+SBJSON.m in Sources */,
FF34FD651E9D93CB0062F8ED /* IASKSettingsStore.m in Sources */,
43D818A315B940C200733444 /* DataUtilities.m in Sources */,

View file

@ -1,29 +0,0 @@
//
// MultiSelectSegmentedControl.h
//
// Created by Yonat Sharon on 19/4/13.
//
// Multiple-Selection Segmented Control
// No need for images - works with the builtin styles of UISegmentedControl.
// To get/set multiple segments programmatically, use the property
// myControl.selectedSegmentIndexes instead of myControl.selectedSegmentIndex
//
#import <UIKit/UIKit.h>
@class MultiSelectSegmentedControl;
@protocol MultiSelectSegmentedControlDelegate <NSObject>
-(void)multiSelect:(MultiSelectSegmentedControl*) multiSelecSegmendedControl didChangeValue:(BOOL) value atIndex: (NSUInteger) index;
@end
@interface MultiSelectSegmentedControl : UISegmentedControl
@property (nonatomic, assign) NSIndexSet *selectedSegmentIndexes;
@property (nonatomic, weak) id<MultiSelectSegmentedControlDelegate> delegate;
@property (nonatomic, readonly) NSArray *selectedSegmentTitles;
@property (nonatomic, assign) BOOL hideSeparatorBetweenSelectedSegments;
- (void)selectAllSegments:(BOOL)select; // pass NO to deselect all
@end

View file

@ -1,218 +0,0 @@
//
// MultiSelectSegmentedControl.m
//
// Created by Yonat Sharon on 19/4/13.
//
#import "MultiSelectSegmentedControl.h"
@interface MultiSelectSegmentedControl () {
BOOL hasBeenDrawn;
}
@property (nonatomic, strong) NSMutableArray *sortedSegments;
@property (nonatomic, strong) NSMutableIndexSet *selectedIndexes;
@end
@implementation MultiSelectSegmentedControl
#pragma mark - Selection API
- (NSIndexSet *)selectedSegmentIndexes
{
return self.selectedIndexes;
}
- (void)setSelectedSegmentIndexes:(NSIndexSet *)selectedSegmentIndexes
{
NSIndexSet *validIndexes = [selectedSegmentIndexes indexesPassingTest:^BOOL(NSUInteger idx, BOOL *stop) {
return idx < self.numberOfSegments;
}];
self.selectedIndexes = [[NSMutableIndexSet alloc] initWithIndexSet:validIndexes];
[self selectSegmentsOfSelectedIndexes];
}
- (void)selectAllSegments:(BOOL)select
{
self.selectedSegmentIndexes = select ? [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.numberOfSegments)] : [NSIndexSet indexSet];
}
- (NSArray*)selectedSegmentTitles {
__block NSMutableArray *titleArray = [[NSMutableArray alloc] initWithCapacity:[self.selectedSegmentIndexes count]];
[self.selectedSegmentIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
//NSLog(@"segment index is selected: %d; text: %@", idx, [self titleForSegmentAtIndex:idx]);
[titleArray addObject:[self titleForSegmentAtIndex:idx]];
}];
return [NSArray arrayWithArray:titleArray];
}
- (void)setHideSeparatorBetweenSelectedSegments:(BOOL)hideSeparatorBetweenSelectedSegments
{
if (hideSeparatorBetweenSelectedSegments == _hideSeparatorBetweenSelectedSegments) return;
_hideSeparatorBetweenSelectedSegments = hideSeparatorBetweenSelectedSegments;
[self selectSegmentsOfSelectedIndexes];
}
#pragma mark - Internals
- (void)initSortedSegmentsArray
{
self.sortedSegments = nil;
self.sortedSegments = [NSMutableArray arrayWithArray:self.subviews];
[self.sortedSegments sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
CGFloat x1 = ((UIView *)obj1).frame.origin.x;
CGFloat x2 = ((UIView *)obj2).frame.origin.x;
return (x1 > x2) - (x1 < x2);
}];
}
- (void)selectSegmentsOfSelectedIndexes
{
super.selectedSegmentIndex = UISegmentedControlNoSegment; // to allow taps on any segment
BOOL isPrevSelected = NO;
for (NSUInteger i = 0; i < self.numberOfSegments; ++i) {
BOOL isSelected = [self.selectedIndexes containsIndex:i];
[self.sortedSegments[i] setSelected:isSelected];
if (i > 0) { // divide selected segments in flat UI by hiding builtin divider
NSNumber *showBuiltinDivider = (isSelected && isPrevSelected && !self.hideSeparatorBetweenSelectedSegments) ? @0 : @1;
[self.sortedSegments[i-1] setValue:showBuiltinDivider forKey:@"showDivider"];
}
isPrevSelected = isSelected;
}
}
- (void)valueChanged
{
NSUInteger tappedSegmentIndex = super.selectedSegmentIndex;
if ([self.selectedIndexes containsIndex:tappedSegmentIndex]) {
[self.selectedIndexes removeIndex:tappedSegmentIndex];
if (self.delegate) {
[self.delegate multiSelect:self didChangeValue:NO atIndex:tappedSegmentIndex];
}
}
else {
[self.selectedIndexes addIndex:tappedSegmentIndex];
if (self.delegate) {
[self.delegate multiSelect:self didChangeValue:YES atIndex:tappedSegmentIndex];
}
}
[self selectSegmentsOfSelectedIndexes];
}
#pragma mark - Initialization
- (void)onInit
{
hasBeenDrawn = NO;
[self addTarget:self action:@selector(valueChanged) forControlEvents:UIControlEventValueChanged];
self.selectedIndexes = [NSMutableIndexSet indexSet];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self onInit];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self onInit];
}
return self;
}
#pragma mark - Overrides
- (void)drawRect:(CGRect)rect
{
if (!hasBeenDrawn) {
[self initSortedSegmentsArray];
hasBeenDrawn = YES;
[self selectSegmentsOfSelectedIndexes];
}
[super drawRect:rect];
}
- (void)setMomentary:(BOOL)momentary
{
// won't work with momentary selection
}
- (void)setSelectedSegmentIndex:(NSInteger)selectedSegmentIndex
{
if (nil == self.selectedIndexes) { // inside [super init*]
[super setSelectedSegmentIndex:selectedSegmentIndex];
}
self.selectedSegmentIndexes = selectedSegmentIndex == UISegmentedControlNoSegment ? [NSIndexSet indexSet] : [NSIndexSet indexSetWithIndex:selectedSegmentIndex];
}
- (NSInteger)selectedSegmentIndex
{
NSUInteger firstSelectedIndex = [self.selectedIndexes firstIndex];
return NSNotFound == firstSelectedIndex ? UISegmentedControlNoSegment : firstSelectedIndex;
}
- (void)onInsertSegmentAtIndex:(NSUInteger)segment
{
[self.selectedIndexes shiftIndexesStartingAtIndex:segment by:1];
[self initSortedSegmentsArray];
}
- (void)insertSegmentWithTitle:(NSString *)title atIndex:(NSUInteger)segment animated:(BOOL)animated
{
[super insertSegmentWithTitle:title atIndex:segment animated:animated];
[self onInsertSegmentAtIndex:segment];
}
- (void)insertSegmentWithImage:(UIImage *)image atIndex:(NSUInteger)segment animated:(BOOL)animated
{
[super insertSegmentWithImage:image atIndex:segment animated:animated];
[self onInsertSegmentAtIndex:segment];
}
- (void)removeSegmentAtIndex:(NSUInteger)segment animated:(BOOL)animated
{
// bounds check to avoid exceptions
NSUInteger n = self.numberOfSegments;
if (n == 0) return;
if (segment >= n) segment = n - 1;
// store multiple selection
NSMutableIndexSet *newSelectedIndexes = [[NSMutableIndexSet alloc] initWithIndexSet:self.selectedIndexes];
[newSelectedIndexes addIndex:segment]; // workaround - see http://ootips.org/yonat/workaround-for-bug-in-nsindexset-shiftindexesstartingatindex/
[newSelectedIndexes shiftIndexesStartingAtIndex:segment by:-1];
// remove the segment
super.selectedSegmentIndex = segment; // necessary to avoid NSRange exception
[super removeSegmentAtIndex:segment animated:animated]; // destroys self.selectedIndexes
// restore multiple selection after animation ends
self.selectedIndexes = newSelectedIndexes;
double delayInSeconds = animated? 0.45 : 0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self initSortedSegmentsArray];
[self selectSegmentsOfSelectedIndexes];
});
}
- (void)removeAllSegments
{
super.selectedSegmentIndex = 0;
[super removeAllSegments];
[self.selectedIndexes removeAllIndexes];
self.sortedSegments = nil;
}
@end