| Index: ios/chrome/browser/ui/bookmarks/bookmark_folder_editor_view_controller.mm
|
| diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_folder_editor_view_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_folder_editor_view_controller.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6f6de34a3300c6db95cfe840e7e2d698805452df
|
| --- /dev/null
|
| +++ b/ios/chrome/browser/ui/bookmarks/bookmark_folder_editor_view_controller.mm
|
| @@ -0,0 +1,526 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_folder_editor_view_controller.h"
|
| +
|
| +#include <memory>
|
| +#include <set>
|
| +
|
| +#include "base/auto_reset.h"
|
| +#include "base/i18n/rtl.h"
|
| +#import "base/ios/weak_nsobject.h"
|
| +#include "base/logging.h"
|
| +#include "base/mac/foundation_util.h"
|
| +#include "base/mac/objc_property_releaser.h"
|
| +#include "base/mac/scoped_nsobject.h"
|
| +#include "base/strings/sys_string_conversions.h"
|
| +#include "components/bookmarks/browser/bookmark_model.h"
|
| +#include "components/bookmarks/browser/bookmark_node.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_elevated_toolbar.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_folder_view_controller.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/cells/bookmark_parent_folder_item.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/cells/bookmark_text_field_item.h"
|
| +#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
|
| +#import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
|
| +#import "ios/chrome/browser/ui/icons/chrome_icon.h"
|
| +#import "ios/chrome/browser/ui/material_components/utils.h"
|
| +#include "ios/chrome/browser/ui/rtl_geometry.h"
|
| +#include "ios/chrome/grit/ios_strings.h"
|
| +#import "ios/third_party/material_components_ios/src/components/NavigationBar/src/MaterialNavigationBar.h"
|
| +#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
|
| +#import "ios/third_party/material_components_ios/src/components/ShadowElevations/src/MaterialShadowElevations.h"
|
| +#import "ios/third_party/material_components_ios/src/components/ShadowLayer/src/MaterialShadowLayer.h"
|
| +#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
|
| +#include "ui/base/l10n/l10n_util.h"
|
| +
|
| +using bookmarks::BookmarkNode;
|
| +
|
| +namespace {
|
| +
|
| +typedef NS_ENUM(NSInteger, SectionIdentifier) {
|
| + SectionIdentifierInfo = kSectionIdentifierEnumZero,
|
| +};
|
| +
|
| +typedef NS_ENUM(NSInteger, ItemType) {
|
| + ItemTypeFolderTitle = kItemTypeEnumZero,
|
| + ItemTypeParentFolder,
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +@interface BookmarkFolderEditorViewController ()<
|
| + BookmarkFolderViewControllerDelegate,
|
| + BookmarkModelBridgeObserver,
|
| + BookmarkTextFieldItemDelegate> {
|
| + std::unique_ptr<bookmarks::BookmarkModelBridge> _modelBridge;
|
| + base::mac::ObjCPropertyReleaser
|
| + _propertyReleaser_BookmarkFolderEditorViewController;
|
| + // Flag to ignore bookmark model Move notifications when the move is performed
|
| + // by this class.
|
| + BOOL _ignoresOwnMove;
|
| +}
|
| +@property(nonatomic, assign) BOOL editingExistingFolder;
|
| +@property(nonatomic, assign) bookmarks::BookmarkModel* bookmarkModel;
|
| +@property(nonatomic, assign) ios::ChromeBrowserState* browserState;
|
| +@property(nonatomic, assign) const BookmarkNode* folder;
|
| +@property(nonatomic, retain) BookmarkFolderViewController* folderViewController;
|
| +@property(nonatomic, assign) const BookmarkNode* parentFolder;
|
| +@property(nonatomic, assign) UIBarButtonItem* doneItem;
|
| +@property(nonatomic, retain) BookmarkTextFieldItem* titleItem;
|
| +@property(nonatomic, retain) BookmarkParentFolderItem* parentFolderItem;
|
| +// Bottom toolbar with DELETE button that only appears when the edited folder
|
| +// allows deletion.
|
| +@property(nonatomic, assign) BookmarksElevatedToolbar* toolbar;
|
| +
|
| +// |bookmarkModel| must not be NULL and must be loaded.
|
| +- (instancetype)initWithBookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel
|
| + NS_DESIGNATED_INITIALIZER;
|
| +- (instancetype)initWithStyle:(CollectionViewControllerStyle)style
|
| + NS_UNAVAILABLE;
|
| +
|
| +// Enables or disables the save button depending on the state of the form.
|
| +- (void)updateSaveButtonState;
|
| +
|
| +// Configures collection view model.
|
| +- (void)setupCollectionViewModel;
|
| +
|
| +// Adds toolbar with DELETE button.
|
| +- (void)addToolbar;
|
| +
|
| +// Removes toolbar.
|
| +- (void)removeToolbar;
|
| +
|
| +@end
|
| +
|
| +@implementation BookmarkFolderEditorViewController
|
| +
|
| +@synthesize bookmarkModel = _bookmarkModel;
|
| +@synthesize delegate = _delegate;
|
| +@synthesize editingExistingFolder = _editingExistingFolder;
|
| +@synthesize folder = _folder;
|
| +@synthesize folderViewController = _folderViewController;
|
| +@synthesize parentFolder = _parentFolder;
|
| +@synthesize browserState = _browserState;
|
| +@synthesize doneItem = _doneItem;
|
| +@synthesize titleItem = _titleItem;
|
| +@synthesize parentFolderItem = _parentFolderItem;
|
| +@synthesize toolbar = _toolbar;
|
| +
|
| +#pragma mark - Class methods
|
| +
|
| ++ (instancetype)
|
| +folderCreatorWithBookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel
|
| + parentFolder:(const BookmarkNode*)parentFolder {
|
| + base::scoped_nsobject<BookmarkFolderEditorViewController> folderCreator(
|
| + [[self alloc] initWithBookmarkModel:bookmarkModel]);
|
| + folderCreator.get().parentFolder = parentFolder;
|
| + folderCreator.get().folder = NULL;
|
| + folderCreator.get().editingExistingFolder = NO;
|
| + return folderCreator.autorelease();
|
| +}
|
| +
|
| ++ (instancetype)
|
| +folderEditorWithBookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel
|
| + folder:(const BookmarkNode*)folder
|
| + browserState:(ios::ChromeBrowserState*)browserState {
|
| + DCHECK(folder);
|
| + DCHECK(!bookmarkModel->is_permanent_node(folder));
|
| + DCHECK(browserState);
|
| + base::scoped_nsobject<BookmarkFolderEditorViewController> folderEditor(
|
| + [[self alloc] initWithBookmarkModel:bookmarkModel]);
|
| + folderEditor.get().parentFolder = folder->parent();
|
| + folderEditor.get().folder = folder;
|
| + folderEditor.get().browserState = browserState;
|
| + folderEditor.get().editingExistingFolder = YES;
|
| + return folderEditor.autorelease();
|
| +}
|
| +
|
| +#pragma mark - Initialization
|
| +
|
| +- (instancetype)initWithBookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel {
|
| + DCHECK(bookmarkModel);
|
| + DCHECK(bookmarkModel->loaded());
|
| + self = [super initWithStyle:CollectionViewControllerStyleAppBar];
|
| + if (self) {
|
| + _propertyReleaser_BookmarkFolderEditorViewController.Init(
|
| + self, [BookmarkFolderEditorViewController class]);
|
| + _bookmarkModel = bookmarkModel;
|
| +
|
| + // Set up the bookmark model oberver.
|
| + _modelBridge.reset(
|
| + new bookmarks::BookmarkModelBridge(self, _bookmarkModel));
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +- (instancetype)initWithStyle:(CollectionViewControllerStyle)style {
|
| + NOTREACHED();
|
| + return nil;
|
| +}
|
| +
|
| +- (void)dealloc {
|
| + _titleItem.delegate = nil;
|
| + _folderViewController.delegate = nil;
|
| + [super dealloc];
|
| +}
|
| +
|
| +#pragma mark - UIViewController
|
| +
|
| +- (void)viewDidLoad {
|
| + [super viewDidLoad];
|
| + self.collectionView.backgroundColor = [UIColor whiteColor];
|
| +
|
| + // Add Done button.
|
| + base::scoped_nsobject<UIBarButtonItem> doneItem([[UIBarButtonItem alloc]
|
| + initWithTitle:l10n_util::GetNSString(
|
| + IDS_IOS_BOOKMARK_EDIT_MODE_EXIT_MOBILE)
|
| + style:UIBarButtonItemStylePlain
|
| + target:self
|
| + action:@selector(saveFolder)]);
|
| + doneItem.get().accessibilityIdentifier = @"Save";
|
| + self.navigationItem.rightBarButtonItem = doneItem;
|
| + self.doneItem = doneItem;
|
| +
|
| + if (self.editingExistingFolder) {
|
| + // Add Cancel Button.
|
| + UIBarButtonItem* cancelItem =
|
| + [ChromeIcon templateBarButtonItemWithImage:[ChromeIcon closeIcon]
|
| + target:self
|
| + action:@selector(cancel)];
|
| + cancelItem.accessibilityLabel =
|
| + l10n_util::GetNSString(IDS_IOS_BOOKMARK_NEW_CANCEL_BUTTON_LABEL);
|
| + cancelItem.accessibilityIdentifier = @"Cancel";
|
| + self.navigationItem.leftBarButtonItem = cancelItem;
|
| +
|
| + [self addToolbar];
|
| + } else {
|
| + // Add Back button.
|
| + UIBarButtonItem* backItem =
|
| + [ChromeIcon templateBarButtonItemWithImage:[ChromeIcon backIcon]
|
| + target:self
|
| + action:@selector(back)];
|
| + backItem.accessibilityLabel =
|
| + l10n_util::GetNSString(IDS_IOS_BOOKMARK_NEW_BACK_LABEL);
|
| + backItem.accessibilityIdentifier = @"Back";
|
| + self.navigationItem.leftBarButtonItem = backItem;
|
| + }
|
| +
|
| + [self updateEditingState];
|
| + [self setupCollectionViewModel];
|
| +}
|
| +
|
| +- (void)viewWillAppear:(BOOL)animated {
|
| + [super viewWillAppear:animated];
|
| + [self updateSaveButtonState];
|
| +}
|
| +
|
| +#pragma mark - Accessibility
|
| +
|
| +- (BOOL)accessibilityPerformEscape {
|
| + [self.delegate bookmarkFolderEditorDidCancel:self];
|
| + return YES;
|
| +}
|
| +
|
| +#pragma mark - Actions
|
| +
|
| +- (void)back {
|
| + [self.delegate bookmarkFolderEditorDidCancel:self];
|
| +}
|
| +
|
| +- (void)cancel {
|
| + [self.delegate bookmarkFolderEditorDidCancel:self];
|
| +}
|
| +
|
| +- (void)deleteFolder {
|
| + DCHECK(self.editingExistingFolder);
|
| + DCHECK(self.folder);
|
| + std::set<const BookmarkNode*> editedNodes;
|
| + editedNodes.insert(self.folder);
|
| + bookmark_utils_ios::DeleteBookmarksWithUndoToast(
|
| + editedNodes, self.bookmarkModel, self.browserState);
|
| + [self.delegate bookmarkFolderEditorDidDeleteEditedFolder:self];
|
| +}
|
| +
|
| +- (void)saveFolder {
|
| + DCHECK(self.parentFolder);
|
| +
|
| + NSString* folderString = self.titleItem.text;
|
| + DCHECK(folderString.length > 0);
|
| + base::string16 folderTitle = base::SysNSStringToUTF16(folderString);
|
| +
|
| + if (self.editingExistingFolder) {
|
| + DCHECK(self.folder);
|
| + self.bookmarkModel->SetTitle(self.folder, folderTitle);
|
| + if (self.folder->parent() != self.parentFolder) {
|
| + base::AutoReset<BOOL> autoReset(&_ignoresOwnMove, YES);
|
| + std::set<const BookmarkNode*> editedNodes;
|
| + editedNodes.insert(self.folder);
|
| + bookmark_utils_ios::MoveBookmarksWithUndoToast(
|
| + editedNodes, self.bookmarkModel, self.parentFolder,
|
| + self.browserState);
|
| + }
|
| + } else {
|
| + DCHECK(!self.folder);
|
| + self.folder = self.bookmarkModel->AddFolder(
|
| + self.parentFolder, self.parentFolder->child_count(), folderTitle);
|
| + }
|
| + [self.delegate bookmarkFolderEditor:self didFinishEditingFolder:self.folder];
|
| +}
|
| +
|
| +- (void)changeParentFolder {
|
| + std::set<const BookmarkNode*> editedNodes;
|
| + if (self.folder)
|
| + editedNodes.insert(self.folder);
|
| + base::scoped_nsobject<BookmarkFolderViewController> folderViewController(
|
| + [[BookmarkFolderViewController alloc]
|
| + initWithBookmarkModel:self.bookmarkModel
|
| + allowsNewFolders:NO
|
| + editedNodes:editedNodes
|
| + allowsCancel:NO
|
| + selectedFolder:self.parentFolder]);
|
| + folderViewController.get().delegate = self;
|
| + self.folderViewController = folderViewController;
|
| +
|
| + [self.navigationController pushViewController:folderViewController
|
| + animated:YES];
|
| +}
|
| +
|
| +#pragma mark - BookmarkFolderViewControllerDelegate
|
| +
|
| +- (void)folderPicker:(BookmarkFolderViewController*)folderPicker
|
| + didFinishWithFolder:(const BookmarkNode*)folder {
|
| + self.parentFolder = folder;
|
| + [self updateParentFolderState];
|
| + [self.navigationController popViewControllerAnimated:YES];
|
| + self.folderViewController.delegate = nil;
|
| + self.folderViewController = nil;
|
| +}
|
| +
|
| +- (void)folderPickerDidCancel:(BookmarkFolderViewController*)folderPicker {
|
| + [self.navigationController popViewControllerAnimated:YES];
|
| + self.folderViewController.delegate = nil;
|
| + self.folderViewController = nil;
|
| +}
|
| +
|
| +#pragma mark - BookmarkModelBridgeObserver
|
| +
|
| +- (void)bookmarkModelLoaded {
|
| + // The bookmark model is assumed to be loaded when this controller is created.
|
| + NOTREACHED();
|
| +}
|
| +
|
| +- (void)bookmarkNodeChanged:(const BookmarkNode*)bookmarkNode {
|
| + if (bookmarkNode == self.parentFolder) {
|
| + [self updateParentFolderState];
|
| + }
|
| +}
|
| +
|
| +- (void)bookmarkNodeChildrenChanged:(const BookmarkNode*)bookmarkNode {
|
| + // No-op.
|
| +}
|
| +
|
| +- (void)bookmarkNode:(const BookmarkNode*)bookmarkNode
|
| + movedFromParent:(const BookmarkNode*)oldParent
|
| + toParent:(const BookmarkNode*)newParent {
|
| + if (_ignoresOwnMove)
|
| + return;
|
| + if (bookmarkNode == self.folder) {
|
| + DCHECK(oldParent == self.parentFolder);
|
| + self.parentFolder = newParent;
|
| + [self updateParentFolderState];
|
| + }
|
| +}
|
| +
|
| +- (void)bookmarkNodeDeleted:(const BookmarkNode*)bookmarkNode
|
| + fromFolder:(const BookmarkNode*)folder {
|
| + if (bookmarkNode == self.parentFolder) {
|
| + self.parentFolder = NULL;
|
| + [self updateParentFolderState];
|
| + return;
|
| + }
|
| + if (bookmarkNode == self.folder) {
|
| + self.folder = NULL;
|
| + self.editingExistingFolder = NO;
|
| + [self updateEditingState];
|
| + }
|
| +}
|
| +
|
| +- (void)bookmarkModelRemovedAllNodes {
|
| + if (self.bookmarkModel->is_permanent_node(self.parentFolder))
|
| + return; // The current parent folder is still valid.
|
| +
|
| + self.parentFolder = NULL;
|
| + [self updateParentFolderState];
|
| +}
|
| +
|
| +#pragma mark - BookmarkTextFieldItemDelegate
|
| +
|
| +- (void)textDidChangeForItem:(BookmarkTextFieldItem*)item {
|
| + [self updateSaveButtonState];
|
| +}
|
| +
|
| +- (BOOL)textFieldShouldReturn:(UITextField*)textField {
|
| + [textField resignFirstResponder];
|
| + return YES;
|
| +}
|
| +
|
| +#pragma mark - UICollectionViewDelegate
|
| +
|
| +- (void)collectionView:(UICollectionView*)collectionView
|
| + didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
|
| + [super collectionView:collectionView didSelectItemAtIndexPath:indexPath];
|
| + if ([self.collectionViewModel itemTypeForIndexPath:indexPath] ==
|
| + ItemTypeParentFolder) {
|
| + [self changeParentFolder];
|
| + }
|
| +}
|
| +
|
| +#pragma mark - UICollectionViewFlowLayout
|
| +
|
| +- (CGSize)collectionView:(UICollectionView*)collectionView
|
| + layout:(UICollectionViewLayout*)collectionViewLayout
|
| + sizeForItemAtIndexPath:(NSIndexPath*)indexPath {
|
| + switch ([self.collectionViewModel itemTypeForIndexPath:indexPath]) {
|
| + case ItemTypeFolderTitle: {
|
| + const CGFloat kTitleCellHeight = 96;
|
| + return CGSizeMake(CGRectGetWidth(collectionView.bounds),
|
| + kTitleCellHeight);
|
| + }
|
| + case ItemTypeParentFolder: {
|
| + const CGFloat kParentFolderCellHeight = 50;
|
| + return CGSizeMake(CGRectGetWidth(collectionView.bounds),
|
| + kParentFolderCellHeight);
|
| + }
|
| + default:
|
| + NOTREACHED();
|
| + return CGSizeZero;
|
| + }
|
| +}
|
| +
|
| +#pragma mark - Private
|
| +
|
| +- (void)setParentFolder:(const BookmarkNode*)parentFolder {
|
| + if (!parentFolder) {
|
| + parentFolder = self.bookmarkModel->mobile_node();
|
| + }
|
| + _parentFolder = parentFolder;
|
| +}
|
| +
|
| +- (void)updateEditingState {
|
| + if (![self isViewLoaded])
|
| + return;
|
| +
|
| + self.view.accessibilityIdentifier =
|
| + (self.folder) ? @"Folder Editor" : @"Folder Creator";
|
| +
|
| + [self setTitle:(self.folder)
|
| + ? l10n_util::GetNSString(
|
| + IDS_IOS_BOOKMARK_NEW_GROUP_EDITOR_EDIT_TITLE)
|
| + : l10n_util::GetNSString(
|
| + IDS_IOS_BOOKMARK_NEW_GROUP_EDITOR_CREATE_TITLE)];
|
| +}
|
| +
|
| +- (void)updateParentFolderState {
|
| + NSIndexPath* folderSelectionIndexPath =
|
| + [self.collectionViewModel indexPathForItemType:ItemTypeParentFolder
|
| + sectionIdentifier:SectionIdentifierInfo];
|
| + self.parentFolderItem.title =
|
| + bookmark_utils_ios::TitleForBookmarkNode(self.parentFolder);
|
| + [self.collectionView reloadItemsAtIndexPaths:@[ folderSelectionIndexPath ]];
|
| +
|
| + if (self.editingExistingFolder && !self.toolbar)
|
| + [self addToolbar];
|
| +
|
| + if (!self.editingExistingFolder && self.toolbar)
|
| + [self removeToolbar];
|
| +}
|
| +
|
| +- (void)setupCollectionViewModel {
|
| + [self loadModel];
|
| +
|
| + [self.collectionViewModel addSectionWithIdentifier:SectionIdentifierInfo];
|
| +
|
| + base::scoped_nsobject<BookmarkTextFieldItem> titleItem(
|
| + [[BookmarkTextFieldItem alloc] initWithType:ItemTypeFolderTitle]);
|
| + titleItem.get().text =
|
| + (self.folder)
|
| + ? bookmark_utils_ios::TitleForBookmarkNode(self.folder)
|
| + : l10n_util::GetNSString(IDS_IOS_BOOKMARK_NEW_GROUP_DEFAULT_NAME);
|
| + titleItem.get().placeholder =
|
| + l10n_util::GetNSString(IDS_IOS_BOOKMARK_NEW_EDITOR_NAME_LABEL);
|
| + titleItem.get().accessibilityIdentifier = @"Title";
|
| + [self.collectionViewModel addItem:titleItem
|
| + toSectionWithIdentifier:SectionIdentifierInfo];
|
| + titleItem.get().delegate = self;
|
| + self.titleItem = titleItem;
|
| +
|
| + base::scoped_nsobject<BookmarkParentFolderItem> parentFolderItem(
|
| + [[BookmarkParentFolderItem alloc] initWithType:ItemTypeParentFolder]);
|
| + parentFolderItem.get().title =
|
| + bookmark_utils_ios::TitleForBookmarkNode(self.parentFolder);
|
| + [self.collectionViewModel addItem:parentFolderItem
|
| + toSectionWithIdentifier:SectionIdentifierInfo];
|
| + self.parentFolderItem = parentFolderItem;
|
| +}
|
| +
|
| +- (void)addToolbar {
|
| + // Add bottom toolbar with Delete button.
|
| + base::scoped_nsobject<BookmarksElevatedToolbar> buttonBar(
|
| + [[BookmarksElevatedToolbar alloc] init]);
|
| + base::scoped_nsobject<UIBarButtonItem> deleteItem([[UIBarButtonItem alloc]
|
| + initWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_GROUP_DELETE)
|
| + style:UIBarButtonItemStylePlain
|
| + target:self
|
| + action:@selector(deleteFolder)]);
|
| + deleteItem.get().accessibilityIdentifier = @"Delete Folder";
|
| + [deleteItem setTitleTextAttributes:@{
|
| + NSForegroundColorAttributeName : [UIColor blackColor]
|
| + }
|
| + forState:UIControlStateNormal];
|
| + [buttonBar.get().layer
|
| + addSublayer:[[[MDCShadowLayer alloc] init] autorelease]];
|
| + buttonBar.get().shadowElevation = MDCShadowElevationSearchBarResting;
|
| + buttonBar.get().items = @[ deleteItem ];
|
| + [self.view addSubview:buttonBar];
|
| +
|
| + // Constraint |buttonBar| to be in bottom.
|
| + buttonBar.get().translatesAutoresizingMaskIntoConstraints = NO;
|
| + [self.view addConstraints:
|
| + [NSLayoutConstraint
|
| + constraintsWithVisualFormat:@"H:|[buttonBar]|"
|
| + options:0
|
| + metrics:nil
|
| + views:NSDictionaryOfVariableBindings(
|
| + buttonBar)]];
|
| + [self.view addConstraint:[NSLayoutConstraint
|
| + constraintWithItem:buttonBar
|
| + attribute:NSLayoutAttributeBottom
|
| + relatedBy:NSLayoutRelationEqual
|
| + toItem:self.view
|
| + attribute:NSLayoutAttributeBottom
|
| + multiplier:1.0
|
| + constant:0.0]];
|
| + [self.view
|
| + addConstraint:[NSLayoutConstraint
|
| + constraintWithItem:buttonBar
|
| + attribute:NSLayoutAttributeHeight
|
| + relatedBy:NSLayoutRelationEqual
|
| + toItem:nil
|
| + attribute:NSLayoutAttributeNotAnAttribute
|
| + multiplier:1.0
|
| + constant:48.0]];
|
| + self.toolbar = buttonBar;
|
| +}
|
| +
|
| +- (void)removeToolbar {
|
| + [self.toolbar removeFromSuperview];
|
| + self.toolbar = nil;
|
| +}
|
| +
|
| +- (void)updateSaveButtonState {
|
| + self.doneItem.enabled = (self.titleItem.text.length > 0);
|
| +}
|
| +
|
| +@end
|
|
|