diff --git a/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertBackground.png b/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertBackground.png new file mode 100755 index 0000000..57d8cc9 Binary files /dev/null and b/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertBackground.png differ diff --git a/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertBackground@2x.png b/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertBackground@2x.png new file mode 100755 index 0000000..c2b7c66 Binary files /dev/null and b/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertBackground@2x.png differ diff --git a/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertButton.png b/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertButton.png new file mode 100755 index 0000000..1cccd89 Binary files /dev/null and b/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertButton.png differ diff --git a/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertButton@2x.png b/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertButton@2x.png new file mode 100755 index 0000000..a1d3d2a Binary files /dev/null and b/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertButton@2x.png differ diff --git a/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertButtonPressed.png b/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertButtonPressed.png new file mode 100755 index 0000000..5094083 Binary files /dev/null and b/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertButtonPressed.png differ diff --git a/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertButtonPressed@2x.png b/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertButtonPressed@2x.png new file mode 100755 index 0000000..8ae06dc Binary files /dev/null and b/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertButtonPressed@2x.png differ diff --git a/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertShadowMask.png b/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertShadowMask.png new file mode 100755 index 0000000..d0e3c5d Binary files /dev/null and b/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertShadowMask.png differ diff --git a/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertShadowMask@2x.png b/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertShadowMask@2x.png new file mode 100755 index 0000000..ca2d8ec Binary files /dev/null and b/RedmineMobile/Libs/MLTableAlert/Images/MLTableAlertShadowMask@2x.png differ diff --git a/RedmineMobile/Libs/MLTableAlert/MLTableAlert.h b/RedmineMobile/Libs/MLTableAlert/MLTableAlert.h new file mode 100755 index 0000000..3e3165a --- /dev/null +++ b/RedmineMobile/Libs/MLTableAlert/MLTableAlert.h @@ -0,0 +1,47 @@ +// +// MLTableAlert.h +// +// Version 1.0 +// +// Created by Matteo Del Vecchio on 11/12/12. +// Copyright (c) 2012 Matthew Labs. All rights reserved. +// For the complete copyright notice, read Source Code License. +// + +#import +#import + +@class MLTableAlert; + + +// Blocks definition for table view management +typedef NSInteger (^MLTableAlertNumberOfRowsBlock)(NSInteger section); +typedef UITableViewCell* (^MLTableAlertTableCellsBlock)(MLTableAlert *alert, NSIndexPath *indexPath); +typedef void (^MLTableAlertRowSelectionBlock)(NSIndexPath *selectedIndex); +typedef void (^MLTableAlertCompletionBlock)(void); + + +@interface MLTableAlert : UIView + +@property (nonatomic, strong) UITableView *table; + +@property (nonatomic, assign) CGFloat height; + +@property (nonatomic, strong) MLTableAlertCompletionBlock completionBlock; // Called when Cancel button pressed +@property (nonatomic, strong) MLTableAlertRowSelectionBlock selectionBlock; // Called when a row in table view is pressed + + +// Classe method; rowsBlock and cellsBlock MUST NOT be nil ++(MLTableAlert *)tableAlertWithTitle:(NSString *)title cancelButtonTitle:(NSString *)cancelBtnTitle numberOfRows:(MLTableAlertNumberOfRowsBlock)rowsBlock andCells:(MLTableAlertTableCellsBlock)cellsBlock; + +// Initialization method; rowsBlock and cellsBlock MUST NOT be nil +-(id)initWithTitle:(NSString *)title cancelButtonTitle:(NSString *)cancelBtnTitle numberOfRows:(MLTableAlertNumberOfRowsBlock)rowsBlock andCells:(MLTableAlertTableCellsBlock)cellsBlock; + +// Allows you to perform custom actions when a row is selected or the cancel button is pressed +-(void)configureSelectionBlock:(MLTableAlertRowSelectionBlock)selBlock andCompletionBlock:(MLTableAlertCompletionBlock)comBlock; + +// Show the alert +-(void)show; + +@end + diff --git a/RedmineMobile/Libs/MLTableAlert/MLTableAlert.m b/RedmineMobile/Libs/MLTableAlert/MLTableAlert.m new file mode 100755 index 0000000..75e1050 --- /dev/null +++ b/RedmineMobile/Libs/MLTableAlert/MLTableAlert.m @@ -0,0 +1,339 @@ +// +// MLTableAlert.m +// +// Version 1.0 +// +// Created by Matteo Del Vecchio on 11/12/12. +// Copyright (c) 2012 Matthew Labs. All rights reserved. +// For the complete copyright notice, read Source Code License. +// + +#import "MLTableAlert.h" + + +#define kTableAlertWidth 284.0 +#define kLateralInset 12.0 +#define kVerticalInset 8.0 +#define kMinAlertHeight 264.0 +#define kCancelButtonHeight 44.0 +#define kCancelButtonMargin 5.0 +#define kTitleLabelMargin 12.0 + + +// Since orientation is managed by view controllers, +// MLTableAlertController is used under the MLTableAlert +// to provide support for orientation and rotation +@interface MLTableAlertController : UIViewController +@end + +@implementation MLTableAlertController + +// Orientation support +-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation +{ + if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) + return YES; + else + return (toInterfaceOrientation == UIInterfaceOrientationPortrait); +} + +-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration +{ + MLTableAlert *ta = [self.view.subviews lastObject]; + if (ta != nil && [ta isKindOfClass:[MLTableAlert class]]) + { + // Rotate the MLTableAlert if orientation changes + // when it is visible on screen + [UIView animateWithDuration:duration animations:^{ + [ta sizeToFit]; + + CGFloat x = CGRectGetMidX(self.view.bounds); + CGFloat y = CGRectGetMidY(self.view.bounds); + ta.center = CGPointMake(x, y); + ta.frame = CGRectIntegral(ta.frame); + }]; + } + else + return; +} + +@end + + +@interface MLTableAlert () +@property (nonatomic, strong) UIView *alertBg; +@property (nonatomic, strong) UILabel *titleLabel; +@property (nonatomic, strong) UIButton *cancelButton; +@property (nonatomic, strong) UIWindow *appWindow; + +@property (nonatomic, strong) NSString *title; +@property (nonatomic, strong) NSString *cancelButtonTitle; + +@property (nonatomic) BOOL cellSelected; + +@property (nonatomic, strong) MLTableAlertNumberOfRowsBlock numberOfRows; +@property (nonatomic, strong) MLTableAlertTableCellsBlock cells; + +-(void)createBackgroundView; // Draws and manages the view behind the alert +-(void)animateIn; // Animates the alert when it has to be shown +-(void)animateOut; // Animates the alert when it has to be dismissed +-(void)dismissTableAlert; // Dismisses the alert +@end + + + +@implementation MLTableAlert + +#pragma mark - MLTableAlert Class Method + ++(MLTableAlert *)tableAlertWithTitle:(NSString *)title cancelButtonTitle:(NSString *)cancelBtnTitle numberOfRows:(MLTableAlertNumberOfRowsBlock)rowsBlock andCells:(MLTableAlertTableCellsBlock)cellsBlock +{ + return [[self alloc] initWithTitle:title cancelButtonTitle:cancelBtnTitle numberOfRows:rowsBlock andCells:cellsBlock]; +} + +#pragma mark - MLTableAlert Initialization + +-(id)initWithTitle:(NSString *)title cancelButtonTitle:(NSString *)cancelButtonTitle numberOfRows:(MLTableAlertNumberOfRowsBlock)rowsBlock andCells:(MLTableAlertTableCellsBlock)cellsBlock +{ + // Throw exception if rowsBlock or cellsBlock is nil + if (rowsBlock == nil || cellsBlock == nil) + { + [[NSException exceptionWithName:@"rowsBlock and cellsBlock Error" reason:@"These blocks MUST NOT be nil" userInfo:nil] raise]; + return nil; + } + + self = [super init]; + if (self) + { + _numberOfRows = rowsBlock; + _cells = cellsBlock; + _title = title; + _cancelButtonTitle = cancelButtonTitle; + _height = kMinAlertHeight; // Defining default (and minimum) alert height + } + + return self; +} + +#pragma mark - Actions + +-(void)configureSelectionBlock:(MLTableAlertRowSelectionBlock)selBlock andCompletionBlock:(MLTableAlertCompletionBlock)comBlock +{ + self.selectionBlock = selBlock; + self.completionBlock = comBlock; +} + +-(void)createBackgroundView +{ + // reset cellSelected value + self.cellSelected = NO; + + // Allocating controller for presenting MLTableAlert + MLTableAlertController *controller = [[MLTableAlertController alloc] init]; + controller.view.backgroundColor = [UIColor clearColor]; + + // Creating new UIWindow to manage MLTableAlert and MLTableAlertController + self.appWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + self.appWindow.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.4]; + self.appWindow.rootViewController = controller; + self.appWindow.alpha = 0.0; + self.appWindow.windowLevel = UIWindowLevelStatusBar; + self.appWindow.hidden = NO; + [self.appWindow makeKeyAndVisible]; + + // Adding MLTableAlert as subview of MLTableAlertController (controller) + [controller.view addSubview:self]; + + // setting options to MLTableAlert + self.frame = self.superview.bounds; + self.opaque = NO; + + // get background color darker + [UIView animateWithDuration:0.2 animations:^{ + self.appWindow.alpha = 1.0; + }]; +} + +-(void)animateIn +{ + // UIAlertView-like pop in animation + self.alertBg.transform = CGAffineTransformMakeScale(0.6, 0.6); + [UIView animateWithDuration:0.2 animations:^{ + self.alertBg.transform = CGAffineTransformMakeScale(1.1, 1.1); + } completion:^(BOOL finished){ + [UIView animateWithDuration:1.0/15.0 animations:^{ + self.alertBg.transform = CGAffineTransformMakeScale(0.9, 0.9); + } completion:^(BOOL finished){ + [UIView animateWithDuration:1.0/7.5 animations:^{ + self.alertBg.transform = CGAffineTransformIdentity; + }]; + }]; + }]; +} + +-(void)animateOut +{ + [UIView animateWithDuration:1.0/7.5 animations:^{ + self.alertBg.transform = CGAffineTransformMakeScale(0.9, 0.9); + } completion:^(BOOL finished) { + [UIView animateWithDuration:1.0/15.0 animations:^{ + self.alertBg.transform = CGAffineTransformMakeScale(1.1, 1.1); + } completion:^(BOOL finished) { + [UIView animateWithDuration:0.3 animations:^{ + self.alertBg.transform = CGAffineTransformMakeScale(0.01, 0.01); + self.appWindow.alpha = 0.3; + } completion:^(BOOL finished){ + // table alert not shown anymore + [self removeFromSuperview]; + self.appWindow.hidden = YES; + [self.appWindow resignKeyWindow]; + }]; + }]; + }]; +} + +-(void)show +{ + [self createBackgroundView]; + + // alert view creation + self.alertBg = [[UIView alloc] initWithFrame:CGRectZero]; + [self addSubview:self.alertBg]; + + // setting alert background image + UIImageView *alertBgImage = [[UIImageView alloc] initWithImage:[[UIImage imageNamed:@"MLTableAlertBackground.png"] stretchableImageWithLeftCapWidth:15 topCapHeight:30]]; + [self.alertBg addSubview:alertBgImage]; + + // alert title creation + self.titleLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + self.titleLabel.backgroundColor = [UIColor clearColor]; + self.titleLabel.textColor = [UIColor whiteColor]; + self.titleLabel.shadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.75]; + self.titleLabel.shadowOffset = CGSizeMake(0, -1); + self.titleLabel.font = [UIFont boldSystemFontOfSize:18.0]; + self.titleLabel.frame = CGRectMake(kLateralInset, 15, kTableAlertWidth - kLateralInset * 2, 22); + self.titleLabel.text = self.title; + self.titleLabel.textAlignment = NSTextAlignmentCenter; + [self.alertBg addSubview:self.titleLabel]; + + // table view creation + self.table = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; + self.table.frame = CGRectMake(kLateralInset, self.titleLabel.frame.origin.y + self.titleLabel.frame.size.height + kTitleLabelMargin, kTableAlertWidth - kLateralInset * 2, (self.height - kVerticalInset * 2) - self.titleLabel.frame.origin.y - self.titleLabel.frame.size.height - kTitleLabelMargin - kCancelButtonMargin - kCancelButtonHeight); + self.table.layer.cornerRadius = 6.0; + self.table.layer.masksToBounds = YES; + self.table.delegate = self; + self.table.dataSource = self; + self.table.separatorStyle = UITableViewCellSeparatorStyleNone; + self.table.backgroundView = [[UIView alloc] init]; + [self.alertBg addSubview:self.table]; + + // setting white-to-gray gradient as table view's background + CAGradientLayer *tableGradient = [CAGradientLayer layer]; + tableGradient.frame = CGRectMake(0, 0, self.table.frame.size.width, self.table.frame.size.height); + tableGradient.colors = [NSArray arrayWithObjects:(id)[[UIColor colorWithRed:240.0/255.0 green:240.0/255.0 blue:240.0/255.0 alpha:1.0] CGColor], (id)[[UIColor colorWithRed:174.0/255.0 green:174.0/255.0 blue:174.0/255.0 alpha:1.0] CGColor], nil]; + [self.table.backgroundView.layer insertSublayer:tableGradient atIndex:0]; + + // adding inner shadow mask on table view + UIImageView *maskShadow = [[UIImageView alloc] initWithImage:[[UIImage imageNamed:@"MLTableAlertShadowMask.png"] stretchableImageWithLeftCapWidth:6 topCapHeight:7]]; + maskShadow.userInteractionEnabled = NO; + maskShadow.layer.masksToBounds = YES; + maskShadow.layer.cornerRadius = 5.0; + maskShadow.frame = self.table.frame; + [self.alertBg addSubview:maskShadow]; + + // cancel button creation + self.cancelButton = [UIButton buttonWithType:UIButtonTypeCustom]; + self.cancelButton.frame = CGRectMake(kLateralInset, self.table.frame.origin.y + self.table.frame.size.height + kCancelButtonMargin, kTableAlertWidth - kLateralInset * 2, kCancelButtonHeight); + self.cancelButton.titleLabel.textAlignment = NSTextAlignmentCenter; + self.cancelButton.titleLabel.font = [UIFont boldSystemFontOfSize:17.0]; + self.cancelButton.titleLabel.shadowOffset = CGSizeMake(0, -1); + self.cancelButton.titleLabel.shadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.75]; + [self.cancelButton setTitle:self.cancelButtonTitle forState:UIControlStateNormal]; + [self.cancelButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; + [self.cancelButton setBackgroundColor:[UIColor clearColor]]; + [self.cancelButton setBackgroundImage:[[UIImage imageNamed:@"MLTableAlertButton.png"] stretchableImageWithLeftCapWidth:5 topCapHeight:0] forState:UIControlStateNormal]; + [self.cancelButton setBackgroundImage:[[UIImage imageNamed:@"MLTableAlertButtonPressed.png"] stretchableImageWithLeftCapWidth:5 topCapHeight:0] forState:UIControlStateHighlighted]; + self.cancelButton.opaque = NO; + self.cancelButton.layer.cornerRadius = 5.0; + [self.cancelButton addTarget:self action:@selector(dismissTableAlert) forControlEvents:UIControlEventTouchUpInside]; + [self.alertBg addSubview:self.cancelButton]; + + // setting alert and alert background image frames + self.alertBg.frame = CGRectMake((self.frame.size.width - kTableAlertWidth) / 2, (self.frame.size.height - self.height) / 2, kTableAlertWidth, self.height - kVerticalInset * 2); + alertBgImage.frame = CGRectMake(0.0, 0.0, kTableAlertWidth, self.height); + + // the alert will be the first responder so any other controls, + // like the keyboard, will be dismissed before the alert + [self becomeFirstResponder]; + + // show the alert with animation + [self animateIn]; +} + +-(void)dismissTableAlert +{ + // dismiss the alert with its animation + [self animateOut]; + + // perform actions contained in completionBlock if the + // cancel button of the alert has been pressed + // if completionBlock == nil, nothing is performed + if (self.completionBlock != nil) + if (!self.cellSelected) + self.completionBlock(); +} + +// Allows the alert to be first responder +-(BOOL)canBecomeFirstResponder +{ + return YES; +} + +// Alert height setter +-(void)setHeight:(CGFloat)height +{ + if (height > kMinAlertHeight) + _height = height; + else + _height = kMinAlertHeight; +} + +#pragma mark - UITableViewDataSource + +-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView +{ + // TODO: Allow multiple sections + return 1; +} + +-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + // according to the numberOfRows block code + return self.numberOfRows(section); +} + +-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + // according to the cells block code + return self.cells(self, indexPath); +} + +#pragma mark - UITableViewDelegate + +-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + [tableView deselectRowAtIndexPath:indexPath animated:YES]; + + // set cellSelected to YES so the completionBlock won't be + // executed because a cell has been pressed instead of the cancel button + self.cellSelected = YES; + // dismiss the alert + [self dismissTableAlert]; + + // perform actions contained in the selectionBlock if it isn't nil + // add pass the selected indexPath + if (self.selectionBlock != nil) + self.selectionBlock(indexPath); +} + +@end diff --git a/RedmineMobile/RedmineMobile.xcodeproj/project.pbxproj b/RedmineMobile/RedmineMobile.xcodeproj/project.pbxproj index f7a22d2..35f7bba 100644 --- a/RedmineMobile/RedmineMobile.xcodeproj/project.pbxproj +++ b/RedmineMobile/RedmineMobile.xcodeproj/project.pbxproj @@ -12,6 +12,16 @@ 2B62D0E31796A15C00AC3C19 /* OZLProjectCreateViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2B62D0E21796A15C00AC3C19 /* OZLProjectCreateViewController.storyboard */; }; 2B62D0E51796A8C800AC3C19 /* OZLIssueFilterViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2B62D0E41796A8C800AC3C19 /* OZLIssueFilterViewController.storyboard */; }; 2B62D0E81796A8D800AC3C19 /* OZLIssueFilterViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B62D0E71796A8D800AC3C19 /* OZLIssueFilterViewController.m */; }; + 2B6F382F1797A00A00D06F51 /* MLTableAlertBackground.png in Resources */ = {isa = PBXBuildFile; fileRef = 2B6F38251797A00A00D06F51 /* MLTableAlertBackground.png */; }; + 2B6F38301797A00A00D06F51 /* MLTableAlertBackground@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 2B6F38261797A00A00D06F51 /* MLTableAlertBackground@2x.png */; }; + 2B6F38311797A00A00D06F51 /* MLTableAlertButton.png in Resources */ = {isa = PBXBuildFile; fileRef = 2B6F38271797A00A00D06F51 /* MLTableAlertButton.png */; }; + 2B6F38321797A00A00D06F51 /* MLTableAlertButton@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 2B6F38281797A00A00D06F51 /* MLTableAlertButton@2x.png */; }; + 2B6F38331797A00A00D06F51 /* MLTableAlertButtonPressed.png in Resources */ = {isa = PBXBuildFile; fileRef = 2B6F38291797A00A00D06F51 /* MLTableAlertButtonPressed.png */; }; + 2B6F38341797A00A00D06F51 /* MLTableAlertButtonPressed@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 2B6F382A1797A00A00D06F51 /* MLTableAlertButtonPressed@2x.png */; }; + 2B6F38351797A00A00D06F51 /* MLTableAlertShadowMask.png in Resources */ = {isa = PBXBuildFile; fileRef = 2B6F382B1797A00A00D06F51 /* MLTableAlertShadowMask.png */; }; + 2B6F38361797A00A00D06F51 /* MLTableAlertShadowMask@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 2B6F382C1797A00A00D06F51 /* MLTableAlertShadowMask@2x.png */; }; + 2B6F38371797A00A00D06F51 /* MLTableAlert.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B6F382E1797A00A00D06F51 /* MLTableAlert.m */; }; + 2B6F38391797BEBA00D06F51 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2B6F38381797BEBA00D06F51 /* QuartzCore.framework */; }; 2B8A11E217963CE500906D34 /* OZLIssueDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B8A11E117963CE500906D34 /* OZLIssueDetailViewController.m */; }; 2B9968AB1794F71B0086F115 /* OZLModelIssueCategory.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B9968AA1794F71B0086F115 /* OZLModelIssueCategory.m */; }; 2B9968AF1794FC0A0086F115 /* OZLProjectCreateViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B9968AD1794FC0A0086F115 /* OZLProjectCreateViewController.m */; }; @@ -69,6 +79,17 @@ 2B62D0E41796A8C800AC3C19 /* OZLIssueFilterViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = OZLIssueFilterViewController.storyboard; path = ViewControllers/OZLIssueFilterViewController.storyboard; sourceTree = ""; }; 2B62D0E61796A8D800AC3C19 /* OZLIssueFilterViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OZLIssueFilterViewController.h; path = ViewControllers/OZLIssueFilterViewController.h; sourceTree = ""; }; 2B62D0E71796A8D800AC3C19 /* OZLIssueFilterViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OZLIssueFilterViewController.m; path = ViewControllers/OZLIssueFilterViewController.m; sourceTree = ""; }; + 2B6F38251797A00A00D06F51 /* MLTableAlertBackground.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = MLTableAlertBackground.png; sourceTree = ""; }; + 2B6F38261797A00A00D06F51 /* MLTableAlertBackground@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MLTableAlertBackground@2x.png"; sourceTree = ""; }; + 2B6F38271797A00A00D06F51 /* MLTableAlertButton.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = MLTableAlertButton.png; sourceTree = ""; }; + 2B6F38281797A00A00D06F51 /* MLTableAlertButton@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MLTableAlertButton@2x.png"; sourceTree = ""; }; + 2B6F38291797A00A00D06F51 /* MLTableAlertButtonPressed.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = MLTableAlertButtonPressed.png; sourceTree = ""; }; + 2B6F382A1797A00A00D06F51 /* MLTableAlertButtonPressed@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MLTableAlertButtonPressed@2x.png"; sourceTree = ""; }; + 2B6F382B1797A00A00D06F51 /* MLTableAlertShadowMask.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = MLTableAlertShadowMask.png; sourceTree = ""; }; + 2B6F382C1797A00A00D06F51 /* MLTableAlertShadowMask@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MLTableAlertShadowMask@2x.png"; sourceTree = ""; }; + 2B6F382D1797A00A00D06F51 /* MLTableAlert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MLTableAlert.h; sourceTree = ""; }; + 2B6F382E1797A00A00D06F51 /* MLTableAlert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MLTableAlert.m; sourceTree = ""; }; + 2B6F38381797BEBA00D06F51 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 2B8A11E017963CE500906D34 /* OZLIssueDetailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OZLIssueDetailViewController.h; path = ViewControllers/OZLIssueDetailViewController.h; sourceTree = ""; }; 2B8A11E117963CE500906D34 /* OZLIssueDetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OZLIssueDetailViewController.m; path = ViewControllers/OZLIssueDetailViewController.m; sourceTree = ""; }; 2B9968A91794F71B0086F115 /* OZLModelIssueCategory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OZLModelIssueCategory.h; path = Models/OZLModelIssueCategory.h; sourceTree = ""; }; @@ -162,6 +183,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 2B6F38391797BEBA00D06F51 /* QuartzCore.framework in Frameworks */, 2BCF88141793CB94006FC859 /* SystemConfiguration.framework in Frameworks */, 2BCF88121793CB4F006FC859 /* CFNetwork.framework in Frameworks */, D5DB805A1792F2BF0081662A /* UIKit.framework in Frameworks */, @@ -173,6 +195,31 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 2B6F38231797A00A00D06F51 /* MLTableAlert */ = { + isa = PBXGroup; + children = ( + 2B6F38241797A00A00D06F51 /* Images */, + 2B6F382D1797A00A00D06F51 /* MLTableAlert.h */, + 2B6F382E1797A00A00D06F51 /* MLTableAlert.m */, + ); + path = MLTableAlert; + sourceTree = ""; + }; + 2B6F38241797A00A00D06F51 /* Images */ = { + isa = PBXGroup; + children = ( + 2B6F38251797A00A00D06F51 /* MLTableAlertBackground.png */, + 2B6F38261797A00A00D06F51 /* MLTableAlertBackground@2x.png */, + 2B6F38271797A00A00D06F51 /* MLTableAlertButton.png */, + 2B6F38281797A00A00D06F51 /* MLTableAlertButton@2x.png */, + 2B6F38291797A00A00D06F51 /* MLTableAlertButtonPressed.png */, + 2B6F382A1797A00A00D06F51 /* MLTableAlertButtonPressed@2x.png */, + 2B6F382B1797A00A00D06F51 /* MLTableAlertShadowMask.png */, + 2B6F382C1797A00A00D06F51 /* MLTableAlertShadowMask@2x.png */, + ); + path = Images; + sourceTree = ""; + }; 2BCB50FD1796370F006845AC /* Views */ = { isa = PBXGroup; children = ( @@ -254,6 +301,7 @@ D5DB80581792F2BF0081662A /* Frameworks */ = { isa = PBXGroup; children = ( + 2B6F38381797BEBA00D06F51 /* QuartzCore.framework */, 2BCF88131793CB94006FC859 /* SystemConfiguration.framework */, 2BCF88111793CB4F006FC859 /* CFNetwork.framework */, D5DB80591792F2BF0081662A /* UIKit.framework */, @@ -294,6 +342,7 @@ D5DB80761792F3EE0081662A /* Libs */ = { isa = PBXGroup; children = ( + 2B6F38231797A00A00D06F51 /* MLTableAlert */, 2BCF880D1793ABA0006FC859 /* Reachability */, 2BCF88091793A26A006FC859 /* MBProgressHUD */, D5DB80771792F3EE0081662A /* AFNetworking */, @@ -471,6 +520,14 @@ 2B62D0E1179649A800AC3C19 /* OZLIssueDetailViewController.storyboard in Resources */, 2B62D0E31796A15C00AC3C19 /* OZLProjectCreateViewController.storyboard in Resources */, 2B62D0E51796A8C800AC3C19 /* OZLIssueFilterViewController.storyboard in Resources */, + 2B6F382F1797A00A00D06F51 /* MLTableAlertBackground.png in Resources */, + 2B6F38301797A00A00D06F51 /* MLTableAlertBackground@2x.png in Resources */, + 2B6F38311797A00A00D06F51 /* MLTableAlertButton.png in Resources */, + 2B6F38321797A00A00D06F51 /* MLTableAlertButton@2x.png in Resources */, + 2B6F38331797A00A00D06F51 /* MLTableAlertButtonPressed.png in Resources */, + 2B6F38341797A00A00D06F51 /* MLTableAlertButtonPressed@2x.png in Resources */, + 2B6F38351797A00A00D06F51 /* MLTableAlertShadowMask.png in Resources */, + 2B6F38361797A00A00D06F51 /* MLTableAlertShadowMask@2x.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -517,6 +574,7 @@ 2BCCA0541795312E00FA8B1A /* OZLConstants.m in Sources */, 2B8A11E217963CE500906D34 /* OZLIssueDetailViewController.m in Sources */, 2B62D0E81796A8D800AC3C19 /* OZLIssueFilterViewController.m in Sources */, + 2B6F38371797A00A00D06F51 /* MLTableAlert.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/RedmineMobile/RedmineMobile/Models/OZLModelProject.h b/RedmineMobile/RedmineMobile/Models/OZLModelProject.h index 4c5281f..a9a5fe6 100644 --- a/RedmineMobile/RedmineMobile/Models/OZLModelProject.h +++ b/RedmineMobile/RedmineMobile/Models/OZLModelProject.h @@ -35,6 +35,8 @@ @property(nonatomic,strong) NSString* description; @property(nonatomic,strong) NSString* name; @property(nonatomic) int parentId; +@property(nonatomic) BOOL isPublic; +@property(nonatomic,strong) NSString* homepage; @property(nonatomic,strong) NSString* createdOn; @property(nonatomic,strong) NSString* updatedOn; diff --git a/RedmineMobile/RedmineMobile/Models/OZLModelProject.m b/RedmineMobile/RedmineMobile/Models/OZLModelProject.m index 23d4dd0..6f72fae 100644 --- a/RedmineMobile/RedmineMobile/Models/OZLModelProject.m +++ b/RedmineMobile/RedmineMobile/Models/OZLModelProject.m @@ -41,6 +41,7 @@ _identifier = [dic objectForKey:@"identifier"]; _name = [dic objectForKey:@"name"]; _description = [dic objectForKey:@"description"]; + _homepage = [dic objectForKey:@"homepage"]; _createdOn = [dic objectForKey:@"created_on"]; _updatedOn = [dic objectForKey:@"updated_on"]; NSDictionary* parent = [dic objectForKey:@"parent"]; @@ -61,7 +62,10 @@ [projectDic setObject:_description forKey:@"description"]; } if (_parentId > 0) { - [projectDic setObject:[NSNumber numberWithInt:_parentId] forKey:@"parent"]; + [projectDic setObject:[NSNumber numberWithInt:_parentId] forKey:@"parent_id"]; + } + if (_homepage.length > 0) { + [projectDic setObject:_homepage forKey:@"homepage"]; } return [[NSMutableDictionary alloc] initWithObjectsAndKeys:projectDic,@"project",nil]; diff --git a/RedmineMobile/RedmineMobile/Utils/OZLNetwork.m b/RedmineMobile/RedmineMobile/Utils/OZLNetwork.m index beeab35..96e41d8 100644 --- a/RedmineMobile/RedmineMobile/Utils/OZLNetwork.m +++ b/RedmineMobile/RedmineMobile/Utils/OZLNetwork.m @@ -114,8 +114,7 @@ if (block) { NSLog(@"the repsonse:%@",responseObject); - int repondNumber = [responseObject intValue]; - block(repondNumber == 201,nil); + block(YES,nil); } } failure:^(AFHTTPRequestOperation *operation, NSError *error) { diff --git a/RedmineMobile/RedmineMobile/ViewControllers/OZLProjectCreateViewController.h b/RedmineMobile/RedmineMobile/ViewControllers/OZLProjectCreateViewController.h index 502a4fc..13da6cc 100644 --- a/RedmineMobile/RedmineMobile/ViewControllers/OZLProjectCreateViewController.h +++ b/RedmineMobile/RedmineMobile/ViewControllers/OZLProjectCreateViewController.h @@ -7,9 +7,17 @@ // #import +#import "OZLModelProject.h" @interface OZLProjectCreateViewController : UITableViewController - (IBAction)onCancel:(id)sender; - (IBAction)onSave:(id)sender; +@property (weak, nonatomic) IBOutlet UITextField *name; +@property (weak, nonatomic) IBOutlet UITextField *identifier; +@property (weak, nonatomic) IBOutlet UITextField *homepageUrl; +@property (weak, nonatomic) IBOutlet UITextView *description; +@property (nonatomic) BOOL isPublic; +@property (nonatomic,strong) OZLModelProject* parentProject; +@property (nonatomic,strong) NSArray* projectList; @end diff --git a/RedmineMobile/RedmineMobile/ViewControllers/OZLProjectCreateViewController.m b/RedmineMobile/RedmineMobile/ViewControllers/OZLProjectCreateViewController.m index 313c2e8..cd2b951 100644 --- a/RedmineMobile/RedmineMobile/ViewControllers/OZLProjectCreateViewController.m +++ b/RedmineMobile/RedmineMobile/ViewControllers/OZLProjectCreateViewController.m @@ -7,8 +7,14 @@ // #import "OZLProjectCreateViewController.h" +#import "MBProgressHUD.h" +#import "OZLNetwork.h" +#import "MLTableAlert.h" -@interface OZLProjectCreateViewController () +@interface OZLProjectCreateViewController () { + MBProgressHUD * _HUD; + +} @end @@ -31,6 +37,11 @@ [self.navigationItem setLeftBarButtonItem:cancelBtn]; UIBarButtonItem* saveBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:@selector(onSave:)]; [self.navigationItem setRightBarButtonItem:saveBtn]; + + + _HUD = [[MBProgressHUD alloc] initWithView:self.view]; + [self.view addSubview:_HUD]; + _HUD.labelText = @"Loading..."; } - (void)didReceiveMemoryWarning @@ -45,6 +56,99 @@ } - (IBAction)onSave:(id)sender { + + if (_name.text.length == 0) { + _HUD.mode = MBProgressHUDModeText; + _HUD.labelText = @"Project name can not be empty."; + [_HUD show:YES]; + [_HUD hide:YES afterDelay:1]; + + return; + } + if (_identifier.text.length == 0) { + _HUD.mode = MBProgressHUDModeText; + _HUD.labelText = @"Project identifier can not be empty."; + [_HUD show:YES]; + [_HUD hide:YES afterDelay:1]; + return; + } + + + OZLModelProject* projectData = [[OZLModelProject alloc] init]; + projectData.name = _name.text; + projectData.identifier = _identifier.text; + //TODO: is_public is not processed yet + + projectData.description = _description.text; + projectData.homepage = _homepageUrl.text; + if (_parentProject) { + projectData.parentId = _parentProject.index; + } + + _HUD.mode = MBProgressHUDModeIndeterminate; + _HUD.labelText = @"Creating Project..."; + [_HUD show:YES]; + [OZLNetwork createProject:projectData withParams:nil andBlock:^(BOOL success, NSError *error) { + if (error) { + NSLog(@"create project error: %@",error.description); + }else { + + } + [_HUD hide:YES]; + }]; } +- (void)viewDidUnload { + [self setName:nil]; + [self setIdentifier:nil]; + [self setHomepageUrl:nil]; + [self setDescription:nil]; + [super viewDidUnload]; +} + +-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (indexPath.section ==0 && indexPath.row == 4) {//parent project + // create the alert + MLTableAlert* tableAlert = [MLTableAlert tableAlertWithTitle:@"Parent Project" cancelButtonTitle:@"Cancel" numberOfRows:^NSInteger (NSInteger section) + { + return _projectList.count; + } + andCells:^UITableViewCell* (MLTableAlert *anAlert, NSIndexPath *indexPath) + { + static NSString *CellIdentifier = @"CellIdentifier"; + UITableViewCell *cell = [anAlert.table dequeueReusableCellWithIdentifier:CellIdentifier]; + if (cell == nil) + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; + + if (indexPath.row == 0) { + cell.textLabel.text = @"None"; + }else { + cell.textLabel.text = [[_projectList objectAtIndex:indexPath.row - 1] name]; + } + return cell; + }]; + + // Setting custom alert height + tableAlert.height = 350; + + // configure actions to perform + [tableAlert configureSelectionBlock:^(NSIndexPath *selectedIndex){ + UITableViewCell* parentCell = [self.tableView cellForRowAtIndexPath:indexPath]; + if (selectedIndex.row == 0) { + _parentProject = nil; + parentCell.detailTextLabel.text = @"None"; + }else { + _parentProject = [_projectList objectAtIndex:selectedIndex.row - 1]; + parentCell.detailTextLabel.text = _parentProject.name; + } + [parentCell.detailTextLabel sizeToFit]; + } andCompletionBlock:^{ + + }]; + + // show the alert + [tableAlert show]; + } +} @end diff --git a/RedmineMobile/RedmineMobile/ViewControllers/OZLProjectCreateViewController.storyboard b/RedmineMobile/RedmineMobile/ViewControllers/OZLProjectCreateViewController.storyboard index 338fe56..cdfb4ff 100644 --- a/RedmineMobile/RedmineMobile/ViewControllers/OZLProjectCreateViewController.storyboard +++ b/RedmineMobile/RedmineMobile/ViewControllers/OZLProjectCreateViewController.storyboard @@ -112,7 +112,7 @@ -