UIScrollView 教程:入门指南

2017-07-22

作者:Owen Brown,原文链接,原文日期:2017-07-21

译者:Chenghui Bai;校对:Chenghui Bai;定稿:Chenghui Bai

image.png

学习如何使用UISCrollView进行分页,缩放,滚动,等等。。。!

更新日志:我们用Xcode9.0和Swift4.0对这个教程进行了更新。原著是Ray Wenderlich.

UIScrollView 在iOS中一个很常用的控件.是 UITableView 的基类,使用它展示超过一个屏幕内容是很好的方法。在这个 UIScrollView 教程中,将建立一个类似相册的app,并且将学习到关于UIScrollView 的一切。

你讲学会:

.如何使用UIScrollView 去缩放并查看一张很大的图片。

.如何保证以UIScrollView 的内容为中心进行缩放。

.如何使用UIScrollView自动布局进行竖直方向滚动。

.如何保证当输入控件是可见的(不会被键盘挡住),当键盘弹出来的时候。

.如何使用UIPageViewController去实现多个页面内容的滚动。

本教程假设你已经知道如何使用 Interface Builder 添加一个对象,通过代码和storyboard连线。如果你还不熟悉怎么操作,可以参考之前的教程: Storyboards tutorial.

入门指南

点击 here 为 UIScrollView 教程下载启动项目,打开Xcode.编译然后运行看看是啥样:

image.gif

你选择一张照片去看他的全部尺寸,很遗憾,你不能看见整张图片由于设备尺寸的限制。如果你真的想像相册app那样在屏幕的默认

尺寸下吧整张图片呈现,并缩放去看细节,能做到吗?是的,你可以。

滚动和缩放一张大图

揭开本 UIScrollView 课程序幕,你将创建一个scrollview让用户可以对图像进行缩放和平移。

打开Main.storyboard,从右下角的Document Outline 中拖拽一个scrollView到Zoomed Photo View Controller,然后,拖一个imageView到scrollView中 ,你的Document Outline 现在应该是下面这样:

image.png

看见红色的点了吗?这是Xcode编译时告诉你对不符合Auto Layout的规则。

去解决它,选中scrollview并且点击位于storyboard 窗口底部的大头针按钮,添加上下左右的约束,设置约束值为0,不要有间距。像下面这样:

image.png

现在,选中imageView,同样给他添加约束。

之后,如果你发现一个Auto Layout的警告,就在Document Outline 选中 Zoomed Photo View Controller 然后选择编辑或解决约束问题,或更新frame,如果你没有发现警告,Xcode可能已经为你自动更新了frame,因此你什么也不需要做,直接编译运行。

image.gif

感谢scrollView,你现在可以通过滑动看全尺寸图片,但是如果你想看到图片缩放来使用屏幕,或者想看到放大缩小?

你需要为他写代码!

打开 ZoomedPhotoViewController.swift,在类的申明中添加:

@IBOutlet weak var scrollView: UIScrollView!

@IBOutlet weak var imageViewBottomConstraint: NSLayoutConstraint!

@IBOutlet weak var imageViewLeadingConstraint: NSLayoutConstraint!

@IBOutlet weak var imageViewTopConstraint: NSLayoutConstraint!

@IBOutlet weak var imageViewTrailingConstraint: NSLayoutConstraint!

回到 Main.storyboard, 设置scrollView的outlet到scrollView,并且设置代码到Zoomed View Controller. 同样,链接新的约束的outlets与 Document Outline 的约束相关联。像这样:

image.png

回到ZoomedPhotoViewController.swift, 在文件接尾添加如下代码:

extension ZoomedPhotoViewController: UIScrollViewDelegate {

  func viewForZooming(in scrollView: UIScrollView) -> UIView? {

    return imageView

  }

}

这让 ZoomedPhotoViewController 遵循UIScrollViewDeleate 并且实现viewForZooming(in:) 方法。这个方法在告诉scrollView的哪个子视图需要捏合缩放,在这告诉需要缩放的是imageview.

下一步,在viewDidLoad():方法中添加如下代码:

fileprivate func updateMinZoomScaleForSize(_ size: CGSize) {

  let widthScale = size.width / imageView.bounds.width

  let heightScale = size.height / imageView.bounds.height

  let minScale = min(widthScale, heightScale)

  scrollView.minimumZoomScale = minScale   

  scrollView.zoomScale = minScale

}

这个方法为scrollView计算缩放scale.当scale为1表示内容按正常尺寸显示。当scale小于1内容缩小显示,当scale大于1内容放大显示。

为了获得最小缩放比scale,首先计算imageview紧贴scrollView所需的宽度比,然后计算高度比,然后在scrollView中取他们中最小值作为缩放比,这样,你将会看见图片完全放大。

由于 maximumZoomScale 默认为1,你不需要设置他,如果你设置大于1,放大图片可能会模糊,如果设置小于1,将放大不到图片本身的分辨率。

最后,在每次控制器更新子视图时,你需要更新最小缩放比。在前面的方法之前,添加下面代码:

override func viewWillLayoutSubviews() {

  super.viewWillLayoutSubviews()

  updateMinZoomScaleForSize(view.bounds.size)

}

编译运行,结果应该和下面一样:

image.gif

现在你可以捏合缩放了,并且图片最初是适配屏幕的。真棒!

然而,现在还有一个问题:图像被固定到滚动视图的顶部,如果是根据中心点固定会更好,对吧?

还是在ZoomedPhotoViewController.swift,在viewForZooming(in:)方法后面添加代码:

func scrollViewDidZoom(_ scrollView: UIScrollView) {

  updateConstraintsForSize(view.bounds.size)

}

fileprivate func updateConstraintsForSize(_ size: CGSize) {

  let yOffset = max(0, (size.height - imageView.frame.height) / 2)

  imageViewTopConstraint.constant = yOffset

  imageViewBottomConstraint.constant = yOffset

  let xOffset = max(0, (size.width - imageView.frame.width) / 2)

  imageViewLeadingConstraint.constant = xOffset

  imageViewTrailingConstraint.constant = xOffset

  view.layoutIfNeeded()

}

用户每次滚动scrollView都会调用scrollViewDidZoom方法,像应的,会调用你的updateConstraintsForSize(_:) 方法通过传view的size.

updateConstraintsForSize(_:) 在scrollView中比较麻烦的是:如果滚动视图的内容尺寸小于边界,内容放在左上而不是中心。

在这适配imageview的布局约束。你计算图像垂直方向的第一个中心点是从view的高度减去ImageView的高度,然后除以2.这个值作为imageview的top和bottom的外间距约束。相似的,您可以计算一个左右的offset去约束imageView的宽度。

给自己一点表扬,编译运行你的项目!选一个图片,如果一切顺利的话,你会得到一个可爱的图像,你可以缩放和平移。

image.gif

竖直方向滚动

现在假设你想改变photoscroll显示图像的顶部添加留言吧。取决于评论的长度,您可能会得到比您的设备所能显示的更多的文本:ScrollView可以帮你做到!

注意:通常情况下,Auto Layout 考虑上下左右距离一个视图的间距,而 UIScrollView 通过改变他的原始bounds来滚动他的内容。使用 Auto Layout,scrollVIew的边实际上指的是contentView的边缘。

用自动布局来调整滚动视图的frame,有关scrollView的宽度和高度的约束必须明确,否则scrollView的边缘必须绑定到其子树之外的视图。

在这 this technical note 你可以知道的更多。

下面你将学习怎么使用Auto Layout 来 fix scrollView的width,实际上他是contentSize的width.

Scroll View 和 Auto Layout

打开Main.storyboard 布局一个页面。

首先,添加一个View Controller.在Simulated Size,用Freeform替换掉Fixed,宽高设置为 340 * 800.

image.png

你会注意到这个控制器又窄又长,模拟一个竖直方向很长的内容。这样模拟有助于你排列Interface Builder.他没有运行时效果。

在最新创建的控制器中的 Attributes Inspector 取消勾选Adjust Scroll View Insets。

image.png

添加一个scrollView,填充这个控制器。

设置左右约束为0,并取消勾选 Constrain to margin.设置上下约束也为0 。

添加一个view作为scrollView的子视图,并填充整个scrollView.然后重新命名为 Container View. 和之前一样,设置上下左右约束为0,并取消勾选 Constrain to Margins.

为了修复Auto Layout 的错误,你需要设置scrollView的size.设置Container View 的宽度和控制器宽度相等。Container View 的高度约束设置为500.

注意:Auto Layout rules 有一条规则就是scrollView的contentSize必须确定。这是使用Auto Layout 设置scrollView size很关键的一步。

添加一个imageView到 Container View.

在 Attributes Inspector 中,为imageView设置图片名为phone1的图片,选择 Aspect Fit模式,并勾选clips to bounds.

image.png

和之前一样,添加上左右约束,高度约束设置为300.

在imageView下面添加一个Lable视图,设置文本内容为“What name fits me best?”添加相对Container View的水平居中的约束。设置相对Photo View竖直方向间距为0的约束。

在Container View中的Lable下面添加一个TextField视图,左右间距相对Container View的约束为8。竖直方向相对Lable约束为30.

下一步,你需要连线一个segue 到最新创建的控制器。

到这,首先删除 Photo Scroll与Zoomed Photo View Controller 之间已存在的segue,不要当心,在控制器上所做的所有工作稍后将被添加到您的应用程序中。

在Photo Scroll 页中,从PhotoCell 拖线连接到最新的控制器,添加一个segue,并设置identifier为showPhotoPage。

编译运行。

image.png

你可以看见,竖直方向上布局是正确的,你尝试选择屏幕。横屏,没有足够的空间来显示所有内容,不过scrollVIew允许你适当的滚动去看lable和textfield。不幸的是,新视图控制器中的image是硬编码的,在集合中选择的这张image他不显示。

fix,在segue执行完成你需要传递imageName到控制器。

在iOS\Source\Cocoa Touch Class 创建一个新的文件,命名为PhotoCommentViewController, 他是UIViewController的子类。确保创建的文件是swift的。点击下一步,保存他到项目中。

用下面的代码替换掉PhotoCommentViewController.swift中的内容:

import UIKit

class PhotoCommentViewController: UIViewController {

  @IBOutlet weak var imageView: UIImageView!

  @IBOutlet weak var scrollView: UIScrollView!

  @IBOutlet weak var nameTextField: UITextField!

  var photoName: String?

  override func viewDidLoad() {

    super.viewDidLoad()

    if let photoName = photoName {

      self.imageView.image = UIImage(named: photoName)

    }

  }

}

用phoneName创建image给imageView设置image.

回到storyboard ,打开控制器的 Identity Inspector ,选择Class栏目设置为PhotoCommentViewController,然后连线scrollView,imageView和 Textfield。

打开CollectionViewController.swift, 用下面的代码替换prepare(segue:sender:) :

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

  if let cell = sender as? UICollectionViewCell,

      let indexPath = collectionView?.indexPath(for: cell),

      let photoCommentViewController = segue.destination as? PhotoCommentViewController {

    photoCommentViewController.photoName = "photo\(indexPath.row + 1)"

  }

}

这是当点击一个相片去设置相片名显示的PhotoCommentViewController。

编译运行:

image.png

当需要滚动去查看更多时,你的视图排列内容的很好。现在你将意识到键盘的2个问题:第一,当输入文字时,键盘挡住了你的textfield.第二,你没办法取消键盘。马上解决这个问题。

管理键盘

不像UITableViewController,他能通过滚动内容的方式去自动处理键盘,当你直接使用UIScrollView时,需要你手动去管理键盘。

你可以通过PhotoCommentViewController监听键盘发送显示和隐藏的通知。打开PhotoCommentViewController.swift

, 在viewDidLoad()中添加下面的代码(暂时忽略编译报错):

NotificationCenter.default.addObserver(

  self,

  selector: #selector(PhotoCommentViewController.keyboardWillShow(_:)),

  name: Notification.Name.UIKeyboardWillShow,

  object: nil

)

NotificationCenter.default.addObserver(

  self,

  selector: #selector(PhotoCommentViewController.keyboardWillHide(_:)),

  name: Notification.Name.UIKeyboardWillHide,

  object: nil

)

下一步,在对象销毁时,添加下面的方法去移除通知的监听者。

deinit {

  NotificationCenter.default.removeObserver(self)

}

然后,然后将上面所说的方法添加到视图控制器中。

func adjustInsetForKeyboardShow(_ show: Bool, notification: Notification) {

  let userInfo = notification.userInfo ?? [:]

  let keyboardFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue

  let adjustmentHeight = (keyboardFrame.height + 20) * (show ? 1 : -1)

  scrollView.contentInset.bottom += adjustmentHeight

  scrollView.scrollIndicatorInsets.bottom += adjustmentHeight

}

@objc func keyboardWillShow(_ notification: Notification) {

  adjustInsetForKeyboardShow(true, notification: notification)

}

@objc func keyboardWillHide(_ notification: Notification) {

  adjustInsetForKeyboardShow(false, notification: notification)

}

在adjustInsetForKeyboardShow(_:,notification:)中,获取键盘高度,并拿键盘高度加上一个20个点的高度作为scrollView的contentInset.这样,上下滑动UIScrollView,UITextField页会一直在屏幕上可见。

当通知被取消时,要么keyboardWillShow(:)方法被调用 ,要么keyboardWillHide(:) 方法被调用。这些方法都会调用 adjustInsetForKeyboardShow(_:,notification:), 指示scrollView移动的方向。

隐藏键盘

在 PhotoCommentViewController.swift:中添加方法:

@IBAction func hideKeyboard(_ sender: AnyObject) {

  nameTextField.endEditing(true)

}

这个方法将会取消掉textfield的第一响应者状态。这样会隐藏键盘。

最后,打开Main.storyboard,在Object Library 中拖一个点击手势到Photo Comment View Controller 的view上,然后在Photo Comment View Controller中写一个IBAction类型的方法hideKeyboard(_:) 。

为了更友好的交互 ,当点击键盘的return按钮时页应该隐藏键盘。右击nameTextField,然后连线 Primary Action Triggered到hideKeyboard(_:)方法。

编译运行:

image.gif

到 Photo Comment View Controller 页,点击text field,然后点击控制器的view的任何地方,键盘应正确显示和隐藏自己相对于屏幕上的其他内容。点击return按钮时页应该一样。

利用UIPageViewCpontroller实现分页

这是UIScrollView教程的第三部分,你讲创建一个允许分页的scrollView。这意味着当你拖拽结束后他会锁定到某一页。这个动作你可以在app store中看一个app的截图时看见。

打开 Main.storyboard,从Object Library拖拽一个Page View Controller ,打开 Identity Inspector,进入 PageViewController.

在Attributes Inspector中,默认Transition Style 被设置为Page Curl ,我们改为Scroll,并设置 Page Spacing为8。

image.png

在Photo Comment View Controller 的 Identity Inspector

中,为PhotoCommentViewController设置一个Storyboard ID ,这样你就可以从代码中引用它了。打开PhotoCommentViewController.swift,添加属性:

var photoIndex: Int!

这将引用要显示的照片的索引,并将被page view controller使用。

在iOS\Source\Cocoa Touch Class创建一个文件,命名ManagePageViewController,是UIPageViewController的子类。

打开 ManagePageViewController.swift

,用下面的代码替换里面的内容:

import UIKit

class ManagePageViewController: UIPageViewController {

  var photos = ["photo1", "photo2", "photo3", "photo4", "photo5"]

  var currentIndex: Int!

  override func viewDidLoad() {

    super.viewDidLoad()

    // 1

    if let viewController = viewPhotoCommentController(currentIndex ?? 0) {

      let viewControllers = [viewController]

      // 2

      setViewControllers(

        viewControllers,

        direction: .forward,

        animated: false,

        completion: nil

      )

    }   

  }

  func viewPhotoCommentController(_ index: Int) -> PhotoCommentViewController? {

    guard let storyboard = storyboard,

      let page = storyboard.instantiateViewController(withIdentifier: "PhotoCommentViewController")

        as? PhotoCommentViewController else {

          return nil

    }

    page.photoName = photos[index]

    page.photoIndex = index

    return page

  }

}

下面是这代码做了什么:

1.viewPhotoCommentController(:) 是通过storyboard创建一个 PhotoCommentViewController类型的实例。你通过一个imageName做为参数,显示的视图与您在前一屏中选择的图像匹配。

2.通过一个包含一个控制器的数组作为参数创建UIPageViewController。

下面,你需要实现UIPageViewControllerDataSource。添加下面的类扩展代码:

extension ManagePageViewController: UIPageViewControllerDataSource {

  func pageViewController(_ pageViewController: UIPageViewController,

                          viewControllerBefore viewController: UIViewController) -> UIViewController? {

    if let viewController = viewController as? PhotoCommentViewController,

      let index = viewController.photoIndex,

      index > 0 {

      return viewPhotoCommentController(index - 1)

    }

    return nil

  }

  func pageViewController(_ pageViewController: UIPageViewController,

                          viewControllerAfter viewController: UIViewController) -> UIViewController? {

    if let viewController = viewController as? PhotoCommentViewController,

      let index = viewController.photoIndex,

      (index + 1) < photos.count {

      return viewPhotoCommentController(index + 1)

    }

    return nil

  }

}

当page在改变时 UIPageViewControllerDataSource 允许你提供内容。在向前和向后的方向上提供分页控制器实例。在这两种情况下,photoindex是用来确定哪些图像当前显示。viewController作为参数,表示当前基于 photoIndex 显示的控制器,一个新的控制器被创建并返回。

你依然需要设置dataSource .在 viewDidLoad()

添加下面代码:

dataSource = self

要使page view 运行只剩下几件事情要做才能。首先,您将修复应用程序的流程。

回到 Main.storyboard ,选择Page View Controller ,在Identity Inspector, 设置class为ManagePageViewController。

移除之前创建的showPhotoPage 。然后从 Scroll View Controller的Photo Cell 上拖拽到 Manage Page View Controller,并设置segue。在Attributes Inspector 设置segue。和之前一样指定名称为showPhotoPage。

打开 CollectionViewController.swift 改变实现 prepare(segue:sender:) 方法:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

  if let cell = sender as? UICollectionViewCell,

    let indexPath = collectionView?.indexPath(for: cell),

    let managePageViewController = segue.destination as? ManagePageViewController {

    managePageViewController.photos = photos

    managePageViewController.currentIndex = indexPath.row

  }

}

编译运行:

image.gif

现在,你可以在不同的详情页面之间滑动。

显示一个page control indicator

在UIScrollView教程的最后一个部分,你将为你的应用添加一个UIPageControl。

很幸运,UIPageViewController 有能力自己提供一个UIPageControl。

这样,你的UIPageViewController 必须有一个transition style 是 UIPageViewControllerTransitionStyleScroll,并且你必须实现2个指定的UIPageViewControllerDataSource数据源方法。你之前设置 Transition Style 做的很好。因此你现在只需要在的ManagePageViewController extension实现2个UIPageViewControllerDataSource数据源方法:

func presentationCount(for pageViewController: UIPageViewController) -> Int {

  return photos.count

}

func presentationIndex(for pageViewController: UIPageViewController) -> Int {

  return currentIndex ?? 0

}

在 presentationCount(for:)中,指定在 page view controller总共显示多少页。

在 presentationIndex(for:)中,当前显示了第几页。

然后实现要求实现的代理方法,你可以添加更多的定制与uiappearance 相关 API。在AppDelegate.swift,用下面的代码替换application(application: didFinishLaunchingWithOptions:):

func application(_ application: UIApplication,

                   didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

  let pageControl = UIPageControl.appearance()

  pageControl.pageIndicatorTintColor = UIColor.lightGray

  pageControl.currentPageIndicatorTintColor = UIColor.red

  return true

}

这样可以自定义 UIPageControl.的颜色。

编译运行:

image.png

把所有的放一起

在最后一步,是去添加当图像被点击进到的缩放视图。

打开PhotoCommentViewController.swift,添加下面的代码:

@IBAction func openZoomingController(_ sender: AnyObject) {

  self.performSegue(withIdentifier: "zooming", sender: nil)

}

override func prepare(for segue: UIStoryboardSegue,

                           sender: Any?) { 

  if let id = segue.identifier,

    let zoomedPhotoViewController = segue.destination as? ZoomedPhotoViewController,

    id == "zooming" {

    zoomedPhotoViewController.photoName = photoName

  }

}

在 Main.storyboard, 在 Photo Comment View Controller和 Zoomed Photo View Controller.之间添加一个Show Detail .选中这个新的segue,打开 Identity Inspector 并设置Identifier 为 zooming.

在 Photo Comment View Controller 中选中imageView。打开Attributes Inspector 选中 User Interaction Enabled。 从imageView 上拖一个手势 Tap Gesture Recognizer ,响应方法为openZoomingController(_:).

现在,你在 Photo Comment View Controller 中点击一个图片,你将进入 Zoomed Photo View Controller ,在 Zoomed Photo View Controller 中你可以缩放图片。

最后一次,编译运行:

image.gif

Yes!你做到了!你制作了一个相册app,你可以通过切换查看一个集合的图片。也具备缩放内容的能力。

Where to Go From Here?

Here is the final PhotoScroll project with all of the code from this UIScrollViewtutorial.

You’ve delved into many of the interesting things that a scroll view is capable of. If you want to go further, there is an entire video series dedicated to scroll views. Take a look.

Now go make some awesome apps, safe in the knowledge that you’ve got mad scroll view skillz!

If you run into any problems along the way or want to leave feedback about what you've read here, join the discussion in the comments below.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,392评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,258评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,417评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,992评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,930评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,199评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,652评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,327评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,463评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,382评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,432评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,118评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,704评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,787评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,999评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,476评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,057评论 2 341

推荐阅读更多精彩内容

  • 通过一个关于相册浏览的简单应用--图片缩放,垂直滑动图片,翻页效果,大家将学到UIScrollView相关的知识。...
    Magenta_she阅读 1,238评论 2 9
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,982评论 4 60
  • This TT has recalled my question for myself in the early ...
    Rutona阅读 237评论 0 0
  • 现在的你是否还能想起曾经那个青涩的自己,是否还会记得曾经暗恋的那个人,是否还把曾经的梦想放在心底。 现在的我们早已...
    茶果果麻麻阅读 316评论 0 0
  • 这段时间又陷入迷茫期,不知道自己喜欢什么,干什么都提不起兴趣,最主要的是对未来的恐惧,到底未来要做什么? 不知道。...
    大大的小玉米阅读 112评论 0 0