2013-06-21 17:48:06 -07:00
//
// IASKAppSettingsViewController . m
// http : // www . inappsettingskit . com
//
// Copyright ( c ) 2009 -2010 :
// Luc Vandal , Edovia Inc . , http : // www . edovia . com
// Ortwin Gentz , FutureTap GmbH , http : // www . futuretap . com
// All rights reserved .
//
// It is appreciated but not required that you give credit to Luc Vandal and Ortwin Gentz ,
// as the original authors of this code . You can give credit in a blog post , a tweet or on
// a info page of your app . Also , the original authors appreciate letting them know if you use this code .
//
// This code is licensed under the BSD license that is available at : http : // www . opensource . org / licenses / bsd - license . php
//
# import "IASKAppSettingsViewController.h"
# import "IASKSettingsReader.h"
# import "IASKSettingsStoreUserDefaults.h"
# import "IASKPSSliderSpecifierViewCell.h"
# import "IASKPSTextFieldSpecifierViewCell.h"
# import "IASKSwitch.h"
# import "IASKSlider.h"
# import "IASKSpecifier.h"
# import "IASKSpecifierValuesViewController.h"
# import "IASKTextField.h"
2017-04-11 16:15:18 -07:00
# import "IASKTextViewCell.h"
2015-03-03 15:56:32 -05:00
# import "IASKMultipleValueSelection.h"
2013-06-21 17:48:06 -07:00
2013-10-08 16:00:55 -07:00
# if ! __has _feature ( objc_arc )
# error "IASK needs ARC"
# endif
2013-06-21 17:48:06 -07:00
static NSString * kIASKCredits = @ "Powered by InAppSettingsKit" ; // Leave this as - is ! ! !
# define kIASKSpecifierValuesViewControllerIndex 0
# define kIASKSpecifierChildViewControllerIndex 1
# define kIASKCreditsViewWidth 285
CGRect IASKCGRectSwap ( CGRect rect ) ;
2017-04-11 16:15:18 -07:00
@ interface IASKAppSettingsViewController ( ) < UITextViewDelegate > {
2013-10-08 16:00:55 -07:00
IASKSettingsReader * _settingsReader ;
id < IASKSettingsStore > _settingsStore ;
id _currentFirstResponder ;
__weak UIViewController * _currentChildViewController ;
2014-05-19 13:33:42 -07:00
BOOL _reloadDisabled ;
2015-03-03 15:56:32 -05:00
// / The selected index for every group ( in case it ' s a radio group ) .
NSArray * _selections ;
2013-10-08 16:00:55 -07:00
}
@ property ( nonatomic , strong ) id currentFirstResponder ;
2017-04-11 16:15:18 -07:00
@ property ( nonatomic , strong ) NSMutableDictionary * rowHeights ;
2013-06-21 17:48:06 -07:00
- ( void ) _textChanged : ( id ) sender ;
- ( void ) synchronizeSettings ;
- ( void ) userDefaultsDidChange ;
- ( void ) reload ;
@ end
@ implementation IASKAppSettingsViewController
2013-10-08 16:00:55 -07:00
// synthesize properties from protocol
2013-06-21 17:48:06 -07:00
@ synthesize settingsReader = _settingsReader ;
@ synthesize settingsStore = _settingsStore ;
2013-10-08 16:00:55 -07:00
@ synthesize file = _file ;
2013-06-21 17:48:06 -07:00
# pragma mark accessors
- ( IASKSettingsReader * ) settingsReader {
if ( ! _settingsReader ) {
_settingsReader = [ [ IASKSettingsReader alloc ] initWithFile : self . file ] ;
2022-04-22 19:23:40 -07:00
_settingsReader . delegate = self . delegate ; // DJS
2015-03-03 15:56:32 -05:00
if ( self . neverShowPrivacySettings ) {
_settingsReader . showPrivacySettings = NO ;
}
2013-06-21 17:48:06 -07:00
}
return _settingsReader ;
}
- ( id < IASKSettingsStore > ) settingsStore {
if ( ! _settingsStore ) {
_settingsStore = [ [ IASKSettingsStoreUserDefaults alloc ] init ] ;
}
return _settingsStore ;
}
- ( NSString * ) file {
if ( ! _file ) {
2015-03-03 15:56:32 -05:00
self . file = @ "Root" ;
2013-06-21 17:48:06 -07:00
}
2013-10-08 16:00:55 -07:00
return _file ;
2013-06-21 17:48:06 -07:00
}
- ( void ) setFile : ( NSString * ) file {
2013-10-08 16:00:55 -07:00
_file = [ file copy ] ;
2015-09-16 16:53:07 -07:00
self . tableView . contentOffset = CGPointMake ( 0 , - self . tableView . contentInset . top ) ;
2013-10-08 16:00:55 -07:00
self . settingsReader = nil ; // automatically initializes itself
2015-03-03 15:56:32 -05:00
if ( ! _reloadDisabled ) {
[ self . tableView reloadData ] ;
[ self createSelections ] ;
}
}
- ( void ) createSelections {
NSMutableArray * sectionSelection = [ NSMutableArray new ] ;
for ( int i = 0 ; i < _settingsReader . numberOfSections ; i + + ) {
IASKSpecifier * specifier = [ self . settingsReader headerSpecifierForSection : i ] ;
if ( [ specifier . type isEqualToString : kIASKPSRadioGroupSpecifier ] ) {
2017-04-11 16:15:18 -07:00
IASKMultipleValueSelection * selection = [ [ IASKMultipleValueSelection alloc ] initWithSettingsStore : self . settingsStore ] ;
2015-03-03 15:56:32 -05:00
selection . tableView = self . tableView ;
selection . specifier = specifier ;
selection . section = i ;
[ sectionSelection addObject : selection ] ;
} else {
[ sectionSelection addObject : [ NSNull null ] ] ;
}
}
_selections = sectionSelection ;
2013-06-21 17:48:06 -07:00
}
- ( BOOL ) isPad {
BOOL isPad = NO ;
# if ( __IPHONE _OS _VERSION _MAX _ALLOWED >= 30200 )
2020-03-29 16:21:00 -07:00
isPad = [ [ UIDevice currentDevice ] userInterfaceIdiom ] = = UIUserInterfaceIdiomPad ;
2013-06-21 17:48:06 -07:00
# endif
return isPad ;
}
# pragma mark standard view controller methods
- ( id ) init {
return [ self initWithStyle : UITableViewStyleGrouped ] ;
}
2015-09-16 16:53:07 -07:00
- ( id ) initWithStyle : ( UITableViewStyle ) style {
2013-06-21 17:48:06 -07:00
if ( style ! = UITableViewStyleGrouped ) {
2015-09-16 16:53:07 -07:00
NSLog ( @ "WARNING: only UITableViewStyleGrouped style is supported by InAppSettingsKit." ) ;
2013-06-21 17:48:06 -07:00
}
2015-09-16 16:53:07 -07:00
if ( ( self = [ super initWithStyle : style ] ) ) {
[ self configure ] ;
2013-06-21 17:48:06 -07:00
}
return self ;
}
2015-09-16 16:53:07 -07:00
- ( id ) initWithNibName : ( NSString * ) nibNameOrNil bundle : ( NSBundle * ) nibBundleOrNil {
if ( nibNameOrNil ) {
NSLog ( @ "%@ is now deprecated, we are moving away from nibs." , NSStringFromSelector ( _cmd ) ) ;
self = [ super initWithStyle : UITableViewStyleGrouped ] ;
} else {
self = [ super initWithNibName : nibNameOrNil bundle : nibBundleOrNil ] ;
}
if ( self ) {
[ self configure ] ;
}
return self ;
}
- ( id ) initWithCoder : ( NSCoder * ) aDecoder {
if ( ( self = [ super initWithCoder : aDecoder ] ) ) {
[ self configure ] ;
_showDoneButton = NO ;
}
return self ;
}
- ( void ) configure {
_reloadDisabled = NO ;
_showDoneButton = YES ;
_showCreditsFooter = YES ; // display credits for InAppSettingsKit creators
2017-04-11 16:15:18 -07:00
self . rowHeights = [ NSMutableDictionary dictionary ] ;
2013-06-21 17:48:06 -07:00
}
2014-05-19 13:33:42 -07:00
- ( void ) viewDidLoad {
2013-10-08 16:00:55 -07:00
[ super viewDidLoad ] ;
if ( [ self isPad ] ) {
2017-09-27 12:19:58 -07:00
self . tableView . separatorStyle = UITableViewCellSeparatorStyleSingleLine ;
2013-10-08 16:00:55 -07:00
}
2013-06-21 17:48:06 -07:00
UITapGestureRecognizer * tapGesture = [ [ UITapGestureRecognizer alloc ] initWithTarget : self action : @ selector ( singleTapToEndEdit : ) ] ;
tapGesture . cancelsTouchesInView = NO ;
[ self . tableView addGestureRecognizer : tapGesture ] ;
}
- ( void ) viewWillAppear : ( BOOL ) animated {
2017-04-11 16:15:18 -07:00
NSIndexPath * selectedIndexPath = [ self . tableView indexPathForSelectedRow ] ;
[ super viewWillAppear : animated ] ;
2013-06-21 17:48:06 -07:00
// if there ' s something selected , the value might have changed
// so reload that row
if ( selectedIndexPath ) {
2017-04-11 16:15:18 -07:00
dispatch_after ( dispatch_time ( DISPATCH_TIME _NOW , ( int64_t ) ( animated * UINavigationControllerHideShowBarDuration * NSEC_PER _SEC ) ) , dispatch_get _main _queue ( ) , ^ {
[ self . tableView reloadRowsAtIndexPaths : [ NSArray arrayWithObject : selectedIndexPath ]
withRowAnimation : UITableViewRowAnimationNone ] ;
// and reselect it , so we get the nice default deselect animation from UITableViewController
[ self . tableView selectRowAtIndexPath : selectedIndexPath animated : NO scrollPosition : UITableViewScrollPositionNone ] ;
[ self . tableView deselectRowAtIndexPath : selectedIndexPath animated : YES ] ;
} ) ;
2013-06-21 17:48:06 -07:00
}
if ( _showDoneButton ) {
UIBarButtonItem * buttonItem = [ [ UIBarButtonItem alloc ] initWithBarButtonSystemItem : UIBarButtonSystemItemDone
target : self
action : @ selector ( dismiss : ) ] ;
self . navigationItem . rightBarButtonItem = buttonItem ;
}
if ( ! self . title ) {
self . title = NSLocalizedString ( @ "Settings" , @ "" ) ;
}
if ( [ self . settingsStore isKindOfClass : [ IASKSettingsStoreUserDefaults class ] ] ) {
2015-03-03 15:56:32 -05:00
NSNotificationCenter * dc = NSNotificationCenter . defaultCenter ;
2015-09-16 16:53:07 -07:00
IASKSettingsStoreUserDefaults * udSettingsStore = ( id ) self . settingsStore ;
[ dc addObserver : self selector : @ selector ( userDefaultsDidChange ) name : NSUserDefaultsDidChangeNotification object : udSettingsStore . defaults ] ;
2015-03-03 15:56:32 -05:00
[ dc addObserver : self selector : @ selector ( didChangeSettingViaIASK : ) name : kIASKAppSettingChanged object : nil ] ;
2013-06-21 17:48:06 -07:00
[ self userDefaultsDidChange ] ; // force update in case of changes while we were hidden
}
}
2017-04-11 16:15:18 -07:00
- ( CGSize ) preferredContentSize {
2019-09-21 14:51:55 -07:00
return [ [ self view ] sizeThatFits : CGSizeMake ( 500 , 2000 ) ] ;
2013-06-21 17:48:06 -07:00
}
- ( void ) viewDidAppear : ( BOOL ) animated {
[ super viewDidAppear : animated ] ;
NSNotificationCenter * dc = [ NSNotificationCenter defaultCenter ] ;
2014-05-19 13:33:42 -07:00
[ dc addObserver : self selector : @ selector ( synchronizeSettings ) name : UIApplicationDidEnterBackgroundNotification object : [ UIApplication sharedApplication ] ] ;
[ dc addObserver : self selector : @ selector ( reload ) name : UIApplicationWillEnterForegroundNotification object : [ UIApplication sharedApplication ] ] ;
2013-06-21 17:48:06 -07:00
[ dc addObserver : self selector : @ selector ( synchronizeSettings ) name : UIApplicationWillTerminateNotification object : [ UIApplication sharedApplication ] ] ;
2015-03-03 15:56:32 -05:00
[ self . tableView beginUpdates ] ;
[ self . tableView endUpdates ] ;
2013-06-21 17:48:06 -07:00
}
- ( void ) viewWillDisappear : ( BOOL ) animated {
[ NSObject cancelPreviousPerformRequestsWithTarget : self ] ;
2014-09-22 16:09:34 -07:00
// hide the keyboard
[ self . currentFirstResponder resignFirstResponder ] ;
2013-06-21 17:48:06 -07:00
[ super viewWillDisappear : animated ] ;
}
- ( void ) viewDidDisappear : ( BOOL ) animated {
2014-05-19 13:33:42 -07:00
NSNotificationCenter * dc = [ NSNotificationCenter defaultCenter ] ;
2015-09-16 16:53:07 -07:00
if ( [ self . settingsStore isKindOfClass : [ IASKSettingsStoreUserDefaults class ] ] ) {
IASKSettingsStoreUserDefaults * udSettingsStore = ( id ) self . settingsStore ;
[ dc removeObserver : self name : NSUserDefaultsDidChangeNotification object : udSettingsStore . defaults ] ;
[ dc removeObserver : self name : kIASKAppSettingChanged object : self ] ;
}
2014-05-19 13:33:42 -07:00
[ dc removeObserver : self name : UIApplicationDidEnterBackgroundNotification object : [ UIApplication sharedApplication ] ] ;
[ dc removeObserver : self name : UIApplicationWillEnterForegroundNotification object : [ UIApplication sharedApplication ] ] ;
[ dc removeObserver : self name : UIApplicationWillTerminateNotification object : [ UIApplication sharedApplication ] ] ;
2013-06-21 17:48:06 -07:00
[ super viewDidDisappear : animated ] ;
}
- ( void ) setHiddenKeys : ( NSSet * ) theHiddenKeys {
[ self setHiddenKeys : theHiddenKeys animated : NO ] ;
}
- ( void ) setHiddenKeys : ( NSSet * ) theHiddenKeys animated : ( BOOL ) animated {
if ( _hiddenKeys ! = theHiddenKeys ) {
NSSet * oldHiddenKeys = _hiddenKeys ;
2013-10-08 16:00:55 -07:00
_hiddenKeys = theHiddenKeys ;
2013-06-21 17:48:06 -07:00
if ( animated ) {
NSMutableSet * showKeys = [ NSMutableSet setWithSet : oldHiddenKeys ] ;
[ showKeys minusSet : theHiddenKeys ] ;
NSMutableSet * hideKeys = [ NSMutableSet setWithSet : theHiddenKeys ] ;
[ hideKeys minusSet : oldHiddenKeys ] ;
// calculate rows to be deleted
NSMutableArray * hideIndexPaths = [ NSMutableArray array ] ;
for ( NSString * key in hideKeys ) {
NSIndexPath * indexPath = [ self . settingsReader indexPathForKey : key ] ;
if ( indexPath ) {
[ hideIndexPaths addObject : indexPath ] ;
}
}
// calculate sections to be deleted
NSMutableIndexSet * hideSections = [ NSMutableIndexSet indexSet ] ;
for ( NSInteger section = 0 ; section < [ self numberOfSectionsInTableView : self . tableView ] ; section + + ) {
2013-10-08 16:00:55 -07:00
NSInteger rowsInSection = 0 ;
2013-06-21 17:48:06 -07:00
for ( NSIndexPath * indexPath in hideIndexPaths ) {
if ( indexPath . section = = section ) {
rowsInSection + + ;
}
}
2015-03-03 15:56:32 -05:00
if ( rowsInSection && rowsInSection >= [ self . settingsReader numberOfRowsForSection : section ] ) {
2013-06-21 17:48:06 -07:00
[ hideSections addIndex : section ] ;
}
}
// set the datasource
self . settingsReader . hiddenKeys = theHiddenKeys ;
// calculate rows to be inserted
NSMutableArray * showIndexPaths = [ NSMutableArray array ] ;
for ( NSString * key in showKeys ) {
NSIndexPath * indexPath = [ self . settingsReader indexPathForKey : key ] ;
if ( indexPath ) {
[ showIndexPaths addObject : indexPath ] ;
}
}
// calculate sections to be inserted
NSMutableIndexSet * showSections = [ NSMutableIndexSet indexSet ] ;
for ( NSInteger section = 0 ; section < [ self . settingsReader numberOfSections ] ; section + + ) {
2013-10-08 16:00:55 -07:00
NSInteger rowsInSection = 0 ;
2013-06-21 17:48:06 -07:00
for ( NSIndexPath * indexPath in showIndexPaths ) {
if ( indexPath . section = = section ) {
rowsInSection + + ;
}
}
2015-03-03 15:56:32 -05:00
if ( rowsInSection && rowsInSection >= [ self . settingsReader numberOfRowsForSection : section ] ) {
2013-06-21 17:48:06 -07:00
[ showSections addIndex : section ] ;
}
2015-03-03 15:56:32 -05:00
}
if ( hideSections . count || hideIndexPaths . count || showSections . count || showIndexPaths . count ) {
[ self . tableView beginUpdates ] ;
UITableViewRowAnimation animation = animated ? UITableViewRowAnimationAutomatic : UITableViewRowAnimationNone ;
if ( hideSections . count ) {
[ self . tableView deleteSections : hideSections withRowAnimation : animation ] ;
}
if ( hideIndexPaths ) {
[ self . tableView deleteRowsAtIndexPaths : hideIndexPaths withRowAnimation : animation ] ;
}
if ( showSections . count ) {
[ self . tableView insertSections : showSections withRowAnimation : animation ] ;
}
if ( showIndexPaths ) {
[ self . tableView insertRowsAtIndexPaths : showIndexPaths withRowAnimation : animation ] ;
}
[ self . tableView endUpdates ] ;
}
} else {
self . settingsReader . hiddenKeys = theHiddenKeys ;
if ( ! _reloadDisabled ) [ self . tableView reloadData ] ;
}
}
UIViewController * childViewController = _currentChildViewController ;
2013-10-08 16:00:55 -07:00
if ( [ childViewController respondsToSelector : @ selector ( setHiddenKeys : animated : ) ] ) {
[ ( id ) childViewController setHiddenKeys : theHiddenKeys animated : animated ] ;
}
2013-06-21 17:48:06 -07:00
}
2015-09-16 16:53:07 -07:00
- ( void ) setNeverShowPrivacySettings : ( BOOL ) neverShowPrivacySettings {
_neverShowPrivacySettings = neverShowPrivacySettings ;
self . settingsReader = nil ;
[ self reload ] ;
}
2013-06-21 17:48:06 -07:00
- ( void ) dealloc {
[ [ NSNotificationCenter defaultCenter ] removeObserver : self ] ;
}
# pragma mark -
# pragma mark Actions
- ( void ) dismiss : ( id ) sender {
[ self . settingsStore synchronize ] ;
if ( self . delegate && [ self . delegate conformsToProtocol : @ protocol ( IASKSettingsDelegate ) ] ) {
[ self . delegate settingsViewControllerDidEnd : self ] ;
}
}
- ( void ) toggledValue : ( id ) sender {
2013-10-08 16:00:55 -07:00
IASKSwitch * toggle = ( IASKSwitch * ) sender ;
2013-06-21 17:48:06 -07:00
IASKSpecifier * spec = [ _settingsReader specifierForKey : [ toggle key ] ] ;
if ( [ toggle isOn ] ) {
if ( [ spec trueValue ] ! = nil ) {
[ self . settingsStore setObject : [ spec trueValue ] forKey : [ toggle key ] ] ;
}
else {
[ self . settingsStore setBool : YES forKey : [ toggle key ] ] ;
}
}
else {
if ( [ spec falseValue ] ! = nil ) {
[ self . settingsStore setObject : [ spec falseValue ] forKey : [ toggle key ] ] ;
}
else {
[ self . settingsStore setBool : NO forKey : [ toggle key ] ] ;
}
}
[ [ NSNotificationCenter defaultCenter ] postNotificationName : kIASKAppSettingChanged
2017-04-11 16:15:18 -07:00
object : self
2013-06-21 17:48:06 -07:00
userInfo : [ NSDictionary dictionaryWithObject : [ self . settingsStore objectForKey : [ toggle key ] ]
forKey : [ toggle key ] ] ] ;
}
- ( void ) sliderChangedValue : ( id ) sender {
2013-10-08 16:00:55 -07:00
IASKSlider * slider = ( IASKSlider * ) sender ;
2013-06-21 17:48:06 -07:00
[ self . settingsStore setFloat : [ slider value ] forKey : [ slider key ] ] ;
[ [ NSNotificationCenter defaultCenter ] postNotificationName : kIASKAppSettingChanged
2017-04-11 16:15:18 -07:00
object : self
2013-06-21 17:48:06 -07:00
userInfo : [ NSDictionary dictionaryWithObject : [ NSNumber numberWithFloat : [ slider value ] ]
forKey : [ slider key ] ] ] ;
}
# pragma mark -
# pragma mark UITableView Functions
- ( NSInteger ) numberOfSectionsInTableView : ( UITableView * ) tableView {
return [ self . settingsReader numberOfSections ] ;
}
- ( NSInteger ) tableView : ( UITableView * ) tableView numberOfRowsInSection : ( NSInteger ) section {
return [ self . settingsReader numberOfRowsForSection : section ] ;
}
- ( CGFloat ) tableView : ( UITableView * ) tableView heightForRowAtIndexPath : ( NSIndexPath * ) indexPath {
IASKSpecifier * specifier = [ self . settingsReader specifierForIndexPath : indexPath ] ;
2017-04-11 16:15:18 -07:00
if ( [ specifier . type isEqualToString : kIASKTextViewSpecifier ] ) {
CGFloat height = ( CGFloat ) [ self . rowHeights [ specifier . key ] doubleValue ] ;
return height > 0 ? height : UITableViewAutomaticDimension ;
} else if ( [ [ specifier type ] isEqualToString : kIASKCustomViewSpecifier ] ) {
2013-06-21 17:48:06 -07:00
if ( [ self . delegate respondsToSelector : @ selector ( tableView : heightForSpecifier : ) ] ) {
return [ self . delegate tableView : tableView heightForSpecifier : specifier ] ;
} else {
return 0 ;
}
}
2015-03-03 15:56:32 -05:00
IASK_IF _IOS7 _OR _GREATER
(
NSDictionary * rowHeights = @ { UIContentSizeCategoryExtraSmall : @ ( 44 ) ,
UIContentSizeCategorySmall : @ ( 44 ) ,
UIContentSizeCategoryMedium : @ ( 44 ) ,
UIContentSizeCategoryLarge : @ ( 44 ) ,
UIContentSizeCategoryExtraLarge : @ ( 47 ) } ;
2017-04-11 16:15:18 -07:00
CGFloat rowHeight = ( CGFloat ) [ rowHeights [ UIApplication . sharedApplication . preferredContentSizeCategory ] doubleValue ] ;
return rowHeight ! = 0 ? rowHeight : 51 ;
2015-03-03 15:56:32 -05:00
) ;
return 44 ;
2013-06-21 17:48:06 -07:00
}
- ( NSString * ) tableView : ( UITableView * ) tableView titleForHeaderInSection : ( NSInteger ) section {
NSString * header = [ self . settingsReader titleForSection : section ] ;
if ( 0 = = header . length ) {
return nil ;
}
return header ;
}
- ( UIView * ) tableView : ( UITableView * ) tableView viewForHeaderInSection : ( NSInteger ) section {
if ( [ self . delegate respondsToSelector : @ selector ( settingsViewController : tableView : viewForHeaderForSection : ) ] ) {
return [ self . delegate settingsViewController : self tableView : tableView viewForHeaderForSection : section ] ;
} else {
return nil ;
}
}
- ( CGFloat ) tableView : ( UITableView * ) tableView heightForHeaderInSection : ( NSInteger ) section {
if ( [ self tableView : tableView viewForHeaderInSection : section ] && [ self . delegate respondsToSelector : @ selector ( settingsViewController : tableView : heightForHeaderForSection : ) ] ) {
2015-09-16 16:53:07 -07:00
CGFloat result = [ self . delegate settingsViewController : self tableView : tableView heightForHeaderForSection : section ] ;
if ( result > 0 ) {
2013-06-21 17:48:06 -07:00
return result ;
}
}
2015-03-03 15:56:32 -05:00
return UITableViewAutomaticDimension ;
2013-06-21 17:48:06 -07:00
}
- ( NSString * ) tableView : ( UITableView * ) tableView titleForFooterInSection : ( NSInteger ) section
{
NSString * footerText = [ self . settingsReader footerTextForSection : section ] ;
if ( _showCreditsFooter && ( section = = [ self . settingsReader numberOfSections ] -1 ) ) {
// show credits since this is the last section
if ( ( footerText = = nil ) || ( [ footerText length ] = = 0 ) ) {
// show the credits on their own
return kIASKCredits ;
} else {
// show the credits below the app ' s FooterText
return [ NSString stringWithFormat : @ "%@\n\n%@" , footerText , kIASKCredits ] ;
}
} else {
2014-05-19 13:33:42 -07:00
return footerText ;
2013-06-21 17:48:06 -07:00
}
}
2014-05-19 13:33:42 -07:00
2015-03-03 15:56:32 -05:00
- ( UITableViewCell * ) tableView : ( UITableView * ) tableView newCellForSpecifier : ( IASKSpecifier * ) specifier {
NSString * identifier = [ NSString stringWithFormat : @ "%@-%ld-%d" , specifier . type , ( long ) specifier . textAlignment , ! ! specifier . subtitle . length ] ;
UITableViewCell * cell = [ tableView dequeueReusableCellWithIdentifier : identifier ] ;
if ( cell ) {
return cell ;
}
UITableViewCellStyle style = ( specifier . textAlignment = = NSTextAlignmentLeft || specifier . subtitle . length ) ? UITableViewCellStyleSubtitle : UITableViewCellStyleDefault ;
if ( [ identifier hasPrefix : kIASKPSToggleSwitchSpecifier ] ) {
cell = [ [ UITableViewCell alloc ] initWithStyle : UITableViewCellStyleSubtitle reuseIdentifier : kIASKPSToggleSwitchSpecifier ] ;
2013-10-08 16:00:55 -07:00
cell . accessoryView = [ [ IASKSwitch alloc ] initWithFrame : CGRectMake ( 0 , 0 , 79 , 27 ) ] ;
2013-06-21 17:48:06 -07:00
[ ( ( IASKSwitch * ) cell . accessoryView ) addTarget : self action : @ selector ( toggledValue : ) forControlEvents : UIControlEventValueChanged ] ;
cell . selectionStyle = UITableViewCellSelectionStyleNone ;
}
2015-03-03 15:56:32 -05:00
else if ( [ identifier hasPrefix : kIASKPSMultiValueSpecifier ] || [ identifier hasPrefix : kIASKPSTitleValueSpecifier ] ) {
cell = [ [ UITableViewCell alloc ] initWithStyle : UITableViewCellStyleValue1 reuseIdentifier : identifier ] ;
cell . accessoryType = [ identifier hasPrefix : kIASKPSMultiValueSpecifier ] ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone ;
2013-06-21 17:48:06 -07:00
}
2015-03-03 15:56:32 -05:00
else if ( [ identifier hasPrefix : kIASKPSTextFieldSpecifier ] ) {
2013-06-21 17:48:06 -07:00
cell = [ [ IASKPSTextFieldSpecifierViewCell alloc ] initWithStyle : UITableViewCellStyleDefault reuseIdentifier : kIASKPSTextFieldSpecifier ] ;
[ ( ( IASKPSTextFieldSpecifierViewCell * ) cell ) . textField addTarget : self action : @ selector ( _textChanged : ) forControlEvents : UIControlEventEditingChanged ] ;
}
2017-04-11 16:15:18 -07:00
else if ( [ identifier hasPrefix : kIASKTextViewSpecifier ] ) {
cell = [ [ IASKTextViewCell alloc ] initWithStyle : UITableViewCellStyleDefault reuseIdentifier : kIASKTextViewSpecifier ] ;
}
2015-03-03 15:56:32 -05:00
else if ( [ identifier hasPrefix : kIASKPSSliderSpecifier ] ) {
2013-06-21 17:48:06 -07:00
cell = [ [ IASKPSSliderSpecifierViewCell alloc ] initWithStyle : UITableViewCellStyleDefault reuseIdentifier : kIASKPSSliderSpecifier ] ;
2015-03-03 15:56:32 -05:00
} else if ( [ identifier hasPrefix : kIASKPSChildPaneSpecifier ] ) {
cell = [ [ UITableViewCell alloc ] initWithStyle : style reuseIdentifier : identifier ] ;
2013-06-21 17:48:06 -07:00
cell . accessoryType = UITableViewCellAccessoryDisclosureIndicator ;
} else if ( [ identifier isEqualToString : kIASKMailComposeSpecifier ] ) {
2015-03-03 15:56:32 -05:00
cell = [ [ UITableViewCell alloc ] initWithStyle : style reuseIdentifier : identifier ] ;
2013-06-21 17:48:06 -07:00
[ cell setAccessoryType : UITableViewCellAccessoryDisclosureIndicator ] ;
} else {
2015-03-03 15:56:32 -05:00
cell = [ [ UITableViewCell alloc ] initWithStyle : style reuseIdentifier : identifier ] ;
if ( [ identifier isEqualToString : kIASKOpenURLSpecifier ] ) {
cell . accessoryType = UITableViewCellAccessoryDisclosureIndicator ;
}
2013-06-21 17:48:06 -07:00
}
2014-05-19 13:33:42 -07:00
IASK_IF _PRE _IOS6 ( cell . textLabel . minimumFontSize = kIASKMinimumFontSize ;
cell . detailTextLabel . minimumFontSize = kIASKMinimumFontSize ; ) ;
IASK_IF _IOS6 _OR _GREATER ( cell . textLabel . minimumScaleFactor = kIASKMinimumFontSize / cell . textLabel . font . pointSize ;
cell . detailTextLabel . minimumScaleFactor = kIASKMinimumFontSize / cell . detailTextLabel . font . pointSize ; ) ;
2013-06-21 17:48:06 -07:00
return cell ;
}
- ( UITableViewCell * ) tableView : ( UITableView * ) tableView cellForRowAtIndexPath : ( NSIndexPath * ) indexPath {
IASKSpecifier * specifier = [ self . settingsReader specifierForIndexPath : indexPath ] ;
if ( [ specifier . type isEqualToString : kIASKCustomViewSpecifier ] && [ self . delegate respondsToSelector : @ selector ( tableView : cellForSpecifier : ) ] ) {
UITableViewCell * cell = [ self . delegate tableView : tableView cellForSpecifier : specifier ] ;
assert ( nil ! = cell && "delegate must return a UITableViewCell for custom cell types" ) ;
return cell ;
}
2015-03-03 15:56:32 -05:00
UITableViewCell * cell = [ self tableView : tableView newCellForSpecifier : specifier ] ;
2013-06-21 17:48:06 -07:00
if ( [ specifier . type isEqualToString : kIASKPSToggleSwitchSpecifier ] ) {
cell . textLabel . text = specifier . title ;
2015-03-03 15:56:32 -05:00
cell . detailTextLabel . text = specifier . subtitle ;
2013-06-21 17:48:06 -07:00
id currentValue = [ self . settingsStore objectForKey : specifier . key ] ;
BOOL toggleState ;
if ( currentValue ) {
if ( [ currentValue isEqual : specifier . trueValue ] ) {
toggleState = YES ;
} else if ( [ currentValue isEqual : specifier . falseValue ] ) {
toggleState = NO ;
} else {
toggleState = [ currentValue boolValue ] ;
}
} else {
toggleState = specifier . defaultBoolValue ;
}
IASKSwitch * toggle = ( IASKSwitch * ) cell . accessoryView ;
toggle . on = toggleState ;
toggle . key = specifier . key ;
}
else if ( [ specifier . type isEqualToString : kIASKPSMultiValueSpecifier ] ) {
cell . textLabel . text = specifier . title ;
cell . detailTextLabel . text = [ [ specifier titleForCurrentValue : [ self . settingsStore objectForKey : specifier . key ] ! = nil ?
[ self . settingsStore objectForKey : specifier . key ] : specifier . defaultValue ] description ] ;
}
else if ( [ specifier . type isEqualToString : kIASKPSTitleValueSpecifier ] ) {
cell . textLabel . text = specifier . title ;
id value = [ self . settingsStore objectForKey : specifier . key ] ? : specifier . defaultValue ;
NSString * stringValue ;
if ( specifier . multipleValues || specifier . multipleTitles ) {
stringValue = [ specifier titleForCurrentValue : value ] ;
} else {
stringValue = [ value description ] ;
}
cell . detailTextLabel . text = stringValue ;
cell . userInteractionEnabled = NO ;
}
else if ( [ specifier . type isEqualToString : kIASKPSTextFieldSpecifier ] ) {
cell . textLabel . text = specifier . title ;
NSString * textValue = [ self . settingsStore objectForKey : specifier . key ] ! = nil ? [ self . settingsStore objectForKey : specifier . key ] : specifier . defaultStringValue ;
if ( textValue && ! [ textValue isMemberOfClass : [ NSString class ] ] ) {
textValue = [ NSString stringWithFormat : @ "%@" , textValue ] ;
}
IASKTextField * textField = ( ( IASKPSTextFieldSpecifierViewCell * ) cell ) . textField ;
textField . text = textValue ;
2017-04-11 16:15:18 -07:00
textField . placeholder = specifier . placeholder ;
2013-06-21 17:48:06 -07:00
textField . key = specifier . key ;
textField . delegate = self ;
textField . secureTextEntry = [ specifier isSecure ] ;
textField . keyboardType = specifier . keyboardType ;
textField . autocapitalizationType = specifier . autocapitalizationType ;
if ( [ specifier isSecure ] ) {
textField . autocorrectionType = UITextAutocorrectionTypeNo ;
} else {
textField . autocorrectionType = specifier . autoCorrectionType ;
}
textField . textAlignment = specifier . textAlignment ;
textField . adjustsFontSizeToFitWidth = specifier . adjustsFontSizeToFitWidth ;
}
2017-04-11 16:15:18 -07:00
else if ( [ specifier . type isEqualToString : kIASKTextViewSpecifier ] ) {
IASKTextViewCell * textCell = ( id ) cell ;
NSString * value = [ self . settingsStore objectForKey : specifier . key ] ! = nil ? [ self . settingsStore objectForKey : specifier . key ] : specifier . defaultStringValue ;
textCell . textView . text = value ;
textCell . textView . delegate = self ;
textCell . textView . key = specifier . key ;
textCell . textView . keyboardType = specifier . keyboardType ;
textCell . textView . autocapitalizationType = specifier . autocapitalizationType ;
textCell . textView . autocorrectionType = specifier . autoCorrectionType ;
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ self cacheRowHeightForTextView : textCell . textView ] ;
} ) ;
}
2013-06-21 17:48:06 -07:00
else if ( [ specifier . type isEqualToString : kIASKPSSliderSpecifier ] ) {
if ( specifier . minimumValueImage . length > 0 ) {
( ( IASKPSSliderSpecifierViewCell * ) cell ) . minImage . image = [ UIImage imageWithContentsOfFile : [ _settingsReader pathForImageNamed : specifier . minimumValueImage ] ] ;
}
if ( specifier . maximumValueImage . length > 0 ) {
( ( IASKPSSliderSpecifierViewCell * ) cell ) . maxImage . image = [ UIImage imageWithContentsOfFile : [ _settingsReader pathForImageNamed : specifier . maximumValueImage ] ] ;
}
IASKSlider * slider = ( ( IASKPSSliderSpecifierViewCell * ) cell ) . slider ;
slider . minimumValue = specifier . minimumValue ;
slider . maximumValue = specifier . maximumValue ;
slider . value = [ self . settingsStore objectForKey : specifier . key ] ! = nil ? [ [ self . settingsStore objectForKey : specifier . key ] floatValue ] : [ specifier . defaultValue floatValue ] ;
[ slider addTarget : self action : @ selector ( sliderChangedValue : ) forControlEvents : UIControlEventValueChanged ] ;
slider . key = specifier . key ;
[ cell setNeedsLayout ] ;
}
else if ( [ specifier . type isEqualToString : kIASKPSChildPaneSpecifier ] ) {
cell . textLabel . text = specifier . title ;
2015-03-03 15:56:32 -05:00
cell . detailTextLabel . text = specifier . subtitle ;
2013-06-21 17:48:06 -07:00
} else if ( [ specifier . type isEqualToString : kIASKOpenURLSpecifier ] || [ specifier . type isEqualToString : kIASKMailComposeSpecifier ] ) {
cell . textLabel . text = specifier . title ;
2015-03-03 15:56:32 -05:00
cell . detailTextLabel . text = specifier . subtitle ? : [ specifier . defaultValue description ] ;
cell . accessoryType = ( specifier . textAlignment = = NSTextAlignmentLeft ) ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone ;
2013-06-21 17:48:06 -07:00
} else if ( [ specifier . type isEqualToString : kIASKButtonSpecifier ] ) {
NSString * value = [ self . settingsStore objectForKey : specifier . key ] ;
2017-04-11 16:15:18 -07:00
cell . textLabel . text = ( [ value isKindOfClass : NSString . class ] && [ self . settingsReader titleForId : value ] . length ) ? [ self . settingsReader titleForId : value ] : specifier . title ;
2015-03-03 15:56:32 -05:00
cell . detailTextLabel . text = specifier . subtitle ;
IASK_IF _IOS7 _OR _GREATER
2022-03-09 21:06:09 -07:00
( if ( specifier . isCritical ) {
cell . textLabel . textColor = UIColor . redColor ;
} else if ( specifier . textAlignment ! = NSTextAlignmentLeft ) {
2015-03-03 15:56:32 -05:00
cell . textLabel . textColor = tableView . tintColor ;
2022-03-09 21:06:09 -07:00
} ) ;
2015-03-03 15:56:32 -05:00
cell . textLabel . textAlignment = specifier . textAlignment ;
2014-05-19 13:33:42 -07:00
cell . accessoryType = ( specifier . textAlignment = = NSTextAlignmentLeft ) ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone ;
2015-03-03 15:56:32 -05:00
} else if ( [ specifier . type isEqualToString : kIASKPSRadioGroupSpecifier ] ) {
NSInteger index = [ specifier . multipleValues indexOfObject : specifier . radioGroupValue ] ;
2017-04-11 16:15:18 -07:00
cell . textLabel . text = [ self . settingsReader titleForId : specifier . multipleTitles [ index ] ] ;
2015-03-03 15:56:32 -05:00
[ _selections [ indexPath . section ] updateSelectionInCell : cell indexPath : indexPath ] ;
2013-06-21 17:48:06 -07:00
} else {
cell . textLabel . text = specifier . title ;
}
cell . imageView . image = specifier . cellImage ;
cell . imageView . highlightedImage = specifier . highlightedCellImage ;
2017-04-11 16:15:18 -07:00
if ( ! [ specifier . type isEqualToString : kIASKPSMultiValueSpecifier ] && ! [ specifier . type isEqualToString : kIASKPSTitleValueSpecifier ] && ! [ specifier . type isEqualToString : kIASKPSTextFieldSpecifier ] && ! [ specifier . type isEqualToString : kIASKTextViewSpecifier ] ) {
2013-06-21 17:48:06 -07:00
cell . textLabel . textAlignment = specifier . textAlignment ;
}
cell . detailTextLabel . textAlignment = specifier . textAlignment ;
cell . textLabel . adjustsFontSizeToFitWidth = specifier . adjustsFontSizeToFitWidth ;
cell . detailTextLabel . adjustsFontSizeToFitWidth = specifier . adjustsFontSizeToFitWidth ;
return cell ;
}
- ( NSIndexPath * ) tableView : ( UITableView * ) tableView willSelectRowAtIndexPath : ( NSIndexPath * ) indexPath {
// create a set of specifier types that can ' t be selected
static NSSet * noSelectionTypes = nil ;
if ( nil = = noSelectionTypes ) {
2013-10-08 16:00:55 -07:00
noSelectionTypes = [ NSSet setWithObjects : kIASKPSToggleSwitchSpecifier , kIASKPSSliderSpecifier , nil ] ;
2013-06-21 17:48:06 -07:00
}
IASKSpecifier * specifier = [ self . settingsReader specifierForIndexPath : indexPath ] ;
if ( [ noSelectionTypes containsObject : specifier . type ] ) {
return nil ;
} else {
return indexPath ;
}
}
- ( void ) tableView : ( UITableView * ) tableView didSelectRowAtIndexPath : ( NSIndexPath * ) indexPath {
IASKSpecifier * specifier = [ self . settingsReader specifierForIndexPath : indexPath ] ;
// switches and sliders can ' t be selected ( should be captured by tableView : willSelectRowAtIndexPath : delegate method )
assert ( ! [ [ specifier type ] isEqualToString : kIASKPSToggleSwitchSpecifier ] ) ;
assert ( ! [ [ specifier type ] isEqualToString : kIASKPSSliderSpecifier ] ) ;
2013-10-08 16:00:55 -07:00
2013-06-21 17:48:06 -07:00
if ( [ [ specifier type ] isEqualToString : kIASKPSMultiValueSpecifier ] ) {
2013-10-08 16:00:55 -07:00
IASKSpecifierValuesViewController * targetViewController = [ [ IASKSpecifierValuesViewController alloc ] init ] ;
2013-06-21 17:48:06 -07:00
[ targetViewController setCurrentSpecifier : specifier ] ;
targetViewController . settingsReader = self . settingsReader ;
targetViewController . settingsStore = self . settingsStore ;
2015-03-03 15:56:32 -05:00
IASK_IF _IOS7 _OR _GREATER ( targetViewController . view . tintColor = self . view . tintColor ; )
2013-10-08 16:00:55 -07:00
_currentChildViewController = targetViewController ;
2013-06-21 17:48:06 -07:00
[ [ self navigationController ] pushViewController : targetViewController animated : YES ] ;
2013-10-08 16:00:55 -07:00
} else if ( [ [ specifier type ] isEqualToString : kIASKPSTextFieldSpecifier ] ) {
IASKPSTextFieldSpecifierViewCell * textFieldCell = ( id ) [ tableView cellForRowAtIndexPath : indexPath ] ;
2017-04-11 16:15:18 -07:00
[ textFieldCell . textField becomeFirstResponder ] ;
} else if ( [ [ specifier type ] isEqualToString : kIASKPSChildPaneSpecifier ] ) {
2014-05-19 13:33:42 -07:00
if ( [ specifier viewControllerStoryBoardID ] ) {
NSString * storyBoardFileFromSpecifier = [ specifier viewControllerStoryBoardFile ] ;
2015-03-03 15:56:32 -05:00
storyBoardFileFromSpecifier = storyBoardFileFromSpecifier && storyBoardFileFromSpecifier . length > 0 ? storyBoardFileFromSpecifier : [ [ NSBundle mainBundle ] . infoDictionary objectForKey : @ "UIMainStoryboardFile" ] ;
2014-05-19 13:33:42 -07:00
UIStoryboard * storyBoard = [ UIStoryboard storyboardWithName : storyBoardFileFromSpecifier bundle : nil ] ;
UIViewController * vc = [ storyBoard instantiateViewControllerWithIdentifier : [ specifier viewControllerStoryBoardID ] ] ;
2015-03-03 15:56:32 -05:00
IASK_IF _IOS7 _OR _GREATER ( vc . view . tintColor = self . view . tintColor ; )
2014-05-19 13:33:42 -07:00
[ self . navigationController pushViewController : vc animated : YES ] ;
return ;
}
2013-06-21 17:48:06 -07:00
Class vcClass = [ specifier viewControllerClass ] ;
if ( vcClass ) {
2017-04-11 16:15:18 -07:00
if ( vcClass = = [ NSNull class ] ) {
NSLog ( @ "class '%@' not found" , [ specifier localizedObjectForKey : kIASKViewControllerClass ] ) ;
[ tableView deselectRowAtIndexPath : indexPath animated : YES ] ;
return ;
}
2013-06-21 17:48:06 -07:00
SEL initSelector = [ specifier viewControllerSelector ] ;
if ( ! initSelector ) {
initSelector = @ selector ( init ) ;
}
2013-10-08 16:00:55 -07:00
UIViewController * vc = [ vcClass alloc ] ;
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Warc-performSelector-leaks"
2014-05-19 13:33:42 -07:00
vc = [ vc performSelector : initSelector withObject : [ specifier file ] withObject : specifier ] ;
2013-10-08 16:00:55 -07:00
# pragma clang diagnostic pop
if ( [ vc respondsToSelector : @ selector ( setDelegate : ) ] ) {
[ vc performSelector : @ selector ( setDelegate : ) withObject : self . delegate ] ;
}
if ( [ vc respondsToSelector : @ selector ( setSettingsStore : ) ] ) {
[ vc performSelector : @ selector ( setSettingsStore : ) withObject : self . settingsStore ] ;
}
2015-03-03 15:56:32 -05:00
IASK_IF _IOS7 _OR _GREATER ( vc . view . tintColor = self . view . tintColor ; )
2013-06-21 17:48:06 -07:00
[ self . navigationController pushViewController : vc animated : YES ] ;
return ;
2017-04-11 16:15:18 -07:00
}
NSString * segueIdentifier = [ specifier segueIdentifier ] ;
if ( segueIdentifier ) {
@ try {
[ self performSegueWithIdentifier : segueIdentifier sender : self ] ;
} @ catch ( NSException * exception ) {
NSLog ( @ "segue with identifier '%@' not defined" , segueIdentifier ) ;
[ tableView deselectRowAtIndexPath : indexPath animated : YES ] ;
}
return ;
2013-06-21 17:48:06 -07:00
}
if ( nil = = [ specifier file ] ) {
[ tableView deselectRowAtIndexPath : indexPath animated : YES ] ;
return ;
}
2013-10-08 16:00:55 -07:00
2014-05-19 13:33:42 -07:00
_reloadDisabled = YES ; // Disable internal unnecessary reloads
2013-10-08 16:00:55 -07:00
IASKAppSettingsViewController * targetViewController = [ [ [ self class ] alloc ] init ] ;
targetViewController . showDoneButton = NO ;
2014-05-19 13:33:42 -07:00
targetViewController . showCreditsFooter = NO ; // Does not reload the tableview ( but next setters do it )
2013-10-08 16:00:55 -07:00
targetViewController . delegate = self . delegate ;
2014-05-19 13:33:42 -07:00
targetViewController . settingsStore = self . settingsStore ;
2013-10-08 16:00:55 -07:00
targetViewController . file = specifier . file ;
targetViewController . hiddenKeys = self . hiddenKeys ;
targetViewController . title = specifier . title ;
2015-03-03 15:56:32 -05:00
IASK_IF _IOS7 _OR _GREATER ( targetViewController . view . tintColor = self . view . tintColor ; )
2013-10-08 16:00:55 -07:00
_currentChildViewController = targetViewController ;
2014-05-19 13:33:42 -07:00
_reloadDisabled = NO ;
2015-03-03 15:56:32 -05:00
2013-06-21 17:48:06 -07:00
[ [ self navigationController ] pushViewController : targetViewController animated : YES ] ;
2014-05-19 13:33:42 -07:00
2013-06-21 17:48:06 -07:00
} else if ( [ [ specifier type ] isEqualToString : kIASKOpenURLSpecifier ] ) {
[ tableView deselectRowAtIndexPath : indexPath animated : YES ] ;
2017-04-11 16:15:18 -07:00
[ [ UIApplication sharedApplication ] openURL : [ NSURL URLWithString : [ specifier localizedObjectForKey : kIASKFile ] ] options : @ { } completionHandler : nil ] ;
2013-06-21 17:48:06 -07:00
} else if ( [ [ specifier type ] isEqualToString : kIASKButtonSpecifier ] ) {
[ tableView deselectRowAtIndexPath : indexPath animated : YES ] ;
2013-10-08 16:00:55 -07:00
if ( [ self . delegate respondsToSelector : @ selector ( settingsViewController : buttonTappedForSpecifier : ) ] ) {
[ self . delegate settingsViewController : self buttonTappedForSpecifier : specifier ] ;
} else if ( [ self . delegate respondsToSelector : @ selector ( settingsViewController : buttonTappedForKey : ) ] ) {
// deprecated , provided for backward compatibility
NSLog ( @ "InAppSettingsKit Warning: -settingsViewController:buttonTappedForKey: is deprecated. Please use -settingsViewController:buttonTappedForSpecifier:" ) ;
2015-09-16 16:53:07 -07:00
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
2013-10-08 16:00:55 -07:00
[ self . delegate settingsViewController : self buttonTappedForKey : [ specifier key ] ] ;
2015-09-16 16:53:07 -07:00
# pragma clang diagnostic pop
2013-10-08 16:00:55 -07:00
} else {
// legacy code , provided for backward compatibility
// the delegate mechanism above is much cleaner and doesn ' t leak
Class buttonClass = [ specifier buttonClass ] ;
SEL buttonAction = [ specifier buttonAction ] ;
if ( [ buttonClass respondsToSelector : buttonAction ] ) {
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[ buttonClass performSelector : buttonAction withObject : self withObject : [ specifier key ] ] ;
# pragma clang diagnostic pop
NSLog ( @ "InAppSettingsKit Warning: Using IASKButtonSpecifier without implementing the delegate method is deprecated" ) ;
}
}
2013-06-21 17:48:06 -07:00
} else if ( [ [ specifier type ] isEqualToString : kIASKMailComposeSpecifier ] ) {
[ tableView deselectRowAtIndexPath : indexPath animated : YES ] ;
if ( [ MFMailComposeViewController canSendMail ] ) {
MFMailComposeViewController * mailViewController = [ [ MFMailComposeViewController alloc ] init ] ;
mailViewController . navigationBar . barStyle = self . navigationController . navigationBar . barStyle ;
2015-03-03 15:56:32 -05:00
IASK_IF _IOS7 _OR _GREATER ( mailViewController . navigationBar . tintColor = self . navigationController . navigationBar . tintColor ; ) ;
2014-05-19 13:33:42 -07:00
mailViewController . navigationBar . titleTextAttributes = self . navigationController . navigationBar . titleTextAttributes ;
2013-10-08 16:00:55 -07:00
2013-06-21 17:48:06 -07:00
if ( [ specifier localizedObjectForKey : kIASKMailComposeSubject ] ) {
[ mailViewController setSubject : [ specifier localizedObjectForKey : kIASKMailComposeSubject ] ] ;
}
if ( [ [ specifier specifierDict ] objectForKey : kIASKMailComposeToRecipents ] ) {
[ mailViewController setToRecipients : [ [ specifier specifierDict ] objectForKey : kIASKMailComposeToRecipents ] ] ;
}
if ( [ [ specifier specifierDict ] objectForKey : kIASKMailComposeCcRecipents ] ) {
[ mailViewController setCcRecipients : [ [ specifier specifierDict ] objectForKey : kIASKMailComposeCcRecipents ] ] ;
}
if ( [ [ specifier specifierDict ] objectForKey : kIASKMailComposeBccRecipents ] ) {
[ mailViewController setBccRecipients : [ [ specifier specifierDict ] objectForKey : kIASKMailComposeBccRecipents ] ] ;
}
if ( [ specifier localizedObjectForKey : kIASKMailComposeBody ] ) {
BOOL isHTML = NO ;
if ( [ [ specifier specifierDict ] objectForKey : kIASKMailComposeBodyIsHTML ] ) {
isHTML = [ [ [ specifier specifierDict ] objectForKey : kIASKMailComposeBodyIsHTML ] boolValue ] ;
}
2013-10-08 16:00:55 -07:00
if ( [ self . delegate respondsToSelector : @ selector ( settingsViewController : mailComposeBodyForSpecifier : ) ] ) {
2013-06-21 17:48:06 -07:00
[ mailViewController setMessageBody : [ self . delegate settingsViewController : self
mailComposeBodyForSpecifier : specifier ] isHTML : isHTML ] ;
}
else {
[ mailViewController setMessageBody : [ specifier localizedObjectForKey : kIASKMailComposeBody ] isHTML : isHTML ] ;
}
}
2013-10-08 16:00:55 -07:00
2013-06-21 17:48:06 -07:00
UIViewController < MFMailComposeViewControllerDelegate > * vc = nil ;
2013-10-08 16:00:55 -07:00
if ( [ self . delegate respondsToSelector : @ selector ( settingsViewController : viewControllerForMailComposeViewForSpecifier : ) ] ) {
vc = [ self . delegate settingsViewController : self viewControllerForMailComposeViewForSpecifier : specifier ] ;
2013-06-21 17:48:06 -07:00
}
if ( vc = = nil ) {
vc = self ;
}
mailViewController . mailComposeDelegate = vc ;
2013-10-08 16:00:55 -07:00
_currentChildViewController = mailViewController ;
2020-09-23 21:30:31 -07:00
// UIStatusBarStyle savedStatusBarStyle = [ UIApplication sharedApplication ] . statusBarStyle ;
2014-05-19 13:33:42 -07:00
[ vc presentViewController : mailViewController animated : YES completion : ^ {
2020-09-23 21:30:31 -07:00
// [ UIApplication sharedApplication ] . statusBarStyle = savedStatusBarStyle ;
2014-05-19 13:33:42 -07:00
} ] ;
2013-06-21 17:48:06 -07:00
} else {
2017-04-11 16:15:18 -07:00
IASK_IF _PRE _IOS8
(
2013-06-21 17:48:06 -07:00
UIAlertView * alert = [ [ UIAlertView alloc ]
initWithTitle : NSLocalizedString ( @ "Mail not configured" , @ "InAppSettingsKit" )
message : NSLocalizedString ( @ "This device is not configured for sending Email. Please configure the Mail settings in the Settings app." , @ "InAppSettingsKit" )
delegate : nil
cancelButtonTitle : NSLocalizedString ( @ "OK" , @ "InAppSettingsKit" )
otherButtonTitles : nil ] ;
[ alert show ] ;
2017-04-11 16:15:18 -07:00
)
IASK_IF _IOS8 _OR _GREATER
(
UIAlertController * alert = [ UIAlertController alertControllerWithTitle : NSLocalizedString ( @ "Mail not configured" , @ "InAppSettingsKit" )
message : NSLocalizedString ( @ "This device is not configured for sending Email. Please configure the Mail settings in the Settings app." , @ "InAppSettingsKit" )
preferredStyle : UIAlertControllerStyleAlert ] ;
[ alert addAction : [ UIAlertAction actionWithTitle : NSLocalizedString ( @ "OK" , @ "InAppSettingsKit" ) style : UIAlertActionStyleCancel handler : ^ ( UIAlertAction * action ) { } ] ] ;
[ self presentViewController : alert animated : YES completion : nil ] ;
)
2013-06-21 17:48:06 -07:00
}
2017-04-11 16:15:18 -07:00
2013-10-08 16:00:55 -07:00
} else if ( [ [ specifier type ] isEqualToString : kIASKCustomViewSpecifier ] && [ self . delegate respondsToSelector : @ selector ( settingsViewController : tableView : didSelectCustomViewSpecifier : ) ] ) {
2013-06-21 17:48:06 -07:00
[ self . delegate settingsViewController : self tableView : tableView didSelectCustomViewSpecifier : specifier ] ;
2015-03-03 15:56:32 -05:00
} else if ( [ [ specifier type ] isEqualToString : kIASKPSRadioGroupSpecifier ] ) {
[ _selections [ indexPath . section ] selectRowAtIndexPath : indexPath ] ;
2013-06-21 17:48:06 -07:00
} else {
[ tableView deselectRowAtIndexPath : indexPath animated : NO ] ;
}
}
# pragma mark -
# pragma mark MFMailComposeViewControllerDelegate Function
- ( void ) mailComposeController : ( MFMailComposeViewController * ) controller didFinishWithResult : ( MFMailComposeResult ) result error : ( NSError * ) error {
// Forward the mail compose delegate
if ( [ self . delegate respondsToSelector : @ selector ( settingsViewController : mailComposeController : didFinishWithResult : error : ) ] ) {
[ self . delegate settingsViewController : self
mailComposeController : controller
didFinishWithResult : result
error : error ] ;
}
2013-10-08 16:00:55 -07:00
[ self dismissViewControllerAnimated : YES
completion : nil ] ;
2013-06-21 17:48:06 -07:00
}
# pragma mark -
# pragma mark UITextFieldDelegate Functions
2017-04-11 16:15:18 -07:00
- ( void ) textFieldDidBeginEditing : ( UITextField * ) textField {
2013-06-21 17:48:06 -07:00
self . currentFirstResponder = textField ;
}
- ( void ) _textChanged : ( id ) sender {
2013-10-08 16:00:55 -07:00
IASKTextField * text = sender ;
2013-06-21 17:48:06 -07:00
[ _settingsStore setObject : [ text text ] forKey : [ text key ] ] ;
[ [ NSNotificationCenter defaultCenter ] postNotificationName : kIASKAppSettingChanged
2017-04-11 16:15:18 -07:00
object : self
2013-06-21 17:48:06 -07:00
userInfo : [ NSDictionary dictionaryWithObject : [ text text ]
forKey : [ text key ] ] ] ;
}
- ( BOOL ) textFieldShouldReturn : ( UITextField * ) textField {
[ textField resignFirstResponder ] ;
self . currentFirstResponder = nil ;
return YES ;
}
- ( void ) singleTapToEndEdit : ( UIGestureRecognizer * ) sender {
[ self . tableView endEditing : NO ] ;
}
2017-04-11 16:15:18 -07:00
# pragma mark - UITextViewDelegate
- ( void ) textViewDidEndEditing : ( UITextView * ) textView {
self . currentFirstResponder = textView ;
}
- ( void ) textViewDidChange : ( IASKTextView * ) textView {
[ self cacheRowHeightForTextView : textView ] ;
CGRect visibleTableRect = UIEdgeInsetsInsetRect ( self . tableView . bounds , self . tableView . contentInset ) ;
NSIndexPath * indexPath = [ self . settingsReader indexPathForKey : textView . key ] ;
CGRect cellFrame = [ self . tableView rectForRowAtIndexPath : indexPath ] ;
if ( ! CGRectContainsRect ( visibleTableRect , cellFrame ) ) {
[ self . tableView scrollRectToVisible : CGRectInset ( cellFrame , 0 , - 30 ) animated : YES ] ;
}
[ _settingsStore setObject : textView . text forKey : textView . key ] ;
[ [ NSNotificationCenter defaultCenter ] postNotificationName : kIASKAppSettingChanged
object : textView . key
userInfo : @ { textView . key : textView . text } ] ;
}
- ( void ) cacheRowHeightForTextView : ( IASKTextView * ) textView {
CGFloat maxHeight = self . tableView . bounds . size . height - self . tableView . contentInset . top - self . tableView . contentInset . bottom - 60 ;
CGFloat contentHeight = [ textView sizeThatFits : CGSizeMake ( textView . frame . size . width , 10000 ) ] . height + 16 ;
self . rowHeights [ textView . key ] = @ ( MAX ( 44 , MIN ( maxHeight , contentHeight ) ) ) ;
textView . scrollEnabled = contentHeight > maxHeight ;
[ self . tableView beginUpdates ] ;
[ self . tableView endUpdates ] ;
}
2013-06-21 17:48:06 -07:00
# pragma mark Notifications
- ( void ) synchronizeSettings {
[ _settingsStore synchronize ] ;
}
static NSDictionary * oldUserDefaults = nil ;
- ( void ) userDefaultsDidChange {
2015-03-03 15:56:32 -05:00
dispatch_after ( dispatch_time ( DISPATCH_TIME _NOW , ( int64_t ) ( 0.5 * NSEC_PER _SEC ) ) , dispatch_get _main _queue ( ) , ^ {
2015-09-16 16:53:07 -07:00
IASKSettingsStoreUserDefaults * udSettingsStore = ( id ) self . settingsStore ;
NSDictionary * currentDict = udSettingsStore . defaults . dictionaryRepresentation ;
2015-03-03 15:56:32 -05:00
NSMutableArray * indexPathsToUpdate = [ NSMutableArray array ] ;
for ( NSString * key in currentDict . allKeys ) {
if ( oldUserDefaults && ! [ [ oldUserDefaults valueForKey : key ] isEqual : [ currentDict valueForKey : key ] ] ) {
NSIndexPath * path = [ self . settingsReader indexPathForKey : key ] ;
if ( path && ! [ [ self . settingsReader specifierForKey : key ] . type isEqualToString : kIASKCustomViewSpecifier ] && [ self . tableView . indexPathsForVisibleRows containsObject : path ] ) {
[ indexPathsToUpdate addObject : path ] ;
}
2013-06-21 17:48:06 -07:00
}
}
2015-03-03 15:56:32 -05:00
oldUserDefaults = currentDict ;
for ( UITableViewCell * cell in self . tableView . visibleCells ) {
if ( [ cell isKindOfClass : [ IASKPSTextFieldSpecifierViewCell class ] ] && [ ( ( IASKPSTextFieldSpecifierViewCell * ) cell ) . textField isFirstResponder ] ) {
[ indexPathsToUpdate removeObject : [ self . tableView indexPathForCell : cell ] ] ;
}
2013-06-21 17:48:06 -07:00
}
2015-03-03 15:56:32 -05:00
if ( indexPathsToUpdate . count ) {
[ self . tableView reloadRowsAtIndexPaths : indexPathsToUpdate withRowAnimation : UITableViewRowAnimationAutomatic ] ;
}
} ) ;
}
- ( void ) didChangeSettingViaIASK : ( NSNotification * ) notification {
2017-04-11 16:15:18 -07:00
NSString * key = notification . userInfo . allKeys . firstObject ;
[ oldUserDefaults setValue : notification . userInfo [ key ] forKey : key ] ;
2013-06-21 17:48:06 -07:00
}
- ( void ) reload {
// wait 0.5 sec until UI is available after applicationWillEnterForeground
[ self . tableView performSelector : @ selector ( reloadData ) withObject : nil afterDelay : 0.5 ] ;
}
# pragma mark CGRect Utility function
CGRect IASKCGRectSwap ( CGRect rect ) {
CGRect newRect ;
newRect . origin . x = rect . origin . y ;
newRect . origin . y = rect . origin . x ;
newRect . size . width = rect . size . height ;
newRect . size . height = rect . size . width ;
return newRect ;
}
@ end