Swift自定義UITabBar

前言

很多時候,系統原生的 UITabBar 并不能滿足我們的需求,譬如我們想要給圖標做動態的改變,或者比較炫一點的展示,原生的處理起來都很麻煩。所以很多時候都需要自定義一個 UITabBar,里面的圖標、顏色、背景等等都可以根據需求去改變。

效果展示:

自定義UITabBar

從零開始

先說一下思路

頁面繼承自 UITabBarController ,然后自定義一個 UIView ,添加到 TabBar 上。取消原本的控制按鈕。創建自定義按鈕,即重寫 UIButtonimageView 、和 titleLabelframe ,完成圖片、文字的重新布局。最后實現不同按鈕的協議方法。

效果圖中,只有兩邊的兩個頁面在 UITabBarController 的管理下,中間三個都是通過自定義按鈕實現的模態頁面,即 present 過去的。多用于拍攝圖片、錄制視頻、發表動態等功能。

簡單地展示下 Demo 的文件,因為代碼中會出現圖片名:

Demo文件

代碼實現:
  1. 首先不妨先建立三個基礎文件,然后在豐富代碼。其中, IWCustomButton 繼承自 UIButtonIWCustomTabBarView 繼承自 UIViewIWCustomTabBarController 繼承自 UITabBarController

  2. 修改 AppDelegate 文件中 didFinishLaunchingWithOptions 方法,保證啟動時沒有異常:

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        //  創建Window
        window = UIWindow(frame: UIScreen.main.bounds)
        //  初始化一個tabbar
        let customTabBar = IWCustomTabBarController()
        //  設置根控制器
        window?.rootViewController = customTabBar
        
        window?.makeKeyAndVisible()
        
        return true
    }
  1. 首先在 IWCustomTabBarController 文件中添加代碼:
//  IWCustomTabBarController.swift
import UIKit
class IWCustomTabBarController: UITabBarController {
    
    //  MARK: - Properties
    //  圖片
    fileprivate let tabBarImageNames = ["tb_home","tb_person"]
    fileprivate let tabBarTitles = ["首頁","我的"]

    //  MARK: - LifeCycle
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //  自定義 TabBar 外觀
        createCustomTabBar(addHeight: 0)
        
        //  創建子控制器
        addDefaultChildViewControllers()
        
        //  設置每一個子頁面的按鈕展示
        setChildViewControllerItem()
    }
 
    //  MARK: - Private Methods
    
    /// 添加默認的頁面
   fileprivate func addDefaultChildViewControllers() {
        let vc1 = UIViewController()
        vc1.view.backgroundColor = UIColor.white
        
        let vc2 = UIViewController()
        vc2.view.backgroundColor = UIColor.lightGray
        
        viewControllers = [vc1, vc2]
    }
    
    /// 設置外觀
    ///
    /// - parameter addHeight: 增加高度,0 為默認
    fileprivate let customTabBarView = IWCustomTabBarView()
    fileprivate func createCustomTabBar(addHeight: CGFloat) {
        
        //  改變tabbar 大小
        var oriTabBarFrame = tabBar.frame
        oriTabBarFrame.origin.y -= addHeight
        oriTabBarFrame.size.height += addHeight
        tabBar.frame = oriTabBarFrame
        
        customTabBarView.frame = tabBar.bounds
        customTabBarView.frame.origin.y -= addHeight
        customTabBarView.backgroundColor = UIColor.groupTableViewBackground
        customTabBarView.frame.size.height = tabBar.frame.size.height + addHeight
        customTabBarView.isUserInteractionEnabled = true
        tabBar.addSubview(customTabBarView)
    }
    
    /// 設置子頁面的item項
    fileprivate func setChildViewControllerItem() {
        guard let containViewControllers = viewControllers else {
            print("??  設置子頁面 item 項失敗  ??")
            return
        }
        
        if containViewControllers.count != tabBarImageNames.count {
            fatalError("子頁面數量和設置的tabBarItem數量不一致,請檢查!!")
        }
        
        //  遍歷子頁面
        for (index, singleVC) in containViewControllers.enumerated() {
            singleVC.tabBarItem.image = UIImage(named: tabBarImageNames[index])
            singleVC.tabBarItem.selectedImage = UIImage(named: tabBarImageNames[index] + "_selected")
            singleVC.tabBarItem.title = tabBarTitles[index]
        }
    }
}

上面就是一個基本的純代碼創建的 UITabBarController 的實際效果了,運行后,查看效果:

基本的運行效果

簡單說一下上面的代碼:createCustomTabBar(_: ) 方法傳入的參數主要是為了控制高度,嘗試改變參數值可看到效果。 addDefaultChildViewControllers() 方法是添加子頁面,這里需要說的是并沒有在這個方法里設置文字和圖片,而是選擇重新創建一個方法 setChildViewControllerItem() ,因為后面我們需要獲取頁面有幾個 UITabBarItem
現在明顯的問題就是我們的原始圖片是紅色的,為什么現在都是灰、藍色,因為 UITabBar 使用圖片時渲染了,如果我們需要使用原始圖片,則對 UIImage 方法擴展:

extension UIImage {
    var originalImage: UIImage {
        return self.withRenderingMode(.alwaysOriginal)
    }
}

然后修改遍歷子頁面的代碼:

//  遍歷子頁面
        for (index, singleVC) in containViewControllers.enumerated() {
            singleVC.tabBarItem.image = UIImage(named: tabBarImageNames[index]).originalImage
            singleVC.tabBarItem.selectedImage = UIImage(named: tabBarImageNames[index] + "_selected").originalImage
            singleVC.tabBarItem.title = tabBarTitles[index]
        }

運行后便可查看到原始的圖片效果。

  1. 編寫文件 IWCustomTabBarView :
import UIKit
//  自定義按鈕功能
enum IWCustomButtonOperation {
  case customRecordingVideo       //  錄像
  case customTakePhoto            //  拍照
  case customMakeTape             //  錄音
}
/// 頁面按鈕點擊協議
protocol IWCustomTabBarViewDelegate {
  
  /// 點擊tabBar 管理下的按鈕
  ///
  /// - parameter customTabBarView:     當前視圖
  /// - parameter didSelectedButtonTag: 點擊tag,這個是區分標識
  func iwCustomTabBarView(customTabBarView: IWCustomTabBarView, _ didSelectedButtonTag: Int)
  
  /// 點擊自定義的純按鈕
  ///
  /// - parameter customTabBarView:      當前視圖
  /// - parameter didSelectedOpertaionButtonType: 按鈕類型,拍照、攝像、錄音
  func iwCustomTabBarView(customTabBarView: IWCustomTabBarView, _ didSelectedOpertaionButtonType: IWCustomButtonOperation)
}
class IWCustomTabBarView: UIView {
  //  MARK: - Properties
  //  協議
  var delegate: IWCustomTabBarViewDelegate?
  //  操作按鈕數組
  fileprivate var operationButtons = [IWCustomButton]()
  //  tabbar 管理的按鈕數組
  fileprivate var customButtons = [IWCustomButton]()
  //  自定義按鈕圖片、標題
  fileprivate let operationImageNames = ["tb_normol","tb_normol","tb_normol"]
  fileprivate let operationTitls = ["攝像", "拍照", "錄音"]

  //  MARK: - Init
  override init(frame: CGRect) {
      super.init(frame: frame)
      
      //  添加自定義按鈕
      addOperationButtons()
  }
  
  required init?(coder aDecoder: NSCoder) {
      super.init(coder: aDecoder)
      print("IWCustomTabBarView 頁面 init(coder:) 方法沒有實現")
  }

  /// 布局控件
  override func layoutSubviews() {
      super.layoutSubviews()
      
      //  設置位置
      let btnY: CGFloat = 0
      let btnWidth = bounds.width / CGFloat(subviews.count)
      let btnHeight = bounds.height
      
      //  這里其實就兩個
      for (index, customButton) in customButtons.enumerated() {
          
          switch index {
          case 0:
              customButton.frame = CGRect(x: 0, y: 0, width: btnWidth, height: btnHeight)
              customButton.tag = index
          case 1:
              customButton.frame = CGRect(x: btnWidth * 4, y: 0, width: btnWidth, height: btnHeight)
              customButton.tag = index
          default:
              break
          }
      }
      
      //  這里有三個
      for (index, operBtn) in operationButtons.enumerated() {
          let btnX = (CGFloat(index) + 1) * btnWidth
          operBtn.frame = CGRect(x: btnX, y: btnY, width: btnWidth, height: btnHeight)
      }
  }
  
  //  MARK: - Public Methods
  /// 根據原始的 TabBarItem 設置自定義Button
  ///
  /// - parameter originalTabBarItem: 原始數據
  func addCustomTabBarButton(by originalTabBarItem: UITabBarItem) {
      //  添加初始按鈕
      let customButton = IWCustomButton()
      customButtons.append(customButton)
      addSubview(customButton)
      
      //  添加點擊事件
      customButton.addTarget(self, action: #selector(customButtonClickedAction(customBtn:)), for: .touchUpInside)
      
      //  默認展示第一個頁面
      if customButtons.count == 1 {
          customButtonClickedAction(customBtn: customButton)
      }
  }
  
  //  MARK: - Private Methods
  
  /// 添加操作按鈕
  fileprivate func addOperationButtons() {
      for index in 0 ..< 3 {
          let operationBtn = IWCustomButton()
          operationButtons.append(operationBtn)
          operationBtn.setImage(UIImage(named: operationImageNames[index]), for: .normal)
          operationBtn.setImage(UIImage(named: operationImageNames[index]), for: .highlighted)
          operationBtn.setTitle(operationTitls[index], for: .normal)
          operationBtn.tag = 100 + index
          operationBtn.addTarget(self, action: #selector(operationButtonClickedAction(operBtn:)), for: .touchUpInside)
          addSubview(operationBtn)
      }
  }
  
  ///  操作按鈕點擊事件
  @objc fileprivate func operationButtonClickedAction(operBtn: IWCustomButton) {
      switch operBtn.tag {
      case 100:
          delegate?.iwCustomTabBarView(customTabBarView: self, .customRecordingVideo)
      case 101:
          delegate?.iwCustomTabBarView(customTabBarView: self, .customTakePhoto)
      case 102:
          delegate?.iwCustomTabBarView(customTabBarView: self, .customMakeTape)
      default:
          break
      }
  }
  
  //  保證按鈕的狀態正常顯示
  fileprivate var lastCustomButton = IWCustomButton()
  
  ///  tabbar 管理下按鈕的點擊事件
  @objc fileprivate func customButtonClickedAction(customBtn: IWCustomButton) {
      delegate?.iwCustomTabBarView(customTabBarView: self, customBtn.tag)
      
      lastCustomButton.isSelected = false
      customBtn.isSelected = true
      lastCustomButton = customBtn
  }
}

IWCustomTabBarController 文件的 setChildViewControllerItem() 方法中,修改遍歷子頁面的代碼,獲取當前的 UITabBarItem

// 遍歷子頁面 for (index, singleVC) in containViewControllers.enumerated() { 
singleVC.tabBarItem.image = UIImage(named: tabBarImageNames[index]) 
singleVC.tabBarItem.selectedImage = UIImage(named: tabBarImageNames[index] + "_selected") 
singleVC.tabBarItem.title = tabBarTitles[index]
//  添加相對應的自定義按鈕
            customTabBarView.addCustomTabBarButton(by: singleVC.tabBarItem)
 }

運行后,看到效果好像亂亂的,暫時不用在意,在后面的代碼中會慢慢整理出理想的效果。

亂糟糟的

簡單分析上面的代碼:這里我在中間加入了三個自定義的按鈕。這樣的話,最下面應該是有5個按鈕的。當然也可以加入一個或者兩個等,只需要修改上面對應的數值就可以了。這里面比較主要的就是自定義協議 IWCustomTabBarViewDelegate 和布局方法 layoutSubviews,布局方法里如果能理解兩個 for 循環和對應數組中的數據來源、作用,那么問題就簡單很多了。

這里要說一個屬性 lastCustomButton ,這個屬性會讓我們避免不必要的遍歷按鈕,有些時候多個按鈕只能有一個被選中時,有種常見的方法就是遍歷按鈕數組,令其中一個 isSelected = true ,其他按鈕的 isSelected = false ,而這個屬性就能取代遍歷。

其實存在的問題也很明顯,就是這么寫的話很難去擴展,譬如如果上面的代碼已經完成了,但是臨時需要減少一個自定義按鈕,那么就需要改動多個地方。這里只是提供一種自定義的思路,只是說還有很多可以優化的地方。

  1. 關于自定義的 UIButotn ,是個很有意思的地方。因為視覺上的改變都是在這里發生,先使用默認的設置:
import UIKit
class IWCustomButton: UIButton {

    override init(frame: CGRect) {
        super.init(frame: frame)
        titleLabel?.textAlignment = .center
        setTitleColor(UIColor.gray, for: .normal)
        setTitleColor(UIColor.red, for: .selected)
        titleLabel?.font = UIFont.italicSystemFont(ofSize: 12)
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        print("?????? init(coder:) 方法沒有實現")
    }
    
    /// 根據傳入的 UITabBarItem 設置數據顯示
    ///
    /// - parameter tabBarItem: 數據來源
    func setTabBarItem(tabBarItem: UITabBarItem) {
        setTitle(tabBarItem.title, for: .normal)
        setImage(tabBarItem.image, for: .normal)
        setImage(tabBarItem.selectedImage, for: .highlighted)
        setImage(tabBarItem.selectedImage, for: .selected)
    }
}

修改 IWCustomTabBarView 文件的 addCustomTabBarButton(by: ) 方法:

    //  MARK: - Public Methods
    /// 根據原始的 TabBarItem 設置自定義Button
    ///
    /// - parameter originalTabBarItem: 原始數據
    func addCustomTabBarButton(by originalTabBarItem: UITabBarItem) {
        //  添加初始按鈕
        let customButton = IWCustomButton()
        customButtons.append(customButton)
        addSubview(customButton)
        
        //  傳值
        customButton.setTabBarItem(tabBarItem: originalTabBarItem)
        
        //  添加點擊事件
        customButton.addTarget(self, action: #selector(customButtonClickedAction(customBtn:)), for: .touchUpInside)
        
        //  默認展示第一個頁面
        if customButtons.count == 1 {
            customButtonClickedAction(customBtn: customButton)
        }
    }

看看運行結果:

自定義按鈕后

首先,我們發現了亂的原因,就是自定義的按鈕和原本的 UITabBarItem 的顯示起了沖突。那么先修改這個問題:在 IWCustomTabBarController 方法中頁面即將出現時添加方法:

  override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        //  移除原生的 TabBarItem ,否則會出現覆蓋現象
        tabBar.subviews.forEach { (subView) in
            if subView is UIControl {
                subView.removeFromSuperview()
            }
        }
    }

那么上面重復顯示的原生項此時就移除了。下一個問題:發現自定義按鈕圖像的大小不一致。其實中間圖片本身的大小就是比兩邊的大的。以 2x.png 為例,中間的圖標是 70x70,而兩邊的是 48x48。如果在沒有文字顯示的情況下,在按鈕的初始化方法中添加 imageView?.contentMode = .center ,圖片居中展示,自定義按鈕到這個地方就可以結束了(可以嘗試不要 title ,查看運行效果)。甚至可以在自定義按鈕的初始化方法里使用仿射變換來放大、縮小圖片。

這里為了控制圖片、文字的位置,重寫 UIButton 的兩個方法:

    /// 重寫 UIButton 的 UIImageView 位置
    ///
    /// - parameter contentRect: 始位置
    ///
    /// - returns: 修改后
    override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
        let imageWidth = contentRect.size.height * 4 / 9
        let imageHeight = contentRect.size.height
        return CGRect(x: bounds.width / 2 - imageWidth / 2, y: imageHeight / 9, width: imageWidth, height: imageWidth)
    }
    
    /// 重寫 UIButton 的 TitleLabel 的位置
    ///
    /// - parameter contentRect: 原始位置
    ///
    /// - returns: 修改后
    override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
        let titleWidth = contentRect.size.width
        let titleHeight = contentRect.size.height / 3
        return CGRect(x: bounds.width / 2 - titleWidth / 2, y: bounds.height - titleHeight, width: titleWidth, height: titleHeight)
    }  

對上面代碼做簡單地說明,首先說方法中 contentRect 這個變量,它的 size 是這個 UIButton 的大小,而不是單獨的 UIImageView ,或者 titleLabel 的大小。上面的一些具體數值,譬如 4 / 9 等這種奇葩的比例數值,僅僅是我根據自己的審美觀隨便寫入的一些數值,至于到具體的開發中,可以固定大小,也可以使用更加細致的比例,因為 tabBar 默認的高度是 49 ,那么很多數據就可以使用了。現在看看效果:

修改自定義按鈕后
  1. IWCustomTabBarController 文件中實現 IWCustomTabBarView 文件中的協議方法,首先添加協議,然后實現方法,別忘了令 customTabBarView.delegate = self
    //  MARK: - IWCustomTabBarViewDelegate
    ///  點擊 tabbar 管理下的按鈕
    func iwCustomTabBarView(customTabBarView: IWCustomTabBarView, _ didSelectedButtonTag: Int) {
        selectedIndex = didSelectedButtonTag
    }
    
    ///  點擊自定義添加的的按鈕
    func iwCustomTabBarView(customTabBarView: IWCustomTabBarView, _ didSelectedOpertaionButtonType: IWCustomButtonOperation) {
        switch didSelectedOpertaionButtonType {
        case .customRecordingVideo:
            print("攝像")
            let vc = UIViewController()
            vc.view.backgroundColor = UIColor.orange
            addBackButton(on: vc.view)
            present(vc, animated: true, completion: nil)
        case .customTakePhoto:
            print("拍照")
            let vc = UIViewController()
            vc.view.backgroundColor = UIColor.green
            addBackButton(on: vc.view)
            present(vc, animated: true, completion: nil)
        case .customMakeTape:
            print("錄音")
            let vc = UIViewController()
            vc.view.backgroundColor = UIColor.cyan
            addBackButton(on: vc.view)
            present(vc, animated: true, completion: nil)
        }
    }
    
    fileprivate func addBackButton(on superView: UIView) {
        let btn = UIButton()
        btn.frame = CGRect(x: 100, y: 100, width: 100, height: 50)
        btn.backgroundColor = UIColor.blue
        btn.setTitle("返回", for: .normal)
        btn.setTitleColor(UIColor.white, for: .normal)
        btn.addTarget(self, action: #selector(dismissAction), for: .touchUpInside)
        superView.addSubview(btn)
    }
    @objc func dismissAction() {
        dismiss(animated: true, completion: nil)
    }

上面的代碼,只單獨說一點,就是協議方法 iwCustomTabBarView(customTabBarView : , _ didSelectedButtonTag) 中, selectedIndex 這個屬性并非我們自己定義的變量,而是系統設置的,所以這時候 didSelectedButtonTag 所代表值就顯得很有意思了,它正是我們在 UITabBar 管理下 ViewController 是下標值。看看這時候的效果吧:

完成后
  1. 最后再說一點,有時候我們需要給自定義的 IWCustomTabBarView 添加背景圖片,那么這時候會出現一個問題,就是原本的 TabBar 的淺灰色背景始終會有一條線,此時在 IWCustomTabBarController 文件的 viewDidLoad() 方法中添加下面的代碼即可。
        //  去除 TabBar 陰影
        let originalTabBar = UITabBar.appearance()
        originalTabBar.shadowImage = UIImage()
        originalTabBar.backgroundImage = UIImage()

完了

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容

  • TabBarController是項目主要框架結構, 一般來說系統自帶的TabBarController已足夠使用...
    wolf_吳郎閱讀 3,190評論 0 2
  • 重要:這是針對于正在開發中的API或技術的預備文檔(預發布版本)。蘋果提供這份文檔的目的是幫助你按照文中描述的方式...
    金_波閱讀 2,024評論 3 13
  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,153評論 4 61
  • 新學期開始啦!我們班分到了一位和藹可親的新老師——楊育慧。 一開始,我們大家都以為楊老師是年過半...
    歡樂洋閱讀 374評論 0 0
  • 那年的倫敦不知道是什么樣的,傻子眼里只有光芒和神 穿透昏沉的霧的夜的流星是 跛子逃離的痕跡,軌跡 清晰可見到一點黑...
    pleasen閱讀 140評論 0 0