iOS 中协议和委托传值的用法

虽然之前一直听过和看过委托模式,但是并没有实际的操作。这次来实现一个简单的委托样例。来加深自己的了解。

项目目录结构为:

首先我们创建一个协议:ProtocolA

#import <Foundation/Foundation.h>

@protocol ProtocolA <NSObject>

@required

-(void)sendValue:(NSString*)str;


@end

其中有一个方法sendValue:,由于加上了@required前缀,所以这个方法时必须实现的。如果想要一个方法是可选则实现与否,则要加上@optional

之后我们创建一个类ClassA

//该类遵循ProtocolA,并且实现了它的方法

//ClassA.h
#import <Foundation/Foundation.h>
#import "ProtocolA.h"

@interface ClassA : NSObject<ProtocolA>

@end

//ClassA.m
#import "ClassA.h"

@implementation ClassA

-(void)sendValue:(NSString *)str{
    NSLog(@"我是传过来的值:%@" , str);
}

@end

写到这里,我们大概可以猜到,我们想让ClassA的实例来接受一个委托实现ProtocolA中的方法。

所以我们这里继续创建一个ClassB,让它来发出一个委托,让ClassA代替它实现。

//ClassB.h

#import <Foundation/Foundation.h>
#import "ClassA.h"

@interface ClassB : NSObject
//这里我们创建了一个delegate对象,用于设置委托对象。
@property(weak,nonatomic) id<ProtocolA> delegate;
//创建一个方法来执行委托操作
-(void)dowork;

@end

//ClassB.m

#import "ClassB.h"

@implementation ClassB

//创建a的实例,将a设置为接受委托的对象。
-(void)dowork{
    ClassA *a = [ClassA new];
    self.delegate = a;
    //当响应了委托方法时,执行sendValue:
    if ([self.delegate respondsToSelector:@selector(sendValue:)]) { // 如果协议响应了sendValue:方法
        [self.delegate sendValue:@"Hello"]; // 通知执行协议方法
    }
}

@end

最后在main.m中,执行:

#import <Foundation/Foundation.h>
#import "ClassB.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        ClassB *b = [ClassB new];
        [b dowork];
    }
    return 0;
}

运行结果为:

2017/8/28 posted in  iOS

在ios中判断一个viewcontroller是否已经正常present

之前present一个视图的时候,从来没有研究过如何判断一个视图是都已经正常弹出.比如下面这个方法是否正常执行:

[documentInteractionController presentPreviewAnimated:YES]

然而今天有一个需求需要知道上述方法是否正常执行,并弹出视图.

之后通过阅读该方法的api文档后发现:

// Bypasses the menu and opens the full screen preview window for the item at URL.  Returns NO if the item could not be previewed.
// Note that you must implement the delegate method documentInteractionControllerViewControllerForPreview: to preview the document.
- (BOOL)presentPreviewAnimated:(BOOL)animated;

该方法返回一个BOOL类型的值:当没有正常执行该方法时,会返回NO.正常返回YES.

类似的还有如下方法:

- (BOOL)isBeingPresented NS_AVAILABLE_IOS(5_0);
- (BOOL)isBeingDismissed NS_AVAILABLE_IOS(5_0);

- (BOOL)isMovingToParentViewController NS_AVAILABLE_IOS(5_0);
- (BOOL)isMovingFromParentViewController NS_AVAILABLE_IOS(5_0);

来判断viewController是消失还是出现在当前页面中.

2017/5/24 posted in  iOS

在RN中利用原生Document Interaction Controller来预览和打开文档

在react-native开发中有一个功能需求为,打开之前已经下载到Documents中的文件。一开始本想用webview来加载文件。后来发现有UIDocumentInteractionController这个api来更好的完成这个功能。

首先在RN引用的类中加入UIDocumentInteractionControllerDelegate我们要用到这个来呈现预览视图:

NativeUtil.h

#import <Foundation/Foundation.h>
#import "RCTBridgeModule.h"
#import "UIView+React.h"       //要引入这个头文件,在rn中创建和加载原声视图


@interface NativeUtil : NSObject <RCTBridgeModule,UIDocumentInteractionControllerDelegate>

@end

在NativeUtil.m中实现该委托。定义一个暴露给RN的方法,在js中调用:

RCT_EXPORT_METHOD(ShowFileChooser: (RCTResponseSenderBlock)callback){
  
    NSString *filePath = @"ceshi007";           callback(@[[NSNull null],filePath]);    //测试callback,从native向rn传值。


    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);           //获取应用本沙盒内的目录。
    NSString *documentpath = paths[0];      //获取document的路径
    NSString *fileName = [documentpath stringByAppendingPathComponent:@"Screenshot_2016-12-15-21-29-07-50.png"];            //指定上述图片的路径。
  NSURL *fileURL = [NSURL fileURLWithPath:fileName];
  dispatch_async(dispatch_get_main_queue(), ^{              //运行主线程来加载界面,并初始化UIDocumentInteractionController
    UIDocumentInteractionController *documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:fileURL];
    documentInteractionController.delegate =self ;
    [documentInteractionController presentPreviewAnimated:YES];             //present出来文件的预览界面。
  });
  
}

之后定义(UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller方法,要加载出来预览的界面,上述方法必须实现,且返回一个当前的页面的ViewController。用于作为预览视图的父ViewController来弹出modal。

- (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller{
  UIViewController *newVC =[self getPresentedViewController];  //获取当前屏幕中present出来的viewcontroller。
   return newVC;
};

这里我们是在一个非视图类创建并在加载一个视图,所以我们要先获取到当前界面的ViewController,将被展示的view加到当前view的子视图,或用当前view presentViewController,或pushViewContrller。这里我从网上找到了两个方法:

//获取当前屏幕显示的viewcontroller
- (UIViewController *)getCurrentVC  
{  
    UIViewController *result = nil;  
      
    UIWindow * window = [[UIApplication sharedApplication] keyWindow];  
    if (window.windowLevel != UIWindowLevelNormal)  
    {  
        NSArray *windows = [[UIApplication sharedApplication] windows];  
        for(UIWindow * tmpWin in windows)  
        {  
            if (tmpWin.windowLevel == UIWindowLevelNormal)  
            {  
                window = tmpWin;  
                break;  
            }  
        }  
    }  
      
    UIView *frontView = [[window subviews] objectAtIndex:0];  
    id nextResponder = [frontView nextResponder];  
      
    if ([nextResponder isKindOfClass:[UIViewController class]])  
        result = nextResponder;  
    else  
        result = window.rootViewController;  
      
    return result;  
} 
//获取当前屏幕中present出来的viewcontroller
- (UIViewController *)getPresentedViewController  
{  
    UIViewController *appRootVC = [UIApplication sharedApplication].keyWindow.rootViewController;  
    UIViewController *topVC = appRootVC;  
    if (topVC.presentedViewController) {  
        topVC = topVC.presentedViewController;  
    }  
      
    return topVC;  
}

在RN中,使用modal组件之后,弹出的modal视图的viewcontroller相当于present出来的viewcontroller.

之后使用[self getPresentedViewController]就可以获得viewcontroller来加载视图。

2017/5/15 posted in  iOS