当前位置: 移动技术网 > IT编程>移动开发>IOS > iOS模仿电子书首页实现书架布局样式

iOS模仿电子书首页实现书架布局样式

2019年07月24日  | 移动技术网IT编程  | 我要评论

原油脱硫剂,天津站,神话人物名字

本文实现了类似电子书首页,用来展示图书或小说的布局页面,书架列表【iphone6模拟器】,屏幕尺寸还没进行适配,只是做个简单的demo【纯代码实现方式】

实现采用的是uicollectionview和uicollectionviewflowlayout。关于uicollectionview的详细讲解请参考

 

一、实现layout的decorationview


//
// fwbookshelfdecarationviewcollectionreusableview.h
// fwpersonalapp
//
// created by hzkmn on 16/2/18.
// copyright © 2016年 forrstwoo. all rights reserved.
//

#import <uikit/uikit.h>

extern nsinteger const kdecorationviewheight;


@interface fwbookshelfdecarationview : uicollectionreusableview

@end

//
// fwbookshelfdecarationviewcollectionreusableview.m
// fwpersonalapp
//
// created by hzkmn on 16/2/18.
// copyright © 2016年 forrstwoo. all rights reserved.
//



#import "fwbookshelfdecarationview.h"

nsinteger const kdecorationviewheight = 216;

@implementation fwbookshelfdecarationview

- (instancetype)initwithframe:(cgrect)frame
{
  if (self = [super initwithframe:frame])
  {
    uiimageview *img = [[uiimageview alloc] initwithframe:cgrectmake(0, 0, screensize.width, kdecorationviewheight)];
    img.image = [uiimage imagenamed:@"boolshelf.png"];
    [self addsubview:img];
  }
  
  return self;
}
@end
 

fwbookshelfdecarationview类非常简单只是定义了decarationview的背景图片,图上。

二、下载及导入可重新排序的第三方layout,用于我们移动图书后重新布局
在实际项目中你或许会遇到在一个集合视图中移动一项到另外一个位置,那么此时我们需要对视图中的元素进行重新排序,今天推荐一个很好用的第三方类lxreorderablecollectionviewflowlayout【点此链接进入github】

下面附上实现代码

//
// lxreorderablecollectionviewflowlayout.h
//
// created by stan chang khin boon on 1/10/12.
// copyright (c) 2012 d--buzz. all rights reserved.
//

#import <uikit/uikit.h>

@interface lxreorderablecollectionviewflowlayout : uicollectionviewflowlayout <uigesturerecognizerdelegate>

@property (assign, nonatomic) cgfloat scrollingspeed;
@property (assign, nonatomic) uiedgeinsets scrollingtriggeredgeinsets;
@property (strong, nonatomic, readonly) uilongpressgesturerecognizer *longpressgesturerecognizer;
@property (strong, nonatomic, readonly) uipangesturerecognizer *pangesturerecognizer;

- (void)setupgesturerecognizersoncollectionview __attribute__((deprecated("calls to setupgesturerecognizersoncollectionview method are not longer needed as setup are done automatically through kvo.")));

@end

@protocol lxreorderablecollectionviewdatasource <uicollectionviewdatasource>

@optional

- (void)collectionview:(uicollectionview *)collectionview itematindexpath:(nsindexpath *)fromindexpath willmovetoindexpath:(nsindexpath *)toindexpath;
- (void)collectionview:(uicollectionview *)collectionview itematindexpath:(nsindexpath *)fromindexpath didmovetoindexpath:(nsindexpath *)toindexpath;

- (bool)collectionview:(uicollectionview *)collectionview canmoveitematindexpath:(nsindexpath *)indexpath;
- (bool)collectionview:(uicollectionview *)collectionview itematindexpath:(nsindexpath *)fromindexpath canmovetoindexpath:(nsindexpath *)toindexpath;

@end

@protocol lxreorderablecollectionviewdelegateflowlayout <uicollectionviewdelegateflowlayout>
@optional

- (void)collectionview:(uicollectionview *)collectionview layout:(uicollectionviewlayout *)collectionviewlayout willbegindraggingitematindexpath:(nsindexpath *)indexpath;
- (void)collectionview:(uicollectionview *)collectionview layout:(uicollectionviewlayout *)collectionviewlayout didbegindraggingitematindexpath:(nsindexpath *)indexpath;
- (void)collectionview:(uicollectionview *)collectionview layout:(uicollectionviewlayout *)collectionviewlayout willenddraggingitematindexpath:(nsindexpath *)indexpath;
- (void)collectionview:(uicollectionview *)collectionview layout:(uicollectionviewlayout *)collectionviewlayout didenddraggingitematindexpath:(nsindexpath *)indexpath;

@end
//
// lxreorderablecollectionviewflowlayout.m
//
// created by stan chang khin boon on 1/10/12.
// copyright (c) 2012 d--buzz. all rights reserved.
//

#import "lxreorderablecollectionviewflowlayout.h"
#import <quartzcore/quartzcore.h>
#import <objc/runtime.h>

#define lx_frames_per_second 60.0

#ifndef cggeometry_lxsupport_h_
cg_inline cgpoint
lxs_cgpointadd(cgpoint point1, cgpoint point2) {
  return cgpointmake(point1.x + point2.x, point1.y + point2.y);
}
#endif

typedef ns_enum(nsinteger, lxscrollingdirection) {
  lxscrollingdirectionunknown = 0,
  lxscrollingdirectionup,
  lxscrollingdirectiondown,
  lxscrollingdirectionleft,
  lxscrollingdirectionright
};

static nsstring * const klxscrollingdirectionkey = @"lxscrollingdirection";
static nsstring * const klxcollectionviewkeypath = @"collectionview";

@interface cadisplaylink (lx_userinfo)
@property (nonatomic, copy) nsdictionary *lx_userinfo;
@end

@implementation cadisplaylink (lx_userinfo)
- (void) setlx_userinfo:(nsdictionary *) lx_userinfo {
  objc_setassociatedobject(self, "lx_userinfo", lx_userinfo, objc_association_copy);
}

- (nsdictionary *) lx_userinfo {
  return objc_getassociatedobject(self, "lx_userinfo");
}
@end

@interface uicollectionviewcell (lxreorderablecollectionviewflowlayout)

- (uiimage *)lx_rasterizedimage;

@end

@implementation uicollectionviewcell (lxreorderablecollectionviewflowlayout)

- (uiimage *)lx_rasterizedimage {
  uigraphicsbeginimagecontextwithoptions(self.bounds.size, self.isopaque, 0.0f);
  [self.layer renderincontext:uigraphicsgetcurrentcontext()];
  uiimage *image = uigraphicsgetimagefromcurrentimagecontext();
  uigraphicsendimagecontext();
  return image;
}

@end

@interface lxreorderablecollectionviewflowlayout ()

@property (strong, nonatomic) nsindexpath *selecteditemindexpath;
@property (strong, nonatomic) uiview *currentview;
@property (assign, nonatomic) cgpoint currentviewcenter;
@property (assign, nonatomic) cgpoint pantranslationincollectionview;
@property (strong, nonatomic) cadisplaylink *displaylink;

@property (assign, nonatomic, readonly) id<lxreorderablecollectionviewdatasource> datasource;
@property (assign, nonatomic, readonly) id<lxreorderablecollectionviewdelegateflowlayout> delegate;

@end

@implementation lxreorderablecollectionviewflowlayout

- (void)setdefaults {
  _scrollingspeed = 300.0f;
  _scrollingtriggeredgeinsets = uiedgeinsetsmake(50.0f, 50.0f, 50.0f, 50.0f);
}

- (void)setupcollectionview {
  _longpressgesturerecognizer = [[uilongpressgesturerecognizer alloc] initwithtarget:self
                                        action:@selector(handlelongpressgesture:)];
  _longpressgesturerecognizer.delegate = self;
  
  // links the default long press gesture recognizer to the custom long press gesture recognizer we are creating now
  // by enforcing failure dependency so that they doesn't clash.
  for (uigesturerecognizer *gesturerecognizer in self.collectionview.gesturerecognizers) {
    if ([gesturerecognizer iskindofclass:[uilongpressgesturerecognizer class]]) {
      [gesturerecognizer requiregesturerecognizertofail:_longpressgesturerecognizer];
    }
  }
  
  [self.collectionview addgesturerecognizer:_longpressgesturerecognizer];
  
  _pangesturerecognizer = [[uipangesturerecognizer alloc] initwithtarget:self
                                  action:@selector(handlepangesture:)];
  _pangesturerecognizer.delegate = self;
  [self.collectionview addgesturerecognizer:_pangesturerecognizer];

  // useful in multiple scenarios: one common scenario being when the notification center drawer is pulled down
  [[nsnotificationcenter defaultcenter] addobserver:self selector:@selector(handleapplicationwillresignactive:) name: uiapplicationwillresignactivenotification object:nil];
}

- (id)init {
  self = [super init];
  if (self) {
    [self setdefaults];
    [self addobserver:self forkeypath:klxcollectionviewkeypath options:nskeyvalueobservingoptionnew context:nil];
  }
  return self;
}

- (id)initwithcoder:(nscoder *)adecoder {
  self = [super initwithcoder:adecoder];
  if (self) {
    [self setdefaults];
    [self addobserver:self forkeypath:klxcollectionviewkeypath options:nskeyvalueobservingoptionnew context:nil];
  }
  return self;
}

- (void)dealloc {
  [self invalidatesscrolltimer];
  [self removeobserver:self forkeypath:klxcollectionviewkeypath];
  [[nsnotificationcenter defaultcenter] removeobserver:self name:uiapplicationwillresignactivenotification object:nil];
}

- (void)applylayoutattributes:(uicollectionviewlayoutattributes *)layoutattributes {
  if ([layoutattributes.indexpath isequal:self.selecteditemindexpath]) {
    layoutattributes.hidden = yes;
  }
}

- (id<lxreorderablecollectionviewdatasource>)datasource {
  return (id<lxreorderablecollectionviewdatasource>)self.collectionview.datasource;
}

- (id<lxreorderablecollectionviewdelegateflowlayout>)delegate {
  return (id<lxreorderablecollectionviewdelegateflowlayout>)self.collectionview.delegate;
}

- (void)invalidatelayoutifnecessary {
  nsindexpath *newindexpath = [self.collectionview indexpathforitematpoint:self.currentview.center];
  nsindexpath *previousindexpath = self.selecteditemindexpath;
  
  if ((newindexpath == nil) || [newindexpath isequal:previousindexpath]) {
    return;
  }
  
  if ([self.datasource respondstoselector:@selector(collectionview:itematindexpath:canmovetoindexpath:)] &&
    ![self.datasource collectionview:self.collectionview itematindexpath:previousindexpath canmovetoindexpath:newindexpath]) {
    return;
  }
  
  self.selecteditemindexpath = newindexpath;
  
  if ([self.datasource respondstoselector:@selector(collectionview:itematindexpath:willmovetoindexpath:)]) {
    [self.datasource collectionview:self.collectionview itematindexpath:previousindexpath willmovetoindexpath:newindexpath];
  }

  __weak typeof(self) weakself = self;
  [self.collectionview performbatchupdates:^{
    __strong typeof(self) strongself = weakself;
    if (strongself) {
      [strongself.collectionview deleteitemsatindexpaths:@[ previousindexpath ]];
      [strongself.collectionview insertitemsatindexpaths:@[ newindexpath ]];
    }
  } completion:^(bool finished) {
    __strong typeof(self) strongself = weakself;
    if ([strongself.datasource respondstoselector:@selector(collectionview:itematindexpath:didmovetoindexpath:)]) {
      [strongself.datasource collectionview:strongself.collectionview itematindexpath:previousindexpath didmovetoindexpath:newindexpath];
    }
  }];
}

- (void)invalidatesscrolltimer {
  if (!self.displaylink.paused) {
    [self.displaylink invalidate];
  }
  self.displaylink = nil;
}

- (void)setupscrolltimerindirection:(lxscrollingdirection)direction {
  if (!self.displaylink.paused) {
    lxscrollingdirection olddirection = [self.displaylink.lx_userinfo[klxscrollingdirectionkey] integervalue];

    if (direction == olddirection) {
      return;
    }
  }
  
  [self invalidatesscrolltimer];

  self.displaylink = [cadisplaylink displaylinkwithtarget:self selector:@selector(handlescroll:)];
  self.displaylink.lx_userinfo = @{ klxscrollingdirectionkey : @(direction) };

  [self.displaylink addtorunloop:[nsrunloop mainrunloop] formode:nsrunloopcommonmodes];
}

#pragma mark - target/action methods

// tight loop, allocate memory sparely, even if they are stack allocation.
- (void)handlescroll:(cadisplaylink *)displaylink {
  lxscrollingdirection direction = (lxscrollingdirection)[displaylink.lx_userinfo[klxscrollingdirectionkey] integervalue];
  if (direction == lxscrollingdirectionunknown) {
    return;
  }
  
  cgsize framesize = self.collectionview.bounds.size;
  cgsize contentsize = self.collectionview.contentsize;
  cgpoint contentoffset = self.collectionview.contentoffset;
  uiedgeinsets contentinset = self.collectionview.contentinset;
  // important to have an integer `distance` as the `contentoffset` property automatically gets rounded
  // and it would diverge from the view's center resulting in a "cell is slipping away under finger"-bug.
  cgfloat distance = rint(self.scrollingspeed / lx_frames_per_second);
  cgpoint translation = cgpointzero;
  
  switch(direction) {
    case lxscrollingdirectionup: {
      distance = -distance;
      cgfloat miny = 0.0f - contentinset.top;
      
      if ((contentoffset.y + distance) <= miny) {
        distance = -contentoffset.y - contentinset.top;
      }
      
      translation = cgpointmake(0.0f, distance);
    } break;
    case lxscrollingdirectiondown: {
      cgfloat maxy = max(contentsize.height, framesize.height) - framesize.height + contentinset.bottom;
      
      if ((contentoffset.y + distance) >= maxy) {
        distance = maxy - contentoffset.y;
      }
      
      translation = cgpointmake(0.0f, distance);
    } break;
    case lxscrollingdirectionleft: {
      distance = -distance;
      cgfloat minx = 0.0f - contentinset.left;
      
      if ((contentoffset.x + distance) <= minx) {
        distance = -contentoffset.x - contentinset.left;
      }
      
      translation = cgpointmake(distance, 0.0f);
    } break;
    case lxscrollingdirectionright: {
      cgfloat maxx = max(contentsize.width, framesize.width) - framesize.width + contentinset.right;
      
      if ((contentoffset.x + distance) >= maxx) {
        distance = maxx - contentoffset.x;
      }
      
      translation = cgpointmake(distance, 0.0f);
    } break;
    default: {
      // do nothing...
    } break;
  }
  
  self.currentviewcenter = lxs_cgpointadd(self.currentviewcenter, translation);
  self.currentview.center = lxs_cgpointadd(self.currentviewcenter, self.pantranslationincollectionview);
  self.collectionview.contentoffset = lxs_cgpointadd(contentoffset, translation);
}


- (void)handlelongpressgesture:(uilongpressgesturerecognizer *)gesturerecognizer {
  switch(gesturerecognizer.state) {
    case uigesturerecognizerstatebegan: {
      nsindexpath *currentindexpath = [self.collectionview indexpathforitematpoint:[gesturerecognizer locationinview:self.collectionview]];
      
      if ([self.datasource respondstoselector:@selector(collectionview:canmoveitematindexpath:)] &&
        ![self.datasource collectionview:self.collectionview canmoveitematindexpath:currentindexpath]) {
        return;
      }
      
      self.selecteditemindexpath = currentindexpath;
      
      if ([self.delegate respondstoselector:@selector(collectionview:layout:willbegindraggingitematindexpath:)]) {
        [self.delegate collectionview:self.collectionview layout:self willbegindraggingitematindexpath:self.selecteditemindexpath];
      }
      
      uicollectionviewcell *collectionviewcell = [self.collectionview cellforitematindexpath:self.selecteditemindexpath];
      
      self.currentview = [[uiview alloc] initwithframe:collectionviewcell.frame];
      
      collectionviewcell.highlighted = yes;
      uiimageview *highlightedimageview = [[uiimageview alloc] initwithimage:[collectionviewcell lx_rasterizedimage]];
      highlightedimageview.autoresizingmask = uiviewautoresizingflexiblewidth | uiviewautoresizingflexibleheight;
      highlightedimageview.alpha = 1.0f;
      
      collectionviewcell.highlighted = no;
      uiimageview *imageview = [[uiimageview alloc] initwithimage:[collectionviewcell lx_rasterizedimage]];
      imageview.autoresizingmask = uiviewautoresizingflexiblewidth | uiviewautoresizingflexibleheight;
      imageview.alpha = 0.0f;
      
      [self.currentview addsubview:imageview];
      [self.currentview addsubview:highlightedimageview];
      [self.collectionview addsubview:self.currentview];
      
      self.currentviewcenter = self.currentview.center;
      
      __weak typeof(self) weakself = self;
      [uiview
       animatewithduration:0.3
       delay:0.0
       options:uiviewanimationoptionbeginfromcurrentstate
       animations:^{
         __strong typeof(self) strongself = weakself;
         if (strongself) {
           strongself.currentview.transform = cgaffinetransformmakescale(1.1f, 1.1f);
           highlightedimageview.alpha = 0.0f;
           imageview.alpha = 1.0f;
         }
       }
       completion:^(bool finished) {
         __strong typeof(self) strongself = weakself;
         if (strongself) {
           [highlightedimageview removefromsuperview];
           
           if ([strongself.delegate respondstoselector:@selector(collectionview:layout:didbegindraggingitematindexpath:)]) {
             [strongself.delegate collectionview:strongself.collectionview layout:strongself didbegindraggingitematindexpath:strongself.selecteditemindexpath];
           }
         }
       }];
      
      [self invalidatelayout];
    } break;
    case uigesturerecognizerstatecancelled:
    case uigesturerecognizerstateended: {
      nsindexpath *currentindexpath = self.selecteditemindexpath;
      
      if (currentindexpath) {
        if ([self.delegate respondstoselector:@selector(collectionview:layout:willenddraggingitematindexpath:)]) {
          [self.delegate collectionview:self.collectionview layout:self willenddraggingitematindexpath:currentindexpath];
        }
        
        self.selecteditemindexpath = nil;
        self.currentviewcenter = cgpointzero;
        
        uicollectionviewlayoutattributes *layoutattributes = [self layoutattributesforitematindexpath:currentindexpath];
        
        __weak typeof(self) weakself = self;
        [uiview
         animatewithduration:0.3
         delay:0.0
         options:uiviewanimationoptionbeginfromcurrentstate
         animations:^{
           __strong typeof(self) strongself = weakself;
           if (strongself) {
             strongself.currentview.transform = cgaffinetransformmakescale(1.0f, 1.0f);
             strongself.currentview.center = layoutattributes.center;
           }
         }
         completion:^(bool finished) {
           __strong typeof(self) strongself = weakself;
           if (strongself) {
             [strongself.currentview removefromsuperview];
             strongself.currentview = nil;
             [strongself invalidatelayout];
             
             if ([strongself.delegate respondstoselector:@selector(collectionview:layout:didenddraggingitematindexpath:)]) {
               [strongself.delegate collectionview:strongself.collectionview layout:strongself didenddraggingitematindexpath:currentindexpath];
             }
           }
         }];
      }
    } break;
      
    default: break;
  }
}

- (void)handlepangesture:(uipangesturerecognizer *)gesturerecognizer {
  switch (gesturerecognizer.state) {
    case uigesturerecognizerstatebegan:
    case uigesturerecognizerstatechanged: {
      self.pantranslationincollectionview = [gesturerecognizer translationinview:self.collectionview];
      cgpoint viewcenter = self.currentview.center = lxs_cgpointadd(self.currentviewcenter, self.pantranslationincollectionview);
      
      [self invalidatelayoutifnecessary];
      
      switch (self.scrolldirection) {
        case uicollectionviewscrolldirectionvertical: {
          if (viewcenter.y < (cgrectgetminy(self.collectionview.bounds) + self.scrollingtriggeredgeinsets.top)) {
            [self setupscrolltimerindirection:lxscrollingdirectionup];
          } else {
            if (viewcenter.y > (cgrectgetmaxy(self.collectionview.bounds) - self.scrollingtriggeredgeinsets.bottom)) {
              [self setupscrolltimerindirection:lxscrollingdirectiondown];
            } else {
              [self invalidatesscrolltimer];
            }
          }
        } break;
        case uicollectionviewscrolldirectionhorizontal: {
          if (viewcenter.x < (cgrectgetminx(self.collectionview.bounds) + self.scrollingtriggeredgeinsets.left)) {
            [self setupscrolltimerindirection:lxscrollingdirectionleft];
          } else {
            if (viewcenter.x > (cgrectgetmaxx(self.collectionview.bounds) - self.scrollingtriggeredgeinsets.right)) {
              [self setupscrolltimerindirection:lxscrollingdirectionright];
            } else {
              [self invalidatesscrolltimer];
            }
          }
        } break;
      }
    } break;
    case uigesturerecognizerstatecancelled:
    case uigesturerecognizerstateended: {
      [self invalidatesscrolltimer];
    } break;
    default: {
      // do nothing...
    } break;
  }
}

#pragma mark - uicollectionviewlayout overridden methods

- (nsarray *)layoutattributesforelementsinrect:(cgrect)rect {
  nsarray *layoutattributesforelementsinrect = [super layoutattributesforelementsinrect:rect];
  
  for (uicollectionviewlayoutattributes *layoutattributes in layoutattributesforelementsinrect) {
    switch (layoutattributes.representedelementcategory) {
      case uicollectionelementcategorycell: {
        [self applylayoutattributes:layoutattributes];
      } break;
      default: {
        // do nothing...
      } break;
    }
  }
  
  return layoutattributesforelementsinrect;
}

- (uicollectionviewlayoutattributes *)layoutattributesforitematindexpath:(nsindexpath *)indexpath {
  uicollectionviewlayoutattributes *layoutattributes = [super layoutattributesforitematindexpath:indexpath];
  
  switch (layoutattributes.representedelementcategory) {
    case uicollectionelementcategorycell: {
      [self applylayoutattributes:layoutattributes];
    } break;
    default: {
      // do nothing...
    } break;
  }
  
  return layoutattributes;
}

#pragma mark - uigesturerecognizerdelegate methods

- (bool)gesturerecognizershouldbegin:(uigesturerecognizer *)gesturerecognizer {
  if ([self.pangesturerecognizer isequal:gesturerecognizer]) {
    return (self.selecteditemindexpath != nil);
  }
  return yes;
}

- (bool)gesturerecognizer:(uigesturerecognizer *)gesturerecognizer shouldrecognizesimultaneouslywithgesturerecognizer:(uigesturerecognizer *)othergesturerecognizer {
  if ([self.longpressgesturerecognizer isequal:gesturerecognizer]) {
    return [self.pangesturerecognizer isequal:othergesturerecognizer];
  }
  
  if ([self.pangesturerecognizer isequal:gesturerecognizer]) {
    return [self.longpressgesturerecognizer isequal:othergesturerecognizer];
  }
  
  return no;
}

#pragma mark - key-value observing methods

- (void)observevalueforkeypath:(nsstring *)keypath ofobject:(id)object change:(nsdictionary *)change context:(void *)context {
  if ([keypath isequaltostring:klxcollectionviewkeypath]) {
    if (self.collectionview != nil) {
      [self setupcollectionview];
    } else {
      [self invalidatesscrolltimer];
    }
  }
}

#pragma mark - notifications

- (void)handleapplicationwillresignactive:(nsnotification *)notification {
  self.pangesturerecognizer.enabled = no;
  self.pangesturerecognizer.enabled = yes;
}

#pragma mark - depreciated methods

#pragma mark starting from 0.1.0
- (void)setupgesturerecognizersoncollectionview {
  // do nothing...
}

@end

效果图:

 

三、实现自己的layout

首先继承lxreorderablecollectionviewflowlayout,让该类具有重新排序功能。

//
// fwbookshelfcollectionviewlayout.h
// fwpersonalapp
//
// created by hzkmn on 16/2/18.
// copyright © 2016年 forrstwoo. all rights reserved.
//

#import "lxreorderablecollectionviewflowlayout.h"

extern nsstring * const fwbookshelfcollectionviewlayoutdecorationviewkind;

@interface fwbookshelfcollectionviewlayout : lxreorderablecollectionviewflowlayout

@end

//
// fwbookshelfcollectionviewlayout.m
// fwpersonalapp
//
// created by hzkmn on 16/2/18.
// copyright © 2016年 forrstwoo. all rights reserved.
//

#import "fwbookshelfcollectionviewlayout.h"

#import "fwbookshelfdecarationview.h"

nsstring * const fwbookshelfcollectionviewlayoutdecorationviewkind = @"fwbookshelfcollectionviewlayoutdecorationviewkind";

@interface fwbookshelfcollectionviewlayout ()

@property (nonatomic, strong) nsdictionary *bookshelfrectanges;
@property nsinteger countofrow;

@end

@implementation fwbookshelfcollectionviewlayout

- (void)preparelayout
{
  [super preparelayout];
  
  [self registerclass:[fwbookshelfdecarationview class] fordecorationviewofkind:fwbookshelfcollectionviewlayoutdecorationviewkind];
  
  nsmutabledictionary *dictionary = [nsmutabledictionary dictionary];
  
  nsinteger itemcount = [self.collectionview numberofitemsinsection:0];
  self.countofrow = ceilf(itemcount / 3.0);
  for (int row = 0; row < self.countofrow; row++)
  {
    dictionary[[nsindexpath indexpathforitem:row insection:0]] = [nsvalue valuewithcgrect:cgrectmake(0, kdecorationviewheight * row, screensize.width, kdecorationviewheight)];
  }
  
  self.bookshelfrectanges = [nsdictionary dictionarywithdictionary:dictionary];
}

#pragma mark runtime layout calculations
- (nsarray *)layoutattributesforelementsinrect:(cgrect)rect
{
  // call super so flow layout can return default attributes for all cells, headers, and footers
  // note: flow layout has already taken care of the cell view layout attributes! :)
  nsarray *array = [super layoutattributesforelementsinrect:rect];
  
  // create a mutable copy so we can add layout attributes for any shelfs that
  // have frames that intersect the rect the collectionview is interested in
  nsmutablearray *newarray = [array mutablecopy];
  //  nslog(@"in rect:%@",nsstringfromcgrect(rect));
  // add any decoration views (shelves) who's rect intersects with the
  // cgrect passed to the layout by the collectionview
  [self.bookshelfrectanges enumeratekeysandobjectsusingblock:^(id key, id shelfrect, bool *stop) {
    //    nslog(@"[shelfrect cgrectvalue]:%@",nsstringfromcgrect([shelfrect cgrectvalue]));
    
    if (cgrectintersectsrect([shelfrect cgrectvalue], rect))
    {
      uicollectionviewlayoutattributes *shelfattributes =
      [self layoutattributesfordecorationviewofkind:fwbookshelfcollectionviewlayoutdecorationviewkind
                       atindexpath:key];
      [newarray addobject:shelfattributes];
    }
  }];
  
  for (int i = 0; i < [self.collectionview numberofitemsinsection:0]; i++)
  {
    nsindexpath *indexpath = [nsindexpath indexpathforitem:i insection:0];
    
    [newarray addobject:[self layoutattributesforitematindexpath:indexpath]];
  }
  
  return [newarray copy];
}

- (uicollectionviewlayoutattributes *)layoutattributesforitematindexpath:(nsindexpath *)indexpath
{
  //  nslog(@"%@", nsstringfromcgsize([self screensize]));375 667
  uicollectionviewlayoutattributes *attris = [uicollectionviewlayoutattributes layoutattributesforcellwithindexpath:indexpath];
  
  nsinteger currentrow = indexpath.item / 3;
  cgrect frame = cgrectmake(20 + (indexpath.item % 3) * (kcellwidth + 17.5), 35+ currentrow * (kcellheight + 65), kcellwidth, kcellheight);
  attris.frame = frame;
  attris.zindex = 1;
  
  return attris;
}

- (uicollectionviewlayoutattributes *)layoutattributesfordecorationviewofkind:(nsstring *)decorationviewkind atindexpath:(nsindexpath *)indexpath
{
  
  id shelfrect = self.bookshelfrectanges[indexpath];
  
  // this should never happen, but just in case...
  if (!shelfrect)
    return nil;
  
  uicollectionviewlayoutattributes *attributes =
  [uicollectionviewlayoutattributes layoutattributesfordecorationviewofkind:decorationviewkind
                                withindexpath:indexpath];
  attributes.frame = [shelfrect cgrectvalue];
  //  nslog(@"uicollectionviewlayoutattributes :.%@", nsstringfromcgrect([shelfrect cgrectvalue]));
  
  attributes.zindex = -1; // shelves go behind other views
  
  return attributes;
}

- (cgsize)collectionviewcontentsize
{
  cgsize contentsize = cgsizemake(self.collectionview.bounds.size.width, self.countofrow * kdecorationviewheight + 20);
  
  return contentsize;
}

@end

四、应用

//
// fwancientpoetrycollectionviewcontroller.m
// fwpersonalapp
//
// created by hzkmn on 16/2/17.
// copyright © 2016年 forrstwoo. all rights reserved.
//



#import "fwancientpoetrycollectionviewcontroller.h"

#import "fwbookshelfdecarationview.h"
#import "fwbookshelfcollectionviewlayout.h"
#import "fwbookcategoryviewcontroller.h"

@interface fwancientpoetrycollectionviewcontroller () <lxreorderablecollectionviewdatasource, lxreorderablecollectionviewdelegateflowlayout>

@property (nonatomic, strong) nsmutablearray *books;

@end

@implementation fwancientpoetrycollectionviewcontroller

static nsstring * const cellreuseidentifier = @"cell";
- (void)viewdidload {
  [super viewdidload];
  self.title = @"古籍";
  self.collectionview.backgroundcolor = [uicolor colorwithpatternimage:[uiimage imagenamed:@"bookshelfbackground.png"]];
  [self.collectionview registerclass:[uicollectionviewcell class] forcellwithreuseidentifier:cellreuseidentifier];
  self.books = [[nsmutablearray alloc] initwitharray:[self booknameofallbooks]];
}

- (nsarray *)booknameofallbooks
{
 return [[fwdatamanager getdataforpoetry] allkeys];
}

#pragma mark <uicollectionviewdatasource>

- (nsinteger)numberofsectionsincollectionview:(uicollectionview *)collectionview
{
  return 1;
}

- (nsinteger)collectionview:(uicollectionview *)collectionview numberofitemsinsection:(nsinteger)section
{
  return [self.books count];
}

- (uicollectionviewcell *)collectionview:(uicollectionview *)collectionview cellforitematindexpath:(nsindexpath *)indexpath
{
  uicollectionviewcell *cell = [collectionview dequeuereusablecellwithreuseidentifier:cellreuseidentifier forindexpath:indexpath];
  uiimage *image = [uiimage imagenamed:self.books[indexpath.item]];
  cell.backgroundcolor = [uicolor colorwithpatternimage:image];

  return cell;
}

- (void)collectionview:(uicollectionview *)collectionview itematindexpath:(nsindexpath *)fromindexpath willmovetoindexpath:(nsindexpath *)toindexpath
{
  nsstring *thebookname = self.books[fromindexpath.item];
  [self.books removeobjectatindex:fromindexpath.item];
  [self.books insertobject:thebookname atindex:toindexpath.item];
}

#pragma mark - uicollectionviewdelegateflowlayout

- (cgsize)collectionview:(uicollectionview *)collectionview layout:(uicollectionviewlayout *)collectionviewlayout sizeforitematindexpath:(nsindexpath *)indexpath
{
  return cgsizemake(kcellwidth, kcellheight);
}

- (void)collectionview:(uicollectionview *)collectionview didselectitematindexpath:(nsindexpath *)indexpath
{
  fwbookcategoryviewcontroller *vc = [[fwbookcategoryviewcontroller alloc] initwithurlstring:[[fwdatamanager getdataforpoetry] objectforkey:self.books[indexpath.item]]];
  [self.navigationcontroller pushviewcontroller:vc animated:yes];
}

- (void)didreceivememorywarning {
  [super didreceivememorywarning];

  [self.books removeallobjects];
  self.books = nil;
}

@end

以上就是本文的全部内容,ios模仿实现书架效果,类似于手机上的掌上阅读软件的首页,希望本文对大家的学习有所帮助。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

  • ios uicollectionview实现横向滚动

    现在使用卡片效果的app很多,之前公司让实现一种卡片效果,就写了一篇关于实现卡片的文章。文章最后附有demo实现上我选择了使用uicollectionview ... [阅读全文]
  • iOS UICollectionView实现横向滑动

    本文实例为大家分享了ios uicollectionview实现横向滑动的具体代码,供大家参考,具体内容如下uicollectionview的横向滚动,目前我使... [阅读全文]
  • iOS13适配深色模式(Dark Mode)的实现

    iOS13适配深色模式(Dark Mode)的实现

    好像大概也许是一年前, mac os系统发布了深色模式外观, 看着挺刺激, 时至今日用着也还挺爽的终于, 随着iphone11等新手机的发售, ios 13系统... [阅读全文]
  • ios 使用xcode11 新建项目工程的步骤详解

    ios 使用xcode11 新建项目工程的步骤详解

    xcode11新建项目工程,新增了scenedelegate这个类,转而将原appdelegate负责的对ui生命周期的处理担子接了过来。故此可以理解为:ios... [阅读全文]
  • iOS实现转盘效果

    本文实例为大家分享了ios实现转盘效果的具体代码,供大家参考,具体内容如下demo下载地址: ios转盘效果功能:实现了常用的ios转盘效果,轮盘抽奖效果的实现... [阅读全文]
  • iOS开发实现转盘功能

    本文实例为大家分享了ios实现转盘功能的具体代码,供大家参考,具体内容如下今天给同学们讲解一下一个转盘选号的功能,直接上代码直接看viewcontroller#... [阅读全文]
  • iOS实现轮盘动态效果

    本文实例为大家分享了ios实现轮盘动态效果的具体代码,供大家参考,具体内容如下一个常用的绘图,主要用来打分之类的动画,效果如下。主要是ios的绘图和动画,本来想... [阅读全文]
  • iOS实现九宫格连线手势解锁

    本文实例为大家分享了ios实现九宫格连线手势解锁的具体代码,供大家参考,具体内容如下demo下载地址:效果图:核心代码://// clockview.m// 手... [阅读全文]
  • iOS实现卡片堆叠效果

    本文实例为大家分享了ios实现卡片堆叠效果的具体代码,供大家参考,具体内容如下如图,这就是最终效果。去年安卓5.0发布的时候,当我看到安卓全新的material... [阅读全文]
  • iOS利用余弦函数实现卡片浏览工具

    iOS利用余弦函数实现卡片浏览工具

    本文实例为大家分享了ios利用余弦函数实现卡片浏览工具的具体代码,供大家参考,具体内容如下一、实现效果通过拖拽屏幕实现卡片移动,左右两侧的卡片随着拖动变小,中间... [阅读全文]
验证码:
移动技术网