- 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/
Glenn Block 和我一直在研究 ViewModel 模式。我们一直在尝试找出与该模式相关的最大痛点,目标是添加框架支持以减轻痛苦。 今晚,格伦发帖,“View Model” – the mov
如果我使用 Views 创建 ViewModel 实例且 ViewModel 没有对 View 的引用的方法,我不知道创建 viewmodel 关系的正确方法是什么。 假设我们有 ChildView
当从缓存中重新加载 ViewModel 时,我需要能够拦截框架并执行重新初始化。由于没有重新创建 ViewModel,我既不能使用 Init()、MvxViewModel.InitFromBundle
我的业务模型(实际上它用于使用 Entity Framework 6 读取数据)看起来像: class Profile : NameDescriptionBase { public virtu
我在swift中玩MVVM遇到了这种情况:我为 tableView 创建了模型,其中包含对象列表和对象计数。有点像 class TableViewViewModel { var count :
我正在研究由以下部分组成的应用程序区域: Explorer - 包含 TreeView PropertyInspector - 包含一个 PropertyGrid 编辑器 - 包含一个 Explore
我正在使用 MVVM-Light,并且我有一个列出了销售人员的 DataGrid 工作。用户可以双击打开一个子窗口,该窗口将在网格上列出他们的销售,用户将能够在该网格下填写一些文本框以添加新的销售。
是否有适当的方法来创建包含 subViewModel 的 C#/WPF ViewModel ? 目标是: 我有一个主窗口。该窗口用于读取/创建图像。窗口上有一个按钮,可以在 2 个 UserContr
首先,如果这很简单,我必须道歉。我对 WPF 和 MVVM 非常陌生,我想确保我没有违反任何 WPF 或 MVVM 概念。此外,对于下面的冗长解释(试图提供所有细节): 我目前正在引用具有所有业务逻辑
我有一个架构问题,以及一个我想提出意见的可能解决方案。 我习惯于 WP7 的 MVVM 架构(尽可能但不幸的是,有时 sdk 似乎朝着相反的方向发展)。 WP7 强制采用 ViewFirst 方法,我
鉴于以下情况: ViewModelA 启动 ViewModelB(当然,通过一个通用 Controller ,它使用 Ioc 和 DI 来解析所需的类型)。 ViewModelB 需要在 ViewMo
在我的 WPF MVVM 应用程序中,使用 Caliburn.Micro,我有一个 ViewModel,CreateServiceViewModel,在单击按钮时,它会在单独的窗口中打开一个 Grid
假设我有一个采用特定 ViewModel 的页面( View ): @model IEnumerable 在这个页面中,我有一个通过另一个 ViewModel(我们称之为 PostModel)发布数据
我有两个相似的 ViewModel,我需要将一个转换为另一个。 这是第一个: using System; using System.Collections.Generic; using System.
我有两个 View 共享来自某个 View 模型的一个可观察集合,但具有不同的 Collection View 参数。在 MVVM Light 中实现它的正确方法是什么?是否支持非静态虚拟机?我如何管
我有 2 个 View 模型 - 主视图模型** 存储 View 模型 StorageViewModel.kt class StorageViewModel @ViewModelInject cons
我有一个 AddressesViewModel 保存用户最喜欢的地址,另一个 SearchViewModel 保存搜索到的地址。当用户搜索地址时,我必须通过检查收藏夹数组来检查该地址是否是收藏夹。正确
首先,我看过this post并没有找到我的问题的答案。 我不确定这是否是汇总 型号 类或聚合 查看型号 类,但这就是我所拥有的: 在我的 WPF(使用 Prism)应用程序中,我有一个 View “
我的 View 中有这些过滤器,它们都会更新 FilterViewModel,然后由它负责过滤数据。其中一个 View ,SearchAddressView 需要 PlacemarkViewModel
依赖的 ViewModel 通过构造函数(IoC 容器)注入(inject)。 示例:ProductSelectionViewModel 使用 ShoppingBasketViewModel。 这是一
我是一名优秀的程序员,十分优秀!