UICollectionView 07 - 標(biāo)簽布局

文章按照順序?qū)懙淖柚祝拔恼聦戇^的很多邏輯都會略過友瘤,建議順序閱讀图呢,并下載源碼結(jié)合閱讀。

目錄

項(xiàng)目下載地址: CollectionView-Note

UICollectionView 01 - 基礎(chǔ)布局篇
UICollectionView 02 - 布局和代理篇
UICollectionView 03 - 自定義布局原理篇
UICollectionView 04 - 卡片布局
UICollectionView 05 - 可伸縮Header
UICollectionView 06 - 瀑布流布局
UICollectionView 07 - 標(biāo)簽布局

上一篇的瀑布流只針對cell的自定義布局腰素,這篇為了全面標(biāo)簽布局針對了 整體Header 、sectionHeader 和 cell都做了布局仁烹。 SupplementaryViewtableview 的 header和footer不同耸弄, 我們一個(gè)section可以有任意多個(gè)SupplementaryView ,我們可以自己管理他們的位置卓缰。本篇需要實(shí)現(xiàn)的效果如下

000

所有顏色都使用隨機(jī)色计呈,標(biāo)簽根據(jù)文字大小決定砰诵,一行顯示不下自動(dòng)換行。并添加了可伸縮Header和sectionHeader捌显。 對刪除和新增做了自定義動(dòng)畫茁彭。 算一個(gè)比較全面的例子。下面看下實(shí)現(xiàn)邏輯過程扶歪。

首先我們做一些數(shù)據(jù)準(zhǔn)備理肺,這里不是重點(diǎn) 提一下,大家可以下載代碼查看善镰。

  // 1
  let randomText = "黑發(fā)不知勤學(xué)早白首方悔讀書遲遲日江山麗春風(fēng)花草香杜甫絕句春色滿園關(guān)不住一枝紅杏出墻來葉紹翁游園不值好雨知時(shí)節(jié)當(dāng)春乃發(fā)生杜甫春雨夏天小荷才露尖尖角早有蜻蜓立上頭楊萬里小池接天蓮葉無窮碧映日荷花別樣紅"
  
  // 2
  func genernalText() -> String{
    let textCount = randomText.count
    let randomIndex = arc4random_uniform(UInt32(textCount))
    let start = max(0, Int(randomIndex)-7)
    let startIndex = randomText.startIndex
    let step = arc4random_uniform(5) + 2 // 2到5個(gè)字
    let startTextIndex = randomText.index(startIndex, offsetBy: start)
    let endTexIndex = randomText.index(startIndex, offsetBy: start + Int(step))
    let text = String(randomText[startTextIndex ..< endTexIndex])
    return text
  }
  
  // 3
  func generalTags() -> [[String]]{
    var tags1: [String] = []
    var tags2: [String] = []
    var tags3: [String] = []
    
    for i in 0..<50 {
      if i%3 == 0 {
        tags1.append(genernalText())
      }
      if i%2 == 0{
        tags2.append(genernalText())
      }
      tags3.append(genernalText())
    }
    return [tags1,tags2,tags3]
  }
  1. 聲明一長串文字
  2. 從長文中隨機(jī)產(chǎn)生一個(gè)2-5個(gè)字的文本
  3. 因?yàn)槭欠纸M這里生成三組不同長度的數(shù)組 組成一個(gè)二維數(shù)組 作為數(shù)據(jù)源妹萨。

然后像之前章節(jié)一樣 Storyboard 中創(chuàng)建一個(gè) TagViewController , 聲明 collectionView 炫欺, 新建一個(gè) TagLayout , 替換自帶的 flowLayout (具體替換方法參照之前文章)

在我們的 TagLayout 有一個(gè)變數(shù)就是文本的長度乎完,這個(gè)我們可以根據(jù)文本的字體和Text內(nèi)容計(jì)算出。為了使用便捷這里提供一個(gè)代理方法 (建議下載源碼結(jié)合查看)

protocol TagLayoutDelegate: class {
  func collectionView(_ collectionView: UICollectionView, TextForItemAt indexPath: IndexPath) -> String
}

根據(jù) indexPath 返回對應(yīng)的文本 品洛。

TagLayout 頂部添加一個(gè)枚舉

enum Element {
   case cell
   case header
   case sectionHeader
}

包含了 后面我們要自定義位置的三個(gè)元素

添加一個(gè)變量和常量

// 標(biāo)簽的內(nèi)邊距
var tagInnerMargin: CGFloat = 25
// 元素間距
var itemSpacing: CGFloat = 10
// 行間距
var lineSpacing: CGFloat = 10
// 標(biāo)簽的高度
var itemHeight: CGFloat = 25
// 標(biāo)簽的字體
var itemFont: UIFont = UIFont.systemFont(ofSize: 12)
// header的高度
var headerHeight: CGFloat = 150
// sectionHeader 高度
var sectionHeaderHeight: CGFloat = 50
// header的類型
let headerKind = "ElementTagHeader"

weak var delegate: TagLayoutDelegate?

頂部的header為了區(qū)分系統(tǒng)的 elementKindSectionHeader 我們自定義了一種kind树姨。

然后定義一些私有變量。

// 緩存
private var cache = [Element: [IndexPath: UICollectionViewLayoutAttributes]]()
// 可見區(qū)域
private var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
// 內(nèi)容高度
private var contentHeight: CGFloat = 0
// 用來記錄新增的元素
private var insertIndexPaths = [IndexPath]()
// 用來記錄刪除的元素
private var deleteIndexPaths = [IndexPath]()}

// MARK: - 一些計(jì)算屬性 防止編寫冗余代碼
  
private var collectionViewWidth: CGFloat {
  return collectionView!.frame.width
}

本篇中的緩存按照枚舉類型進(jìn)行了區(qū)分桥状,但是實(shí)質(zhì)還是差不多的帽揪。

下面開始具體的布局信息計(jì)算和緩存 。

override func prepare() {
    // 1
    guard let collectionView = self.collectionView , let delegate = delegate else { return }
    let sections = collectionView.numberOfSections
    // 2 
    prepareCache()
    contentHeight = 0
    
    // 3
    // 可伸縮header
    let headerIndexPath = IndexPath(item: 0, section: 0)
    let headerAttribute = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: headerKind, with: headerIndexPath)
    let frame = CGRect(x: 0, y: 0, width: collectionViewWidth, height: headerHeight)
    headerAttribute.frame = frame
    cache[.header]?[headerIndexPath] = headerAttribute
    contentHeight = frame.maxY
    
    // 4
    for section in 0 ..< sections {
      // 處理sectionHeader
      let sectionHeaderIndexPath = IndexPath(item: 0, section: section)
      
      // 5
      let sectionHeaderAttribute = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, with: sectionHeaderIndexPath)
      var sectionOriginY = contentHeight
      if section != 0 {
        sectionOriginY += lineSpacing
      }
      let sectionFrame = CGRect(x: 0 , y: sectionOriginY , width: collectionViewWidth , height: sectionHeaderHeight)
      sectionHeaderAttribute.frame = sectionFrame
      cache[.sectionHeader]?[sectionHeaderIndexPath] = sectionHeaderAttribute
      contentHeight = sectionFrame.maxY
      
      // 6
      // 處理tag
      let rows = collectionView.numberOfItems(inSection: section)
      var frame = CGRect(x: 0, y: contentHeight + lineSpacing, width: 0, height: 0)
      
      for item in 0 ..< rows {
        let indexPath = IndexPath(item: item, section: section)
        // 7
        let text = delegate.collectionView(collectionView, TextForItemAt: indexPath)
        let tagWidth = self.textWidth(text) + tagInnerMargin
        // 8
        // 其他
        if frame.maxX + tagWidth + itemSpacing*2 > collectionViewWidth {
          // 需要換行
          frame = CGRect(x: itemSpacing , y: frame.maxY + lineSpacing , width: tagWidth, height: itemHeight)
        }else{
          frame = CGRect(x: frame.maxX + itemSpacing, y: frame.origin.y , width: tagWidth , height: itemHeight)
        }
        // 9
        let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
        attributes.frame = frame
        cache[.cell]?[indexPath] = attributes
      }
      // 10 
      contentHeight = frame.maxY
    }
}

private func prepareCache() {
  cache.removeAll(keepingCapacity: true)
  cache[.sectionHeader] = [IndexPath: UICollectionViewLayoutAttributes]()
  cache[.cell] = [IndexPath: UICollectionViewLayoutAttributes]()
  cache[.header] = [IndexPath: UICollectionViewLayoutAttributes]()
}

// 根據(jù)文字 確定label的寬度
private func textWidth(_ text: String) -> CGFloat {
  let rect = (text as NSString).boundingRect(with: .zero, options: .usesLineFragmentOrigin, attributes: [.font: self.itemFont], context: nil)
  return rect.width
}

這段代碼有點(diǎn)長辅斟,我們一一解釋转晰。

  1. 可選綁定,并獲取section的數(shù)量
  2. 一些初始化
  3. 處理頂部的可伸縮header然后加入緩存并更新contentHeight 士飒, 這里使用了我們的自定義類型headerKind 挽霉。
  4. 遍歷section 準(zhǔn)備處理每個(gè)section中的內(nèi)容
  5. 處理sectionHeader 以系統(tǒng)elementKindSectionHeader 作為kind 。 并加入緩存更新 contentHeight
  6. 獲取到某個(gè)section對用的cell個(gè)數(shù)变汪。初始化一個(gè)frame以之前的 contentHeight + 行間距 lineSpacing 起步
  7. 獲取每個(gè)元素的text 侠坎, 然后計(jì)算出對應(yīng)的寬度,加上內(nèi)邊距得到元素的寬度 tagWidth
  8. 如果frame的最大x左邊加上此元素的寬度和兩個(gè)元素邊距大于 collectionViewWidth 裙盾,需要換行顯示实胸。否則追加在此行。 重置frame的值
  9. 將frame值賦值給UICollectionViewLayoutAttributes 并緩存
  10. 某個(gè)section的cell計(jì)算完之后用最后一個(gè)元素的frame更新 contentHeight

雖然代碼多番官,但是邏輯并不復(fù)雜庐完。都是一些加減計(jì)算。 ok徘熔,緩存準(zhǔn)備好了 之后的變很輕松了门躯。

// 1
override var collectionViewContentSize: CGSize {
    return CGSize(width: collectionViewWidth, height: contentHeight)
}

// 2
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    return cache[.cell]?[indexPath]
}
  
// 3
override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    switch elementKind {
    case UICollectionView.elementKindSectionHeader:
      return cache[.sectionHeader]?[indexPath]
    case headerKind:
      return cache[.header]?[indexPath]
    default:
      return nil
    }
}
  1. 返回可滾動(dòng)區(qū)域 collectionViewContentSize
  2. 返回cell對應(yīng)indexPath的 UICollectionViewLayoutAttributes
  3. 返回SupplementaryView 對應(yīng) kind 和 indexPath的 UICollectionViewLayoutAttributes
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    visibleLayoutAttributes.removeAll(keepingCapacity: true)
    for (type , elementInfos) in cache {
      for (_ , attributes) in elementInfos where attributes.frame.intersects(rect){
        // 為可伸縮header
        if let deltalY = self.calculateDeltalY() , type == .header {
          var headerRect = attributes.frame
          headerRect.size.height = headerRect.height + deltalY
          headerRect.origin.y = headerRect.origin.y - deltalY
          attributes.frame = headerRect
        }
        visibleLayoutAttributes.append(attributes)
      }
    }
    return visibleLayoutAttributes
}

// 計(jì)算可伸縮高度
private func calculateDeltalY() -> CGFloat?{
    guard let collectionView = self.collectionView else { return nil }
    let insets = collectionView.contentInset
    let offset = collectionView.contentOffset
    let minY = -insets.top
    
    if offset.y < minY {
      let deltalY = abs(offset.y - minY)
      return deltalY
    }
    return nil
}

layoutAttributesForElements 在前幾篇已經(jīng)用過好多次了,就是item將要展示的時(shí)候?qū)⑺麄兊腢ICollectionViewLayoutAttributes返回酷师。這里結(jié)合了可伸縮Header那篇對header進(jìn)行了處理讶凉。

ok到這里染乌,整個(gè)布局篇已經(jīng)寫好了。我們回到TagViewController中懂讯,新建一個(gè)cell TagCell荷憋。 只有一個(gè)label在充滿整個(gè)cell。設(shè)置字體和layout中保持一致 褐望。

class TagCell: UICollectionViewCell {
  
  static let reuseID = "tagCell"
  
  @IBOutlet weak var tagLabel: UILabel!
  
  var value: String = "" {
    didSet{
      tagLabel.text = value
    }
  }
  
  override func awakeFromNib() {
    super.awakeFromNib()
    backgroundColor = UIColor.randomColor()
    tagLabel.font = UIFont.systemFont(ofSize: 12)
    tagLabel.textColor = UIColor.white
  }
  
}

header沿用了之前的勒庄。 在viewDidLoad中進(jìn)行注冊

collectionView.register(UINib(nibName: "ImageHeaderView", bundle: nil), forSupplementaryViewOfKind: headerKind , withReuseIdentifier: ImageHeaderView.reuseID)
collectionView.register(UINib(nibName: "BasicsHeaderView", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: BasicsHeaderView.reuseID)

這里的ImageHeaderView 所使用的kind是layout中定義的,在Controller中添加計(jì)算屬性

var headerKind: String {
    return layout?.headerKind ?? ""
}

然后再使用Header的時(shí)候也要區(qū)分對待

func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
    switch kind {
    case UICollectionView.elementKindSectionHeader:
      let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: BasicsHeaderView.reuseID, for: indexPath) as! BasicsHeaderView
      view.titleLabel.text = "HEADER -- \(indexPath.section)"
      view.backgroundColor = UIColor.randomColor()
      return view
    case headerKind:
      let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: ImageHeaderView.reuseID, for: indexPath) as! ImageHeaderView
      return view
    default:
      fatalError("No such kind")
    }
}

別忘了實(shí)現(xiàn)Layout的代理

// MARK: - TagLayoutDelegate

extension TagViewController: TagLayoutDelegate {
  
  func collectionView(_ collectionView: UICollectionView, TextForItemAt indexPath: IndexPath) -> String {
    return tags[indexPath.section][indexPath.row]
  }
  
}

其他的基礎(chǔ)代碼和之前無差瘫里,只是換了數(shù)據(jù)源实蔽。

這時(shí)候運(yùn)行所有布局已經(jīng)完成了。

那么如果添加動(dòng)畫呢谨读?也是非常簡單的盐须,記得我們之前聲明了兩個(gè)變量,存儲新增和刪除的元素

TagLayout 中漆腌,添加如下方法 用于記錄新增和刪除的元素。

override func prepare(forCollectionViewUpdates updateItems: [UICollectionViewUpdateItem]) {
    super.prepare(forCollectionViewUpdates: updateItems)
    self.insertIndexPaths.removeAll()
    self.deleteIndexPaths.removeAll()
    for update in updateItems {
      switch update.updateAction {
      case .insert:
        if let indexPath = update.indexPathAfterUpdate {
          self.insertIndexPaths.append(indexPath)
        }
      case .delete:
        if let indexPath = update.indexPathBeforeUpdate {
          self.deleteIndexPaths.append(indexPath)
        }
      default:break
      }
    }
}

然后 用另外兩個(gè)方法去執(zhí)行動(dòng)畫

/// MARK: 動(dòng)畫相關(guān)
  
override func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    guard let attribute = super.initialLayoutAttributesForAppearingItem(at: itemIndexPath) else { return nil }
    if self.insertIndexPaths.contains(itemIndexPath) {
      attribute.transform = CGAffineTransform.identity.scaledBy(x: 4, y: 4).rotated(by: CGFloat(Double.pi/2))
    }
    return attribute
}
  
override func finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    guard let attribute = super.finalLayoutAttributesForDisappearingItem(at: itemIndexPath) else { return nil }
    if self.deleteIndexPaths.contains(itemIndexPath) {
      attribute.transform = CGAffineTransform.identity.scaledBy(x: 4, y: 4).rotated(by: CGFloat(Double.pi/2))
    }
    return attribute
}

initial 和 final的方法還有針對SupplementaryView 的阶冈,本篇并不打算演示闷尿,大家可以自行嘗試。

然后再Storyboard中拖出兩個(gè)控件處理新增和刪除

@IBAction func addTag(_ sender: Any) {
    // 隨機(jī)添加一個(gè)tag
    let text = DataManager.shared.genernalText()
    tags[0].append(text)
    let indexPath = IndexPath(item: tags[0].count - 1, section: 0)
    collectionView.insertItems(at: [indexPath])
  }
  
  
  @IBAction func deleteTag(_ sender: Any) {
    let count = tags[0].count
    if count == 0 {
      return
    }
    let indexPath = IndexPath(item: count - 1, section: 0)
    self.tags[0].remove(at: indexPath.row)
    collectionView.performBatchUpdates({ [ weak self] in
      guard let `self` = self else { return }
      self.collectionView.deleteItems(at: [indexPath])
    }, completion: nil)
  }

collectionView 可以使用 performBatchUpdates 處理一系列的操作女坑,比如新增刪除移動(dòng)等填具。并可以處理回調(diào)。

ok , 運(yùn)行 不出意外應(yīng)該完美匆骗。 有問題的仔細(xì)下載代碼查看劳景。或者評論交流碉就。

000

本系列完結(jié)盟广。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市瓮钥,隨后出現(xiàn)的幾起案子筋量,更是在濱河造成了極大的恐慌,老刑警劉巖碉熄,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桨武,死亡現(xiàn)場離奇詭異,居然都是意外死亡锈津,警方通過查閱死者的電腦和手機(jī)呀酸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來琼梆,“玉大人性誉,你說我怎么就攤上這事窿吩。” “怎么了艾栋?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵爆存,是天一觀的道長。 經(jīng)常有香客問我蝗砾,道長先较,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任悼粮,我火速辦了婚禮闲勺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扣猫。我一直安慰自己菜循,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布申尤。 她就那樣靜靜地躺著癌幕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪昧穿。 梳的紋絲不亂的頭發(fā)上勺远,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機(jī)與錄音时鸵,去河邊找鬼胶逢。 笑死,一個(gè)胖子當(dāng)著我的面吹牛饰潜,可吹牛的內(nèi)容都是我干的初坠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼彭雾,長吁一口氣:“原來是場噩夢啊……” “哼碟刺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起薯酝,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤南誊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蜜托,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抄囚,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年橄务,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了幔托。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖重挑,靈堂內(nèi)的尸體忽然破棺而出嗓化,到底是詐尸還是另有隱情,我是刑警寧澤谬哀,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布刺覆,位于F島的核電站,受9級特大地震影響史煎,放射性物質(zhì)發(fā)生泄漏谦屑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一篇梭、第九天 我趴在偏房一處隱蔽的房頂上張望氢橙。 院中可真熱鬧,春花似錦恬偷、人聲如沸悍手。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坦康。三九已至,卻和暖如春诡延,著一層夾襖步出監(jiān)牢的瞬間滞欠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工孕暇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赤兴。 一個(gè)月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓妖滔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親桶良。 傳聞我的和親對象是個(gè)殘疾皇子座舍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 第二十一章—— 天道好輪回,蒼天饒過誰陨帆。 這是熱巴坐上高鐵看著自己出汗的手內(nèi)心無言的感慨曲秉。 車外的風(fēng)景盡數(shù)向她身后...
    南伊伊閱讀 155評論 0 0
  • 我家樓下的一個(gè)女生,腿部有殘疾疲牵,長得也不漂亮承二,就是本地戶口家里幾套小房子,父母做點(diǎn)生意纲爸,有一點(diǎn)小錢吧亥鸠,算不上富二...
    拂塵周周周閱讀 85評論 0 2
  • 從16世紀(jì)時(shí)负蚊,西班牙和葡萄牙殖民者將金剛鸚鵡帶回歐洲后神妹,它們便變成了人們的好朋友。現(xiàn)已列入《世界自然保護(hù)聯(lián)盟》 2...
    北侖情閱讀 607評論 0 0
  • “那個(gè)誰家妆,你談女朋友了嗎鸵荠?”李經(jīng)理問到,小王回答說“李經(jīng)理伤极,我談了呀蛹找!好多年了,怎么啦塑荒?”熄赡,“談了就好辦,我看你最...
    努力學(xué)習(xí)的清梅閱讀 941評論 4 5
  • 1. 這本書是我喜歡的財(cái)經(jīng)作家吳曉波老師寫的齿税。是繼十年前的《激蕩三十年》之后彼硫,又一本對過去這十年的一個(gè)盤點(diǎn)和總結(jié)。...
    珉二少閱讀 519評論 7 6