NSSortDescriptor

本文介绍了如何使用NSSortDescriptor对Objective-C中的对象数组进行排序,包括键、升序与降序设置,以及如何根据多个排序条件进行排序。

NSSortDescriptor可以实现按照对象的属性进行排序.

NSSortDescriptor有下述参数组成 :

键(key):对于一个给定的集合,对应值的键位将对集合中的每个对象进行排序。
升序(ascending ):指定一个集合是否按照升序(YES)还是降序(NO)进行排序的布尔值。

另外NSSortDescriptor还有一个涉及到排序的值之间的比较的第三个可选参数。默认情况下,这是一个简单的相等性检查,但它的行为可以通过传递一个选择器(SEL)或者比较器(NSComparator)而发生改变。

任何时候当你在为面向用户的字符串排序时,一定要加入localizedStandardCompare:选择器,它将根据当前语言环境的语言规则进行排序(语言环境可能会根据大小写,变音符号等等的顺序而发生改变)。

有的集合(比如NSArray和NSSet)有以sortDescriptors作为参数,可以返回排过序的数组的方法。排序描述符按顺序应用,所以如果两个元素碰巧被捆绑在同一个特定的排序标准,束缚将被后续的任意描述符所打破。

使用NSSortDescriptor的情况:

There are a number of situations in which you can use sort descriptors, for example:
To sort an array (an instance of NSArray or NSMutableArray—see sortedArrayUsingDescriptors: and sortUsingDescriptors:).   
To directly compare two objects (see compareObject:toObject:)
To specify how the elements in a table view should be arranged (see sortDescriptors).
To specify how the elements managed by an array controller should be arranged (see sortDescriptors)
If you are using Core Data, to specify the ordering of objects returned from a fetch request (see sortDescriptors)

以下是几种使用NSSortDescriptor的不同组合来将它们排序的方法:

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property NSString *firstName;
@property NSString *lastName;
@property NSNumber *age;

@end

#import "Person.h"

@implementation Person

//这里决定了排序之后的打印对象信息的输出.
- (NSString *)description {
    return [NSString stringWithFormat:@"%@ %@ %@", self.firstName, self.lastName,self.age];
}

@end


//1: 利用上面自定义对象(Person)。
    NSArray *firstNameArray = @[ @"Alice", @"Bob", @"Charlie", @"Quentin", @"Jack" ];
    NSArray *lastNameArray = @[ @"Smith", @"Jones", @"Smith", @"Alberts", @"Tomas" ];
    NSArray *ageArray = @[ @24, @27, @33, @31,@16 ];

    NSMutableArray *personMutableArray = [NSMutableArray array];

    [firstNameArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

        Person *person =[[Person alloc]init];

        person.firstName = firstNameArray[idx];
        person.lastName = lastNameArray[idx];
        person.age = ageArray[idx];

        [personMutableArray addObject:person];

    }];

    //按开始名字排序
    NSSortDescriptor *firstNameSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"firstName" ascending:YES selector:@selector(localizedStandardCompare:)];

    //按最后名字排序
    NSSortDescriptor *lastNameSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"lastName" ascending:YES selector:@selector(localizedStandardCompare:)];

    //按年龄排序
    NSSortDescriptor *ageSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO];


    NSLog(@"By age: %@", [personMutableArray sortedArrayUsingDescriptors:@[ageSortDescriptor]]);
    /*
     By age: (
     "Charlie Smith 33",
     "Quentin Alberts 31",
     "Bob Jones 27",
     "Alice Smith 24",
     "Jack Tomas 16"
     )

     */

    NSLog(@"By first name: %@", [personMutableArray sortedArrayUsingDescriptors:@[firstNameSortDescriptor]]);
    /*
     By first name: (
     "Alice Smith 24",
     "Bob Jones 27",
     "Charlie Smith 33",
     "Jack Tomas 16",
     "Quentin Alberts 31"
     )
     */

    //设置两个或者以上的排序条件.根据添加顺序进行先后排序.如果lastName遇到相同的会立马按照然first name进行排序.
    NSLog(@"By last name, first name: %@", [personMutableArray sortedArrayUsingDescriptors:@[lastNameSortDescriptor, firstNameSortDescriptor]]);

    /*
     By last name, first name: (
     "Quentin Alberts 31",
     "Bob Jones 27",
     "Alice Smith 24",
     "Charlie Smith 33",
     "Jack Tomas 16"
     )
     */

利用 NSSortDescriptor 对 NSMutableArray 排序的一个场景应用

有时我们在NSMutableArray中存的是网络请求返回的数据,而每一个元素又是一个NSDictionary,如果这时候需要把数组中的元素按照每个元素字典中某一个key来排序,那么我们可以利用NSSortDescriptor来快速实现需求。

-(NSMutableArray *) changeArray:(NSMutableArray *)dicArray orderWithKey:(NSString *)key ascending:(BOOL)yesOrNo{

    NSSortDescriptor *distanceDescriptor = [[NSSortDescriptor alloc] initWithKey:key ascending:yesOrNo];
    [dicArray sortUsingDescriptors:@[distanceDescriptor]];

    return dicArray;
}

IOS中文排序
http://www.cnblogs.com/syxchina/archive/2012/10/11/2720257.html

<think>我们正在解决阿拉伯语环境下PHFetchResult的索引问题。在阿拉伯语环境中,由于语言是从右向左(RTL)显示的,可能会导致UI显示顺序与数据源顺序不一致的问题。但请注意,PHFetchResult本身是一个数据集合,其顺序由我们通过sortDescriptors指定,不受语言环境影响。然而,当我们在UI上展示这些资源时(例如在UICollectionView或UITableView中),阿拉伯语的RTL特性可能会使系统自动翻转滚动方向,导致我们看到的索引顺序与预期不符。 因此,问题核心在于:如何确保在阿拉伯语环境下,UI展示的PHFetchResult资产顺序与程序逻辑顺序一致? 解决方案: 1. 控制数据获取顺序:确保PHFetchResult使用明确的sortDescriptors(例如按创建日期降序)获取数据,这样数据源顺序是固定的。 2. 调整UI布局方向:对于UICollectionView,我们可以设置其布局的direction属性,使其在阿拉伯语环境下仍然保持从左向右(LTR)的滚动方向,从而避免系统自动翻转顺序。 3. 处理索引转换:如果确实需要在RTL布局下展示,并且希望最新项在右上角(类似阿拉伯语习惯),那么数据顺序可以保持不变,但显示时调整索引位置。例如,在水平滚动的UICollectionView中,RTL布局下,第一个数据项(索引0)应该显示在最右侧。这可以通过对UICollectionView的布局进行配置来实现。 具体步骤: 步骤1:确保PHFetchResult的顺序 使用sortDescriptors获取固定顺序的数据,例如按创建时间降序排列(最新的在最前面): ```swift let fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] let assets = PHAsset.fetchAssets(with: fetchOptions) ``` 步骤2:配置UICollectionView的布局方向 在创建UICollectionViewFlowLayout时,设置其布局方向。我们可以根据当前语言环境来决定是否使用RTL,但如果我们希望在任何语言环境下都保持LTR的滚动方向(即从左到右),可以显式设置: ```swift let layout = UICollectionViewFlowLayout() // 设置滚动方向为水平 layout.scrollDirection = .horizontal // 强制布局方向为从左到右(LTR) layout.semanticContentAttribute = .forceLeftToRight // 创建UICollectionView let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) ``` 这样,即使设备语言是阿拉伯语,UICollectionView的滚动方向仍然是从左到右,第一项显示在最左边。 步骤3:如果希望支持RTL布局,但保持数据顺序不变(即最新项在最后) 在阿拉伯语环境下,我们可能希望最新项显示在右上角(即水平列表的最右侧),那么我们可以将数据顺序设置为升序(ascending: true),这样最新项在数据源的末尾,然后UICollectionView使用RTL布局,这样末尾项就会显示在最右侧。 另一种做法是保持数据顺序为降序(即最新项在数据源的最前面),但在布局时,将UICollectionView的布局方向设置为RTL,这样第一项(最新项)就会显示在最右侧。设置方法: ```swift let layout = UICollectionViewFlowLayout() layout.scrollDirection = .horizontal // 设置布局方向为从右到左(RTL) layout.semanticContentAttribute = .forceRightToLeft // 然后创建UICollectionView ``` 这样,数据源的第一项(索引0)就会显示在UICollectionView的最右侧。 步骤4:处理索引转换(可选) 如果我们希望保持UICollectionView的布局方向为LTR,但是又希望阿拉伯语环境下显示顺序与英语不同,那么我们可以对索引进行转换。例如,在阿拉伯语环境下,我们想将数据源的最后一个元素显示为第一个单元格。此时,在数据源方法中,我们可以这样转换: ```swift func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { // 获取当前语言环境是否为阿拉伯语 let isArabic = // 判断当前语言是否是阿拉伯语 let assetIndex: Int if isArabic { // 如果是阿拉伯语,则倒序:第一个cell显示最后一个asset assetIndex = assets.count - 1 - indexPath.item } else { assetIndex = indexPath.item } let asset = assets.object(at: assetIndex) // 配置cell } ``` 但是,这种方法需要我们在数据源方法中做转换,并且要注意reloadData时数据一致性问题。 推荐做法: 根据产品需求,选择步骤2(强制LTR)或步骤3(强制RTL并调整数据顺序)是更清晰的做法,因为这样我们不需要在数据源方法中做额外的索引转换。 注意事项: 1. 在阿拉伯语环境下,除了顺序问题,还应该注意文本对齐方式(NSTextAlignment自然对齐)会变成右对齐,因此需要根据情况调整。 2. 如果使用UICollectionView的布局方向为RTL,那么滚动指示器也会从右向左滚动,这符合阿拉伯语用户的习惯。 总结: 我们通过设置PHFetchOptions的sortDescriptors来固定数据源的顺序,然后通过设置UICollectionView的semanticContentAttribute来控制布局方向,从而在阿拉伯语环境下达到预期的显示顺序。 示例代码(使用步骤3:数据顺序为降序,布局方向为RTL): ```swift // 获取数据:按创建时间降序(最新的在最前面) let fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] let assets = PHAsset.fetchAssets(with: .image, options: fetchOptions) // 创建布局 let layout = UICollectionViewFlowLayout() layout.scrollDirection = .horizontal // 设置布局方向为从右到左(RTL),这样第一项(索引0)就会显示在最右侧 layout.semanticContentAttribute = .forceRightToLeft let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout) // 注册cell等 ``` 这样,在阿拉伯语环境下,最新的照片就会显示在最右侧,符合从右向左的阅读习惯,同时数据源顺序仍然是降序(索引0是最新的)。 如果希望同时支持多语言,可以根据当前语言环境动态设置布局方向: ```swift if UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft { layout.semanticContentAttribute = .forceRightToLeft } else { layout.semanticContentAttribute = .forceLeftToRight } ``` 或者,更简单地,我们可以不强制设置,而是使用系统的自然方向,但是这样系统在阿拉伯语环境下会自动使用RTL。不过,我们这里为了确保一致性,建议显式设置。 最后,记得在照片库变更时更新数据并刷新UI,如之前所述,通过PHPhotoLibraryChangeObserver来更新。 通过以上方法,我们可以解决阿拉伯语环境下PHFetchResult的索引与UI显示顺序不一致的问题。 相关问题: 1. 如何检测当前语言环境是阿拉伯语? 2. 在阿拉伯语环境下,如何调整文本对齐方式? 3. 如何动态切换UICollectionView的布局方向?</think>### iOS中PHFetchResult在阿拉伯语环境下的索引问题解决方案 在阿拉伯语(RTL语言)环境下处理PHFetchResult时,可能会遇到索引顺序问题,主要由于RTL语言的显示特性导致。以下是针对性的解决方案: #### 1. 问题核心原因 - **RTL布局反转**:阿拉伯语环境下,系统会自动反转UI布局方向(从右向左),但PHFetchResult的内部索引顺序保持不变 - **索引错位**:UI显示顺序与数据源索引不匹配,导致点击事件错位[^2] - **特殊字符处理**:类似Android中的数字格式化问题,但iOS中更多体现在布局方向[^1] #### 2. 解决方案:强制LTR布局 在显示PHAsset的UI组件中强制使用LTR(从左向右)布局,保持数据索引与显示顺序一致: ```swift // 在UICollectionView/UITableView子类中 override var semanticContentAttribute: UISemanticContentAttribute { get { .forceLeftToRight } set { /* 保持强制LTR */ } } // 或者在视图控制器中 func configureCollectionView() { let layout = UICollectionViewFlowLayout() layout.semanticContentAttribute = .forceLeftToRight collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) } ``` #### 3. 索引映射方案 当需要保持RTL布局时,建立索引映射关系: ```swift // 创建索引映射表 func mappedIndex(for index: Int) -> Int { guard isRTL else { return index } return fetchResult.count - 1 - index } // 使用示例 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let actualIndex = mappedIndex(for: indexPath.item) let asset = fetchResult.object(at: actualIndex) // 配置cell... } ``` #### 4. 资产显示优化 处理资产元数据中的RTL内容: ```swift // 在自定义Cell中 func configure(with asset: PHAsset) { // 处理日期标签 dateLabel.textAlignment = isRTL ? .right : .left dateLabel.semanticContentAttribute = .forceLeftToRight // 处理特殊字符 if let date = asset.creationDate { let formatter = DateFormatter() formatter.locale = Locale(identifier: "en_US_POSIX") // 强制使用阿拉伯数字 dateLabel.text = formatter.string(from: date) } } ``` #### 5. 完整适配流程 ```swift class PhotoViewController: UIViewController { var fetchResult: PHFetchResult<PHAsset>! var isRTL: Bool { UIView.userInterfaceLayoutDirection == .rightToLeft } override func viewDidLoad() { super.viewDidLoad() setupPhotoFetch() configureCollectionView() } func setupPhotoFetch() { let options = PHFetchOptions() options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] fetchResult = PHAsset.fetchAssets(with: options) } func configureCollectionView() { let layout = UICollectionViewFlowLayout() layout.semanticContentAttribute = .forceLeftToRight // 关键设置 collectionView = UICollectionView(/* 初始化 */) } } extension PhotoViewController: UICollectionViewDataSource { func collectionView(_ cv: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let index = isRTL ? (fetchResult.count - 1 - indexPath.item) : indexPath.item let asset = fetchResult.object(at: index) // 配置cell... } } ``` #### 6. 测试与验证要点 1. **模拟器测试**:在Xcode中设置Scheme的Application Language为Arabic 2. **边界测试**: ```swift // 测试空结果集 PHAsset.fetchAssets(with: .video, options: nil).count == 0 // 测试单元素集合 let singleAsset = PHAsset.fetchAssets(with: options).firstObject ``` 3. **真机验证**:在阿拉伯语系统设置的设备上测试布局方向一致性 #### 7. 注意事项 1. **保持数据顺序**:PHFetchResult的`sortDescriptors`应始终明确设置 2. **避免混合方向**:不要在同一个UI中混合LTR和RTL布局 3. **日期格式化**:使用`en_US_POSIX`区域设置确保数字格式一致[^1] 4. **性能优化**:索引映射操作时间复杂度为O(1),适合大型集合 > **关键原则**:PHFetchResult的索引顺序始终基于`sortDescriptors`,UI适配应通过布局方向控制和索引映射实现,而非修改数据源顺序。 通过强制LTR布局或建立索引映射关系,可确保在阿拉伯语环境下PHFetchResult的索引操作保持准确,避免因RTL布局导致的错位问题。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值