MFMessageComposeVIewController忽略它的委托,而不是它本身(MFMessageComposeVIewController is dismissing its delegate instead of itself)

我有一个符合MFMessageComposeViewControllerDelegate协议的VC。

我用下面的代码成功地展示了这个视图控制器:

- (IBAction)textAction:(id)sender {
    if(![MFMessageComposeViewController canSendText])
    {
        UIAlertView *warningAlert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Your device doesn't support SMS!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [warningAlert show];
        return;
    }

    NSString *numberToCallOrText = self.phoneNumber;
    NSString *message = @"Test message";
    NSArray *recipients = [NSArray arrayWithObject:numberToCallOrText];
    MFMessageComposeViewController *messageController = [[MFMessageComposeViewController alloc] init];
    messageController.messageComposeDelegate = self;
    [messageController setRecipients:recipients];
    [messageController setBody:message];

    // Present message view controller on screen
    [self.view endEditing:YES];
    [self presentViewController:messageController animated:YES completion:nil];
}

另外,我正在处理完成结果,如下所示:

- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult) result
{
    switch (result) {
        case MessageComposeResultCancelled:
            NSLog(@"Canceled");
            break;

        case MessageComposeResultFailed:
        {
            NSLog(@"Failed");
            UIAlertView *warningAlert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Failed to send SMS!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
            [warningAlert show];
            break;
        }

        case MessageComposeResultSent:
            NSLog(@"sent");
            [self.navigationController popViewControllerAnimated:YES];
            break;

        default:
            break;
    }
    [controller.view endEditing:YES];
    [self.navigationController popViewControllerAnimated:YES];
//    [self dismissViewControllerAnimated:YES completion:nil];
//    [controller popToViewController:self animated:YES];
//    [controller dismissViewControllerAnimated:YES completion:nil];
}

三条注释掉的线是我尝试过的替代品。 发生的事情是MFMessageComposeViewController保留在屏幕上(虽然键盘被解除),但是委托从堆栈中弹出。 因此,当我再次点击取消时,我得到一个空引用错误。

这很奇怪,因为这个相同的实现在我的代码中的其他地方工作。 唯一的区别是我已经设置了要初始化的主体。

任何想法为什么错误的VC会在这里突然出现?

谢谢。

编辑 - 破坏的实现是在UITableViewController而不是UIView控制器...可能是什么导致了问题?


I have a VC that conforms to the MFMessageComposeViewControllerDelegate protocol.

I am successfully presenting this view controller with the following code:

- (IBAction)textAction:(id)sender {
    if(![MFMessageComposeViewController canSendText])
    {
        UIAlertView *warningAlert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Your device doesn't support SMS!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [warningAlert show];
        return;
    }

    NSString *numberToCallOrText = self.phoneNumber;
    NSString *message = @"Test message";
    NSArray *recipients = [NSArray arrayWithObject:numberToCallOrText];
    MFMessageComposeViewController *messageController = [[MFMessageComposeViewController alloc] init];
    messageController.messageComposeDelegate = self;
    [messageController setRecipients:recipients];
    [messageController setBody:message];

    // Present message view controller on screen
    [self.view endEditing:YES];
    [self presentViewController:messageController animated:YES completion:nil];
}

Additionally, I am handling the finish result like so:

- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult) result
{
    switch (result) {
        case MessageComposeResultCancelled:
            NSLog(@"Canceled");
            break;

        case MessageComposeResultFailed:
        {
            NSLog(@"Failed");
            UIAlertView *warningAlert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Failed to send SMS!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
            [warningAlert show];
            break;
        }

        case MessageComposeResultSent:
            NSLog(@"sent");
            [self.navigationController popViewControllerAnimated:YES];
            break;

        default:
            break;
    }
    [controller.view endEditing:YES];
    [self.navigationController popViewControllerAnimated:YES];
//    [self dismissViewControllerAnimated:YES completion:nil];
//    [controller popToViewController:self animated:YES];
//    [controller dismissViewControllerAnimated:YES completion:nil];
}

The three commented out lines are alternatives that I have tried. What's happening is that the MFMessageComposeViewController is remaining on the screen (though the keyboard is dismissed), but the delegate is being popped from the stack. Therefore, when I hit cancel again, I get a null reference error.

It's odd, because this same implementation works elsewhere in my code. The only difference is that I've set the body to be initialized.

Any ideas why the wrong VC is getting popped here?

Thanks.

Edit - The broken implementation is on a UITableViewController rather than UIView Controller... could that be what is causing the problem?


原文:https://stackoverflow.com/questions/40389457
2022-11-05 16:11

满意答案

我一开始努力让搜索字段在MVC风格的应用程序中工作。

我能够在你的sencha小提琴应用程序中使搜索字段工作。

在你的控制器中我做到了

Ext.define('Sencha.controller.Main', {
    extend: 'Ext.app.Controller',

    config: {
        refs: {
            main: 'mainpanel'
        },
        control: {
            '#list': {
                disclose: 'showDetail'
            },
            '#view':{
                activate:function(){
                    Ext.getCmp('list').add({
                        xtype:'toolbar',
                        docked:'top',
                        items:[{
                            xtype: 'searchfield',
                            itemId:'contact_search',
                            placeHolder: 'Search....',
                            listeners: {
                                scope: this,
                                clearicontap: this.onSearchClearIconTap,
                                keyup: this.onSearchKeyUp}
                        }]
                    })
                        }
            }
        }
    },

    showDetail: function(list, record) {
        this.getMain().push({
            xtype: 'recipedetail',
            title: record.fullName(),
            data: record.data
        })
            },  onSearchKeyUp: function(field) {
                //get the store and the value of the field
                var value = field.getValue(),
                    store = Ext.getCmp('list').getStore();

                //first clear any current filters on thes tore
                store.clearFilter();

                //check if a value is set first, as if it isnt we dont have to do anything
                if (value) {
                    //the user could have entered spaces, so we must split them so we can loop through them all
                    var searches = value.split(' '),
                        regexps = [],
                        i;

                    //loop them all
                    for (i = 0; i < searches.length; i++) {
                        //if it is nothing, continue
                        if (!searches[i]) continue;

                        //if found, create a new regular expression which is case insenstive
                        regexps.push(new RegExp(searches[i], 'i'));
                    }

                    //now filter the store by passing a method
                    //the passed method will be called for each record in the store
                    store.filter(function(record) {
                        var matched = [];

                        //loop through each of the regular expressions
                        for (i = 0; i < regexps.length; i++) {
                            var search = regexps[i],
                                didMatch = record.get('title').match(search);

                            //if it matched the first or last name, push it into the matches array
                            matched.push(didMatch);
                        }

                        //if nothing was found, return false (dont so in the store)
                        if (regexps.length > 1 && matched.indexOf(false) != -1) {
                            return false;
                        } else {
                            //else true true (show in the store)
                            return matched[0];
                        }
                    });
                }
            },

    /**
* Called when the user taps on the clear icon in the search field.
* It simply removes the filter form the store
*/
    onSearchClearIconTap: function() {
        //call the clearFilter method on the store instance
        this.getStore().clearFilter();
    }

});

然后在app.js中我向视口添加了一个ID

launch: function() {
        Ext.Viewport.add({
            id:'view',
            xtype: 'mainpanel'
        });
    }

我还在RecieptList.js中添加了一个ID

    xtype: 'recipelist',
    requires: ['Sencha.store.Recipes'],
    id:'list',
    config: {

可能不是最传统的解决方案,但它的工作原理。 很容易看出它们如何一起工作,希望有所帮助。


I struggled with getting the search field to work in an MVC style app at first.

I was able to make a search field work in your sencha fiddle app like this.

In your controller I did

Ext.define('Sencha.controller.Main', {
    extend: 'Ext.app.Controller',

    config: {
        refs: {
            main: 'mainpanel'
        },
        control: {
            '#list': {
                disclose: 'showDetail'
            },
            '#view':{
                activate:function(){
                    Ext.getCmp('list').add({
                        xtype:'toolbar',
                        docked:'top',
                        items:[{
                            xtype: 'searchfield',
                            itemId:'contact_search',
                            placeHolder: 'Search....',
                            listeners: {
                                scope: this,
                                clearicontap: this.onSearchClearIconTap,
                                keyup: this.onSearchKeyUp}
                        }]
                    })
                        }
            }
        }
    },

    showDetail: function(list, record) {
        this.getMain().push({
            xtype: 'recipedetail',
            title: record.fullName(),
            data: record.data
        })
            },  onSearchKeyUp: function(field) {
                //get the store and the value of the field
                var value = field.getValue(),
                    store = Ext.getCmp('list').getStore();

                //first clear any current filters on thes tore
                store.clearFilter();

                //check if a value is set first, as if it isnt we dont have to do anything
                if (value) {
                    //the user could have entered spaces, so we must split them so we can loop through them all
                    var searches = value.split(' '),
                        regexps = [],
                        i;

                    //loop them all
                    for (i = 0; i < searches.length; i++) {
                        //if it is nothing, continue
                        if (!searches[i]) continue;

                        //if found, create a new regular expression which is case insenstive
                        regexps.push(new RegExp(searches[i], 'i'));
                    }

                    //now filter the store by passing a method
                    //the passed method will be called for each record in the store
                    store.filter(function(record) {
                        var matched = [];

                        //loop through each of the regular expressions
                        for (i = 0; i < regexps.length; i++) {
                            var search = regexps[i],
                                didMatch = record.get('title').match(search);

                            //if it matched the first or last name, push it into the matches array
                            matched.push(didMatch);
                        }

                        //if nothing was found, return false (dont so in the store)
                        if (regexps.length > 1 && matched.indexOf(false) != -1) {
                            return false;
                        } else {
                            //else true true (show in the store)
                            return matched[0];
                        }
                    });
                }
            },

    /**
* Called when the user taps on the clear icon in the search field.
* It simply removes the filter form the store
*/
    onSearchClearIconTap: function() {
        //call the clearFilter method on the store instance
        this.getStore().clearFilter();
    }

});

And then in the app.js I added an ID to the viewport

launch: function() {
        Ext.Viewport.add({
            id:'view',
            xtype: 'mainpanel'
        });
    }

I also added an ID to the RecieptList.js

    xtype: 'recipelist',
    requires: ['Sencha.store.Recipes'],
    id:'list',
    config: {

Might not be the most conventional solution but it works. It is pretty easy to see how it all works together, hope that helps.

相关问答

更多

Sencha Touch 2.3:将数据从列表视图推送到详细视图(Sencha Touch 2.3: Pushing data from list view to detail view)

你是绝对正确的。 您没有设置嵌套面板的数据,而是设置Prac.view.Detail的数据。 数据是面板的配置属性。 这意味着sencha将为您创建一个setData()方法。 当您在内部使用此方法时,将分别updateData() applyData()或updateData() 。 在你的情况下,这应该工作: Ext.define('Prac.view.Detail', { extend: 'Ext.Panel', xtype: 'detail', config: {...

sencha touch 2:列出具有模型关联的人口(sencha touch 2: list population with associations in model)

AFAIK如果您正在寻找要显示的项目数组,您应该使用商店中的项目模型和指向项目的rootProperty Ext.define('senchaHackerNews.store.Search', { extend: 'Ext.data.Store', requires: ['senchaHackerNews.model.SearchResults'], config: { storeId: 'hnSearchStore', model: 's...

Sencha触摸列表与模型和商店(Sencha Touch List with Model and Store)

undefined来自这一行: Ext.ComponentQuery.query('#offersListHomeView')[0].update(); 更新是decrapted(我写了,因为我用旧的Sencha Touch版本开发一次并且这个更新是必要的)并且将调用setHtml(),因为我们没有传递任何参数它是设置“undefined”,这将显示在我们的观点。 在新的sencha版本中,您只需删除此行即可。 我还在控制器中使用此代码管理了全局存储问题: launch: function() ...

将onLoad侦听器添加到Sencha Touch List(Add onLoad Listener to Sencha Touch List)

正如@Jimmy所提到的,列表中没有onLoad方法。 但是,有几种方法可以解决它。 我对你想要实现的内容的理解是,当加载支持列表的存储时,你希望从ProductList实例(而不是list )触发事件,这样在控制器中你可以将control配置为: control: { ProductList: { productListSelectedCommand: 'productListSelectCommand', productListLoadedCommand...

Sencha Touch,setRecord()不起作用。(Sencha Touch, setRecord() not working. Fields are blank.)

我不确定,但您应该在Person详细信息视图中使用以下代码: items: [ { xtype:'formpanel', id:'personDetailViewForm', items:[{ xtype: 'fieldset', items: [{ xtype: 'textfield', nam...

Sencha touch,选择主对象项目时显示详细信息(Sencha touch, Show details when selecting master objects' item)

你想要的教程在这里: http://docs-origin.sencha.com/touch/2.3.0/#!/guide/first_app 专注于“博客”功能。 它遵循主要细节模式。 关于您的申请,我不知道,因为您没有上传任何相关的源代码。 The tutorial you want is here: http://docs-origin.sencha.com/touch/2.3.0/#!/guide/first_app Stay focus on the "Blog" features. I...

搜索字段在sencha touch中(Search fields In sencha touch)

我想您需要搜索字段的搜索功能..在键入时显示结果。 您可以通过使用正则表达式并将它们与商店中的条目进行比较来实现此目的。 这是我在项目中使用的代码: //referencing my searchfield Search: '#searchfield'; //attaching an event Search: { keyup: "OnFocus" } //the actual function OnFocus: ...

如何使用详细信息卡在列表中添加搜索?(How to add search in list with detail cards? (Sencha Touch 2))

我一开始努力让搜索字段在MVC风格的应用程序中工作。 我能够在你的sencha小提琴应用程序中使搜索字段工作。 在你的控制器中我做到了 Ext.define('Sencha.controller.Main', { extend: 'Ext.app.Controller', config: { refs: { main: 'mainpanel' }, control: { '#list'...

Sencha Touch读取XML数据(Sencha Touch Reading XML data)

嘿@GordanWebb尝试从XML文件中显示数据, Ext.regModel('Personal', { fields : [ 'id', {name: 'name', type: 'string'}, {name: 'email', type: 'string'} ] }), { scroll : 'vertical', items : [ { ...

sencha touch将变量添加到模板/ List / etc.(sencha touch add variables to templates/List/ etc)

您可以在模板中使用内联javascript代码来分割坐标变量。 以下是文档中的示例: var tpl = new Ext.XTemplate( '<p>Name: {name}</p>', '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>', '<p>Kids: ', '<tpl for="kids">', '<div class="{[xindex % 2...

相关文章

更多

忽略特殊文件-git入门教程

有些时候,你必须把某些文件放到Git工作目录中,但又不能提交它们,比如保存了数据库密码的配置文件啦,等 ...

HttpClient 获取HTTPS证书和忽略证书错误

使用Apache HttpClient 4.5从资源服务器获取证书。 证书用于通过使用SSL / TL ...

微信收费事件背后被广泛忽略的技术细节

作为一个横跨通信与互联网两大行业的从业者,前四年的核心网经验和后五年的互联网经验让我不得不感慨一个非常 ...

微信收费事件背后被广泛忽略的技术细节

作为一个横跨通信与互联网两大行业的从业者,前四年的核心网经验和后五年的互联网经验让我不得不感慨一个非常 ...

SolrCloud分布式检索时忽略宕机的Shard

“&lt;lst name=”error”&gt; &lt;str name=”msg”&gt;no ...

实现友盟分享

-(void)shareWork:(id)sender{ UIActionSheet *share ...

solr dataimport 数据导入源码分析(五)

我们注意到EntityProcessorWrapper的初始化方法 @Override pub ...

友盟分享到朋友圈时, 点击链接跳转到友盟自家页面的处理

// 要添加这两项, 不然, 微信中的地址点击会进入 umeng的 默认页面 [UMSocial ...

Storm-源码分析- bolt (backtype.storm.task)

Bolt关键的接口为execute, Tuple的真正处理逻辑, 通过OutputCollector ...

设计模式之代理模式(静态代理和动态代理)

最近在看spring的aop方面的知识,aop是基于动态代理模式实现的。所以顺便看了关于代理模式方面的 ...

最新问答

更多

获取MVC 4使用的DisplayMode后缀(Get the DisplayMode Suffix being used by MVC 4)

我用Google搜索了一个解决方案。 “EnumDisplayModeProvider”是我自己设置网站的各种模式的枚举。 public EnumDisplayModeProvider GetDisplayModeId() { foreach (var mode in DisplayModeProvider.Instance.Modes) if (mode.CanHandleContext(HttpContext)) {

如何通过引用返回对象?(How is returning an object by reference possible?)

这相对简单:在类的构造函数中,您可以分配内存,例如使用new 。 如果你制作一个对象的副本,你不是每次都分配新的内存,而是只复制指向原始内存块的指针,同时递增一个也存储在内存中的引用计数器,使得每个副本都是对象可以访问它。 如果引用计数降至零,则销毁对象将减少引用计数并仅释放分配的内存。 您只需要一个自定义复制构造函数和赋值运算符。 这基本上是共享指针的工作方式。 This is relatively easy: In the class' constructor, you allocate m

矩阵如何存储在内存中?(How are matrices stored in memory?)

正如它在“熵编码”中所说的那样,使用Z字形图案,与RLE一起使用,在许多情况下,RLE已经减小了尺寸。 但是,据我所知,DCT本身并没有给出稀疏矩阵。 但它通常会增强矩阵的熵。 这是compressen变得有损的点:输入矩阵用DCT传输,然后量化量化然后使用霍夫曼编码。 As it says in "Entropy coding" a zig-zag pattern is used, together with RLE which will already reduce size for man

每个请求的Java新会话?(Java New Session For Each Request?)

你是如何进行重定向的? 您是否事先调用了HttpServletResponse.encodeRedirectURL()? 在这里阅读javadoc 您可以使用它像response.sendRedirect(response.encodeRedirectURL(path)); The issue was with the path in the JSESSIONID cookie. I still can't figure out why it was being set to the tomca

css:浮动div中重叠的标题h1(css: overlapping headlines h1 in floated divs)

我认为word-break ,如果你想在一个单词中打破行,你可以指定它,这样做可以解决问题: .column { word-break:break-all; } jsFiddle演示。 您可以在此处阅读有关word-break属性的更多信息。 I think word-break, with which you can specify if you want to break line within a word, will do the trick: .column { word-break

无论图像如何,Caffe预测同一类(Caffe predicts same class regardless of image)

我认为您忘记在分类时间内缩放输入图像,如train_test.prototxt文件的第11行所示。 您可能应该在C ++代码中的某个位置乘以该因子,或者使用Caffe图层来缩放输入(请查看ELTWISE或POWER图层)。 编辑: 在评论中进行了一次对话之后,结果发现在classification.cpp文件中错误地删除了图像均值,而在原始训练/测试管道中没有减去图像均值。 I think you have forgotten to scale the input image during cl

xcode语法颜色编码解释?(xcode syntax color coding explained?)

转到: Xcode => Preferences => Fonts & Colors 您将看到每个语法高亮颜色旁边都有一个简短的解释。 Go to: Xcode => Preferences => Fonts & Colors You'll see that each syntax highlighting colour has a brief explanation next to it.

在Access 2010 Runtime中使用Office 2000校对工具(Use Office 2000 proofing tools in Access 2010 Runtime)

你考虑过第三方拼写检查吗? 您可以将在C#中开发的自定义WinForms控件插入访问数据库吗? VB6控件怎么样? 如果你能找到一个使用第三方库进行拼写检查的控件,那可能会有效。 Have you considered a third party spell checker? Can you insert a custom WinForms controls developed in C# into an access database? What about a VB6 control? If

从单独的Web主机将图像传输到服务器上(Getting images onto server from separate web host)

我有同样的问题,因为我在远程服务器上有两个图像,我需要在每天的预定义时间复制到我的本地服务器,这是我能够提出的代码... try { if(@copy('url/to/source/image.ext', 'local/absolute/path/on/server/' . date("d-m-Y") . ".gif")) { } else { $errors = error_get_last(); throw new Exception($err

从旧版本复制文件并保留它们(旧/新版本)(Copy a file from old revision and keep both of them (old / new revision))

我不确定我完全明白你在说什么。 你能编辑你的帖子并包含你正在做的Subversion命令/操作的特定顺序吗? 最好使用命令行svn客户端,以便容易为其他人重现问题。 如果您只是想获取文件的旧副本(即使该文件不再存在),您可以使用如下命令: svn copy ${repo}/trunk/moduleA/file1@${rev} ${repo}/trunk/moduleB/file1 其中${repo}是您的存储库的URL, ${rev}是您想要的文件的版本。 这将恢复该文件的旧版本,包括最高版本