Integrate the CKEditor as html editor into iOS application

In short, it is very easy to integrate the CKEditor into the iOS application and make it work for you.  In this document, I will introduce:

how to embed CKEditor into your iOS application, how to customize the toolbar icons of the CKEditor, how to set html content to CKEditor and how to get its html content, how to resize CKEditor frame in fly and how to disable the default inputAccessoryView of UIWebView which annoys the user with the default toolbar with "Previous" and "Next" buttons.  

 

Here is my screenshot on the CKEditor on my iOS application

 

Here are my steps on how to do it.

 

1.  Please download the latest version of the CKEditor.  What the version I used is CKEditor_3.6.4 from the url: http://download.cksource.com/CKEditor/CKEditor/CKEditor%203.6.4/ckeditor_3.6.4.zip

 

2. Add the CKEditor source files into your project. 

In my project Libraries group, I create a new group "CKEditor" and then right click on this group and select "Add Files to 'myproject' ", in the file browser dialog, I choose the ckeditor_3.6.4 folder to involve all the files into my project.

 

After added source files into the project, please make sure all the js files are moved into Copy Bundle Resources section in Build Phases to avoid the [WARN]warning: no rule to process file '$(PROJECT_DIR)/EndPlay/ckeditor_3.6.4/ckeditor/ckeditor_basic.js' of type sourcecode.javascript for architecture i386

 

Here are my steps to do:

In the Project Navigator, click the Project  to show the TARGETS item in the right side view.  Click the your project name in the TARGETS, then click the Build Phases tab.  In the down list view of Compile Sources, please drag and drop all the js files into the Copy Bundle Resources section.

 

3.  Refine or create a html file to embed CKEditor into html body, and customize the toolbar icons of CKEditor.

 In my application, to speed up, I just copy and rename the readonly.html to "ckeditorIPad.html" and "ckeditorIPhone.html".   And changed the default text in editor as below in these html files:

<textarea id="editor1"name="editor1"cols="80"rows="10">{*CKEditor HTML BODY*}</textarea>

 

This change will be easy for me to fill the user text into editor later. 

 

Customize the toolbar to fit for your use and organize the position of toolbar differently for iPhone and iPad because the screen size different, that is why I have two separate html files.

To customize the toolbar, the key is to init  the icon set in the CKEDITOR.config.  such as I initialized a simple icon set:

    CKEDITOR.config.toolbar_Abstract =

        [ [ 'Source', '-', 'FontSize', '-', 'TextColor', 'BGColor', '-', 'Bold', 'Italic', 'Underline', '-', 'PasteText', '-', 'Subscript','Superscript', '-', 'JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock', '-', 'Undo', 'Redo', '-', 'Link', 'Unlink' ] ];

 

When initialized the CKEditor instance, we can user this icon set with the keyword 'Abstract':

                 var editor1 = CKEDITOR.replace( 'editor1', {

                                              filebrowseBrowseUrl:null,

                                              filebrowserUploadUrl:null,

                                              height:'300px',

                                               width:'100%',

                                              toolbar:'Abstract',

                                              resize_enabled:true

                                              } );

 

Here is the example code of my customized html file:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<!--

Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.

For licensing, see LICENSE.html or http://ckeditor.com/license

-->

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

    <title>Read-only State &mdash; CKEditor Sample</title>

    <meta content="text/html; charset=utf-8"http-equiv="content-type" />

    <script type="text/javascript"src="ckeditor.js"></script>

    <script src="sample.js"type="text/javascript"></script>

    <link href="sample.css"rel="stylesheet"type="text/css" />

    <script type="text/javascript">

    //<![CDATA[

 

var editor;

    CKEDITOR.config.toolbar_Abstract =

        [

         [ 'Source', '-', 'FontSize', '-', 'TextColor', 'BGColor', '-', 'Bold', 'Italic', 'Underline', '-', 'PasteText', '-', 'Subscript','Superscript', '-', 'JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock', '-', 'Undo', 'Redo', '-', 'Link', 'Unlink' ]

         ];

// The instanceReady event is fired, when an instance of CKEditor has finished

// its initialization.

CKEDITOR.on( 'instanceReady', function( ev )

    {

      editor = ev.editor;

 

      // Show this "on" button.

      document.getElementById( 'readOnlyOn' ).style.display = '';

 

      // Event fired when the readOnly property changes.

      editor.on( 'readOnly', function()

        {

           document.getElementById( 'readOnlyOn' ).style.display = this.readOnly ? 'none' : '';

           document.getElementById( 'readOnlyOff' ).style.display = this.readOnly ? '' : 'none';

        });

    });

 

function toggleReadOnly( isReadOnly )

{

    // Change the read-only state of the editor.

    // http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.editor.html#setReadOnly

    editor.setReadOnly( isReadOnly );

}

 

    //]]>

    </script>

 

</head>

<body>

    <!-- This <div> holds alert messages to be display in the sample page. -->

    <div id="alerts">

      <noscript>

        <p>

           <strong>CKEditor requires JavaScript to run</strong>. In a browser with no JavaScript

           support, like yours, you should still see the contents (HTML data) and you should

           be able to edit it normally, without a rich editor interface.

        </p>

      </noscript>

    </div>

    <form action="sample_posteddata.php"method="post">

      <p>

        <textarea id="editor1"name="editor1"cols="80"rows="10">{*CKEditor HTML BODY*}</textarea>

       

            <script type="text/javascript">

            //<![CDATA[

        // Replace the <textarea id="editor1"> with an CKEditor instance.

                var editor1 = CKEDITOR.replace( 'editor1', {

                                              filebrowseBrowseUrl:null,

                                              filebrowserUploadUrl:null,

                                              height:'300px',

                                               width:'100%',

                                              toolbar:'Abstract',

                                              resize_enabled:true

                                              } );

            //]]>

            </script>

      </p>

    </form>

 

</body>

</html>

        

 

Later, we will use UIWebview control to load this html file to show the CKEditor.

 

4.  In iOS code, init a UIWebView htmlEditorView to load the CKEditor.

 

-(void) initHtmlEditorView:(NSString*) htmlText withBounds:(CGRect) bounds

{

    if (self.htmlEditorView == nil)

    {

        self.htmlEditorView = [[UIWebView alloc] initWithFrame:bounds];

        self.htmlEditorView.delegate = self;

        self.htmlEditorView.autoresizesSubviews = YES;

 

        self.htmlEditorView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;

                                           

        [[self egoTextView] addSubview:self.htmlEditorView];

    } else

    {

        [self.htmlEditorView setFrame:bounds];

    }

   

    NSString* path = [[NSBundle mainBundle] bundlePath];

    NSURL* baseURL = [NSURL fileURLWithPath:path];

   

    NSString* filePath = nil;

   

    if (isPad)

    {

        filePath = [[NSBundle mainBundle] pathForResource:@"ckeditoriPad" ofType:@"html"];

    } else

    {

        filePath = [[NSBundle mainBundle] pathForResource:@"ckeditoriPhone" ofType:@"html"];

    }

   

    NSMutableString *templateString = [NSMutableString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding  error:nil];

 

    // Fill in the user html text into the CKEditor body.

    if (htmlText == nil || [NSNull null] == (NSNull*) htmlText || [htmlText isEqualToString: @""]))

    {

        [templateString replaceOccurrencesOfString:@"{*CKEditor HTML BODY*}" withString:@"" options: NSLiteralSearch range:NSMakeRange(0,         [templateString length] - 1)];

    }else

    {

        [templateString replaceOccurrencesOfString:@"{*CKEditor HTML BODY*}" withString:htmlText options: NSLiteralSearch range:NSMakeRange(0, [templateString length] - 1)]; 

    }

 

 

    NSData* htmlData = [templateString dataUsingEncoding: NSUTF8StringEncoding];

   

    if (htmlData)

    {

        [self.htmlEditorView loadData:htmlData MIMEType:@"text/html" textEncodingName:@"UTF-8" baseURL:baseURL];

    }

}

 

5.  Then we need consider how to resize the CKEditor to fit for the htmlEditorView, and how to get the html content from CKEditor.

 

In my application, I have a toolbar view with "Done" button set to the inputAccessoryView of the UIWebView (htmlEditorView).  So in the DoneAction, I just call below statement to get the html content from CKEditor.  "editor.getData()" is the JS API call.  editor is the global variable of my CKEditor, which is defined in the html file (ckeditoriPhone.html, or ckeditoriPad.html).

 

NSString *html = [self.htmlEditorView stringByEvaluatingJavaScriptFromString:@"editor.getData();"];

 

To resize the CKEditor window in fly, I implement below method to make its height always fit for the height of its parent view.

-(void) adjustHtmlEditorSize

{

    NSInteger height = (int)(self.htmlEditorView.frame.size.height);

    if (height < 320) height = 320;

    NSString* script = [NSString stringWithFormat:@"editor.resize( \'100%@\', \'%d\' );",@"%", height];

    [self.htmlEditorView stringByEvaluatingJavaScriptFromString:script];

}

 

Also you might need consider to refine the sample.css file to change the margin size between the CKEditor and UIWebView.  In my code, I just changed the body's margin and padding as below:

 

body {

    margin: 0px 0px 0px 0px;

    padding: 0px 5px 0px 0px;

}

 

p, blockquote, address, form, pre, dl, h1.samples, h2.samples {

    margin-bottom:15px;

    margin-right:5px;

}

 

 

6.  The last thing you might meet as well is the default inputAccessoryView is shown up when you have focus on the CKEditor.

It annoys me to show the toolbar with "Previous" and "Next" buttons, which is nothing related to CKEditor.  So disable it or change its default view to customized view which depends on your need.   

Here is a good document on how to disable the default inputAccessoryView of UIWebView:  http://bjhomer.blogspot.com/2012/03/how-to-hide-inputaccessoryview-of.html

In my code, I referenced BJ's solution to disable the inputAccessoryView by calling below code in webViewDidFinishLoad:

- (void) webViewDidFinishLoad:(UIWebView *)webView

{

    [self adjustHtmlEditorSize];

    if (![self.htmlEditorView isHidesInputAccessoryView])

    {

        [self.htmlEditorView hidesInputAccessoryView:YES];

    }

}

 

// Here is the code on the category of UIWebView.

@interface UIWebView (HideInputAccessoryView)

 -(BOOL) isHidesInputAccessoryView;

 -(void) hidesInputAccessoryView:(BOOL)value;

@end

 

 

@implementation UIWebView (HideInputAccessoryView)

 

static const char * const hackishFixClassName = "UIWebBrowserViewMinusAccessoryView";

static Class hackishFixClass = Nil;

 

- (UIView *) hackishlyFoundBrowserView {

    UIScrollView *scrollView = self.scrollView;

   

    UIView *browserView = nil;

    for (UIView *subview in scrollView.subviews) {

        if ([NSStringFromClass([subview class]) hasPrefix:@"UIWebBrowserView"]) {

            browserView = subview;

            break;

        }

    }

    return browserView;

}

 

- (id)methodReturningNil {

    return nil;

}

 

- (void)ensureHackishSubclassExistsOfBrowserViewClass:(Class)browserViewClass {

    if (!hackishFixClass) {

        id newClass = objc_allocateClassPair(browserViewClass, hackishFixClassName, 0);

        IMP nilImp = [self methodForSelector:@selector(methodReturningNil)];

        class_addMethod(newClass, @selector(inputAccessoryView), nilImp, "@@:");

        objc_registerClassPair(newClass);

       

        hackishFixClass = newClass;

    }

}

 

- (BOOL) isHidesInputAccessoryView {

    UIView *browserView = [self hackishlyFoundBrowserView];

    return [browserView class] == hackishFixClass;

}

 

- (void) hidesInputAccessoryView:(BOOL)value {

    UIView *browserView = [self hackishlyFoundBrowserView];

    if (browserView == nil) {

        return;

    }

    [self ensureHackishSubclassExistsOfBrowserViewClass:[browserView class]];

   

    if (value) {

        object_setClass(browserView, hackishFixClass);

    }

    else {

        Class normalClass = objc_getClass("UIWebBrowserView");

        object_setClass(browserView, normalClass);

    }

    [browserView reloadInputViews];

}

 

@end

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章