- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我正在努力将 RAC 集成到我的项目中,目标是创建一个 ViewModel 层,该层将允许从网络轻松缓存/预取(以及 MVVM 的所有其他好处)。我还不是特别熟悉 MVVM 或 FRP,我正在尝试为 iOS 开发开发一个不错的、可重用的模式。我对此有几个问题。
首先,这就是我将 ViewModel 添加到我的一个 View 的方式,只是为了尝试一下。 (我想在这里稍后引用)。
在 ViewController viewDidLoad:
@weakify(self)
//Setup signals
RAC(self.navigationItem.title) = self.viewModel.nameSignal;
RAC(self.specialtyLabel.text) = self.viewModel.specialtySignal;
RAC(self.bioButton.hidden) = self.viewModel.hiddenBioSignal;
RAC(self.bioTextView.text) = self.viewModel.bioSignal;
RAC(self.profileImageView.hidden) = self.viewModel.hiddenProfileImageSignal;
[self.profileImageView rac_liftSelector:@selector(setImageWithContentsOfURL:placeholderImage:) withObjectsFromArray:@[self.viewModel.profileImageSignal, [RACTupleNil tupleNil]]];
[self.viewModel.hasOfficesSignal subscribeNext:^(NSArray *offices) {
self.callActionSheet = [[UIActionSheet alloc] initWithTitle:@"Choose Office" delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil];
self.directionsActionSheet = [[UIActionSheet alloc] initWithTitle:@"Choose Office" delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil];
self.callActionSheet.delegate = self;
self.directionsActionSheet.delegate = self;
}];
[self.viewModel.officesSignal subscribeNext:^(NSArray *offices){
@strongify(self)
for (LMOffice *office in offices) {
[self.callActionSheet addButtonWithTitle: office.name ? office.name : office.address1];
[self.directionsActionSheet addButtonWithTitle: office.name ? office.name : office.address1];
//add offices to maps
CLLocationCoordinate2D coordinate = {office.latitude.doubleValue, office.longitude.doubleValue};
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = coordinate;
[self.mapView addAnnotation:point];
}
//zoom to include all offices
MKMapRect zoomRect = MKMapRectNull;
for (id <MKAnnotation> annotation in self.mapView.annotations)
{
MKMapPoint annotationPoint = MKMapPointForCoordinate(annotation.coordinate);
MKMapRect pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0.2, 0.2);
zoomRect = MKMapRectUnion(zoomRect, pointRect);
}
[self.mapView setVisibleMapRect:zoomRect animated:YES];
}];
[self.viewModel.openingsSignal subscribeNext:^(NSArray *openings) {
@strongify(self)
if (openings && openings.count > 0) {
[self.openingsTable reloadData];
}
}];
ViewModel.h
@property (nonatomic, strong) LMProvider *doctor;
@property (nonatomic, strong) RACSubject *fetchDoctorSubject;
- (RACSignal *)nameSignal;
- (RACSignal *)specialtySignal;
- (RACSignal *)bioSignal;
- (RACSignal *)profileImageSignal;
- (RACSignal *)openingsSignal;
- (RACSignal *)officesSignal;
- (RACSignal *)hiddenBioSignal;
- (RACSignal *)hiddenProfileImageSignal;
- (RACSignal *)hasOfficesSignal;
ViewModel.m
- (id)init {
self = [super init];
if (self) {
_fetchDoctorSubject = [RACSubject subject];
//fetch doctor details when signalled
@weakify(self)
[self.fetchDoctorSubject subscribeNext:^(id shouldFetch) {
@strongify(self)
if ([shouldFetch boolValue]) {
[self.doctor fetchWithCompletion:^(NSError *error){
if (error) {
//TODO: display error message
NSLog(@"Error fetching single doctor info: %@", error);
}
}];
}
}];
}
return self;
}
- (RACSignal *)nameSignal {
return [RACAbleWithStart(self.doctor.displayName) distinctUntilChanged];
}
- (RACSignal *)specialtySignal {
return [RACAbleWithStart(self.doctor.primarySpecialty.name) distinctUntilChanged];
}
- (RACSignal *)bioSignal {
return [RACAbleWithStart(self.doctor.bio) distinctUntilChanged];
}
- (RACSignal *)profileImageSignal {
return [[[RACAbleWithStart(self.doctor.profilePhotoURL) distinctUntilChanged]
map:^id(NSURL *url){
if (url && ![url.absoluteString hasPrefix:@"https:"]) {
url = [NSURL URLWithString:[NSString stringWithFormat:@"https:%@", url.absoluteString]];
}
return url;
}]
filter:^BOOL(NSURL *url){
return (url != nil && ![url.absoluteString isEqualToString:@""]);
}];
}
- (RACSignal *)openingsSignal {
return [RACAbleWithStart(self.doctor.openings) distinctUntilChanged];
}
- (RACSignal *)officesSignal {
return [RACAbleWithStart(self.doctor.offices) distinctUntilChanged];
}
- (RACSignal *)hiddenBioSignal {
return [[self bioSignal] map:^id(NSString *bioString) {
return @(bioString == nil || [bioString isEqualToString:@""]);
}];
}
- (RACSignal *)hiddenProfileImageSignal {
return [[self profileImageSignal] map:^id(NSURL *url) {
return @(url == nil || [url.absoluteString isEqualToString:@""]);
}];
}
- (RACSignal *)hasOfficesSignal {
return [[self officesSignal] map:^id(NSArray *array) {
return @(array.count > 0);
}];
}
我使用信号的方式是否正确?具体来说,使用 bioSignal
更新数据以及使用 hiddenBioSignal
直接绑定(bind)到 textView 的隐藏属性是否有意义?
我的主要问题是将委托(delegate)处理的问题转移到 ViewModel 中(希望如此)。委托(delegate)在 iOS 世界中非常普遍,我想找出最好的解决方案,甚至只是适度可行的解决方案。
例如,对于 UITableView,我们需要同时提供委托(delegate)和数据源。我是否应该在 Controller NSUInteger numberOfRowsInTable
上设置一个属性并将其绑定(bind)到 ViewModel 上的信号?我真的不清楚如何使用 RAC 为我的 TableView 提供 tableView: cellForRowAtIndexPath:
中的单元格。我是否只需要以“传统”方式执行这些操作,或者是否可以为细胞提供某种信号提供者?或者也许最好让它保持原样,因为 ViewModel 不应该真正关心构建 View ,而只需修改 View 的源代码?
此外,有没有比我使用主题 (fetchDoctorSubject) 更好的方法?
任何其他评论也将不胜感激。这项工作的目标是制作一个预取/缓存 ViewModel 层,可以在需要时发出信号以在后台加载数据,从而减少设备上的等待时间。如果由此产生任何可重用的东西(模式除外),它当然是开源的。
编辑:另一个问题:根据文档,我应该为 ViewModel 中的所有信号使用属性而不是方法?我想我应该在 init 中配置它们?或者我应该保持原样以便 getter 返回新信号?
我是否应该像 ReactiveCocoa 的 github 帐户中的 ViewModel 示例那样拥有一个 active
属性?
最佳答案
View 模型应该为 View 建模。也就是说,它不应该规定任何 View 外观本身,而是规定 View 外观背后的逻辑。它不应该直接了解 View 。这是一般指导原则。
关于一些细节。
It looks like according to the documentation, I should be using properties for all of the signals in my ViewModel instead of methods? I think I should configure them in init? Or should I leave it as-is so that getters return new signals?
是的,我们通常只使用反射(reflect)其模型属性的属性。我们会在 -init
中配置它们,有点像:
- (id)init {
self = [super init];
if (self == nil) return nil;
RAC(self.title) = RACAbleWithStart(self.model.title);
return self;
}
请记住, View 模型只是用于特定用途的模型。具有普通旧属性的普通旧对象。
Am I right in the way I'm using signals? Specifically, does it make sense to have
bioSignal
to update the data as well as ahiddenBioSignal
to directly bind to the hidden property of a textView?
如果生物信号的隐藏是由某些特定的模型逻辑驱动的,那么将其作为 View 模型的属性公开是有意义的。但尽量不要从隐藏性等观点的角度来考虑它。也许更多的是关于有效性、加载等。一些与具体呈现方式无关的东西。
For a UITableView, for example, we need to provide both a delegate and a dataSource. Should I have a property on my controller NSUInteger numberOfRowsInTable and bind it to a signal on the ViewModel? And I'm really unclear on how to use RAC to provide my TableView with cells in tableView: cellForRowAtIndexPath:. Do I just need to do these the "traditional" way or is it possible to have some sort of signal provider for the cells? Or maybe it's best to leave it how it is, because a ViewModel shouldn't really be concerned with building the views, just modifying the source of the views?
最后一行完全正确。您的 View 模型应该为 View Controller 提供要显示的数据(数组、集合等),但您的 View Controller 仍然是 TableView 的委托(delegate)和数据源。 View Controller 创建单元格,但单元格由 View 模型中的数据填充。如果您的单元格相对复杂,您甚至可以拥有单元格 View 模型。
Further, is there a better approach than my use of a subject (fetchDoctorSubject)?
考虑在这里使用 RACCommand
。它将为您提供一种更好的方式来处理并发请求、错误和线程安全。命令是从 View 到 View 模型的一种非常典型的通信方式。
Should I have an active property as in the ViewModel example in ReactiveCocoa's github account?
就看你是否需要了。在 iOS 上,它可能不像 OS X 那样普遍需要,在 OS X 上,您可以分配多个 View 和 View 模型,但不能同时“激活”。
希望这对您有所帮助。看起来您总体上正朝着正确的方向前进!
关于ios - 使用 ReactiveCocoa 的 iOS 应用程序的 ViewModel 模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17551369/
IO 设备如何知道属于它的内存中的值在memory mapped IO 中发生了变化? ? 例如,假设内存地址 0 专用于保存 VGA 设备的背景颜色。当我们更改 memory[0] 中的值时,VGA
我目前正在开发一个使用Facebook sdk登录(通过FBLoginView)的iOS应用。 一切正常,除了那些拥有较旧版本的facebook的人。 当他们按下“使用Facebook登录”按钮时,他
假设我有: this - is an - example - with some - dashesNSRange将使用`rangeOfString:@“-”拾取“-”的第一个实例,但是如果我只想要最后
Card.io SDK提供以下详细信息: 卡号,有效期,月份,年份,CVV和邮政编码。 如何从此SDK获取国家名称。 - (void)userDidProvideCreditCardInfo:(Car
iOS 应用程序如何从网络服务下载图片并在安装过程中将它们安装到用户的 iOS 设备上?可能吗? 最佳答案 您无法控制应用在用户设备上的安装,因此无法在安装过程中下载其他数据。 只需在安装后首次启动应
我曾经开发过一款企业版 iOS 产品,我们公司曾将其出售给大型企业,供他们的员工使用。 该应用程序通过 AppStore 提供,企业用户获得了公司特定的配置文件(包含应用程序配置文件)以启用他们有权使
我正在尝试将 Card.io SDK 集成到我的 iOS 应用程序中。我想为 CardIO ui 做一个简单的本地化,如更改取消按钮标题或“在此保留信用卡”提示文本。 我在 github 上找到了这个
我正在使用 CardIOView 和 CardIOViewDelegate 类,没有可以设置为 YES 的 BOOL 来扫描 collectCardholderName。我可以看到它在 CardIOP
我有一个集成了通话工具包的 voip 应用程序。每次我从我的 voip 应用程序调用时,都会在 native 电话应用程序中创建一个新的最近通话记录。我在 voip 应用程序中也有自定义联系人(电话应
iOS 应用程序如何知道应用程序打开时屏幕上是否已经有键盘?应用程序运行后,它可以接收键盘显示/隐藏通知。但是,如果应用程序在分屏模式下作为辅助应用程序打开,而主应用程序已经显示键盘,则辅助应用程序不
我在模拟器中收到以下错误: ImageIO: CGImageReadSessionGetCachedImageBlockData *** CGImageReadSessionGetCachedIm
如 Apple 文档所示,可以通过 EAAccessory Framework 与经过认证的配件(由 Apple 认证)进行通信。但是我有点困惑,因为一些帖子告诉我它也可以通过 CoreBluetoo
尽管现在的调试器已经很不错了,但有时找出应用程序中正在发生的事情的最好方法仍然是古老的 NSLog。当您连接到计算机时,这样做很容易; Xcode 会帮助弹出日志查看器面板,然后就可以了。当您不在办公
在我的 iOS 应用程序中,我定义了一些兴趣点。其中一些有一个 Kontakt.io 信标的名称,它绑定(bind)到一个特定的 PoI(我的意思是通常贴在信标标签上的名称)。现在我想在附近发现信标,
我正在为警报提示创建一个 trigger.io 插件。尝试从警报提示返回数据。这是我的代码: // Prompt + (void)show_prompt:(ForgeTask*)task{
您好,我是 Apple iOS 的新手。我阅读并搜索了很多关于推送通知的文章,但我没有发现任何关于 APNS 从 io4 到 ios 6 的新更新的信息。任何人都可以向我提供 APNS 如何在 ios
UITabBar 的高度似乎在 iOS 7 和 8/9/10/11 之间发生了变化。我发布这个问题是为了让其他人轻松找到答案。 那么:在 iPhone 和 iPad 上的 iOS 8/9/10/11
我想我可以针对不同的 iOS 版本使用不同的 Storyboard。 由于 UI 的差异,我将创建下一个 Storyboard: Main_iPhone.storyboard Main_iPad.st
我正在写一些东西,我将使用设备的 iTunes 库中的一部分音轨来覆盖 2 个视频的组合,例如: AVMutableComposition* mixComposition = [[AVMutableC
我创建了一个简单的 iOS 程序,可以顺利编译并在 iPad 模拟器上运行良好。当我告诉 XCode 4 使用我连接的 iPad 设备时,无法编译相同的程序。问题似乎是当我尝试使用附加的 iPad 时
我是一名优秀的程序员,十分优秀!