上一话中我们实现了切换密码输入的功能,这一话我们来完成登录的功能。
我们创建一个Swift的类用来存储用户信息:
import Foundation struct User { let name:String let company:String let login:String let password:String static func login(login:String,password:String) -> User?{ if let user = database[login] { if user.password == password{ return user } } return nil } static let database:Dictionary<String,User> = { var theDatabase = Dictionary<String,User>() for user in [ User(name: "苹果公司的CG", company: "苹果公司", login: "apple", password: "foo"),User(name: "斯坦福大学的CG", company: "斯坦福大学", login: "standford", password: "foo"),User(name: "西电的CG", company: "西安电子科技大学", login: "xidian", password: "foo") ]{ theDatabase[user.login] = user } return theDatabase }() }
这里有一个用户的结构体,一个登录的方法和一个小的数据库。接下来我们要在视图中应用它,更新后的控制器代码:
import UIKit class ViewController: UIViewController { @IBOutlet weak var loginField: UITextField! @IBOutlet weak var passwordField: UITextField! @IBOutlet weak var passwordLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() updateUI() } var loggedInUser:User? { didSet{ updateUI()} } @IBAction func login(sender: UIButton) { loggedInUser = User.login(loginField.text ?? "", password: passwordField.text ?? "") } @IBAction func toggleSecurity(sender: UIButton) { secure = !secure } var secure = false { didSet{ updateUI()}} private func updateUI(){ passwordField.secureTextEntry = secure passwordLabel.text = secure ? "Secure Password":"Password" } }
现在我们有了登录方法。
现在我们想在视图的底部每当登录成功后就显示一张图片和登录的人名以及它所在的公司。
在storyboard左下角放置一张图片,然后连接左侧和下放的约束,你会看到有一个警告,因为现在我们的imageView中还没有放入图片,所以不知道尺寸。放置一张图片进去:
设置图片的时候你会发现图片下面有1X、2X、3X,分别对应了不同的屏幕分辨率,我们甚至可以在不同的分辨率下显示不同的图片:
图片右侧也有属性检查器,你可以很轻松地管理自己的图片:
我多拖一些不同尺寸的图片进来,后面会有用,回到我们的storyboard中,给我们的imageView设置一张图片并且update frame,你会发现图片太大了:
那么该怎么做呢?我们首先连接图片和右侧按钮,选择垂直距离约束:
然后双击修改为标准值:
update一下看看:
看起来很好,但是你会发现有很多警告,这是为什么呢?
我们到大纲视图看一下,提示我们设置hugging属性,那么这个hugging是干什么的呢?当我们的视图被拉伸或者压缩的时候,这个属性用来指示哪一部分会优先被拉伸或者压缩。
很明显我们的文本框和按钮都不能被压缩,但是图片可以被压缩。选中图片然后去尺寸检查器中,就是这两部分的内容:
现在我把图片的数值压缩系数修改为700
现在所有的警告都不见了!因为现在系统知道压缩的时候该压缩哪个了。数值越大表示越不想被压缩。现在我们继续添加两个label用来显示姓名和公司
现在给它们添加约束,先将Name至于中线部分,Name相对左边的图片:
Company相对Name:
我们希望它们左侧对齐,现在Name的中线与视图的中线对齐,但是我希望Name的底部和中线对齐,那么该怎么做呢?
点击Name左侧的约束,在右侧查看它的属性检查器:
这里有两个Item,我修改下面的属性,也就是Name标签,修改为Bottom:
现在可以看到Name标签已经变成底部与中线对齐了:
尺寸检查器下面的部分也有作用:
constant可以增加偏移量,Multiplier不常用,可以修改比例,我把Constant改为50:
或者把Multiplier改为4:1
跑到顶部去了。现在我们在代码中增加这三项:
@IBOutlet weak var imageView: UIImageView! @IBOutlet weak var companyLabel: UILabel! @IBOutlet weak var nameLabel: UILabel!
然后更新我们的updateUI方法:
private func updateUI(){ passwordField.secureTextEntry = secure passwordLabel.text = secure ? "Secure Password":"Password" nameLabel.text = loggedInUser?.name companyLabel.text = loggedInUser?.company imageView.image = loggedInUser?.image }
你会发现imageView这一行会报错,因为我们的User中根本没有imageView,它导入的是Foundation库,不包含UIKit,所以不应该有页面元素,那么该怎么办呢?别忘了我们还有extension,注意扩展要在类的定义外面写:
extension User { var image: UIImage? { if let image = UIImage(named: login) { return image } else { return UIImage(named:"cg") } } }
你会发现报错消失了,现在我们的User已经有image属性了。
赶紧来运行一下看看:
看起来不错,我们输入apple 和foo:
这实在有点糟糕,我们的第一个文本框发生了拉伸,而我希望它是hugging的,原因是我们把这些view相互依存在一起,一种做法是把图片的hugging属性垂直方向调小:
运行看看:
这次它没有被压扁,但是图片被拉伸了。
这是因为我们之前设置了图片到按钮的距离等于标准值,但是我们不希望它是一个固定值,所以我选中这个约束,然后修改它的属性,把equal改为大于等于这个值:
现在试试,图片的问题完美解决,换一个standford账号登录一下,问题出现了,在竖屏的时候我们的文字跑到屏幕外面去了:
我们需要给右侧的标签增加约束,只需要添加到右边界的约束,然后修改为标准值即可。
再次测试:
还是有问题,因为右侧的标签文字丢失了一部分。这是因为被压缩的标签优先级太低了,我们修改它在水平方向的压缩系数:
运行:
虽然所有的内容都显示完整了,但是我们的图片不太美观。图片的宽高比是不固定的,如果我们修改UserName使它变得很长的话
上面有很多空白的空间没有被利用,而图片却被压缩了,我们希望保留图片的宽高比,而且在文本过于长的时候图片在上方显示。
现在来解决这两个问题:
首先解决宽高比,要知道宽高比在storyboard中是个常量,而每张图片的宽高比是不同的,除非我们在代码中首先计算每张图片的宽高比,继续回到代码中。
首先UIImage是没有宽高比这个属性的,所以我们需要扩展它:
extension UIImage { var aspectRatio:CGFloat { return size.height != 0 ? size.width / size.height : 0 } }
然后我们写一个属性来表示新的约束:
var aspectRatioConstraint:NSLayoutConstraint? { willSet{ if let existingConstraint = aspectRatioConstraint { view.removeConstraint(existingConstraint) } } didSet{ if let newConstraint = aspectRatioConstraint { view.addConstraint(newConstraint) } } }//所有约束的类型
写一个计算属性来更新约束
var image:UIImage?{ get { return imageView.image } set{ imageView.image = newValue if let constrainedView = imageView,let newImage = newValue{ aspectRatioConstraint = NSLayoutConstraint(item: constrainedView, attribute: .Width, relatedBy: .Equal, toItem: constrainedView, attribute: .Height, multiplier: newImage.aspectRatio, constant: 0) } else { aspectRatioConstraint = nil } } }
最后修改updateUI方法,使用计算属性替代以前的属性:
private func updateUI(){ passwordField.secureTextEntry = secure passwordLabel.text = secure ? "Secure Password":"Password" nameLabel.text = loggedInUser?.name companyLabel.text = loggedInUser?.company image = loggedInUser?.image //有改动 }
现在来运行看看:
看起来不错,只是对于小图标来说放在下面显得中间的空间太大了,如何利用中间的空间呢?我们知道任何的iphone在竖屏的时候都是水平方向紧凑,竖直方向普通的。点击storyboard下方的sizeclass区域,我们会看到当前编辑的是宽为Any高为Any的sizeclass:
我选择单独编辑在宽为Any,高为Regular情况下地约束,下面会出现一个蓝色的条,提示你当前处在特定屏幕尺寸的编辑中:
在当前尺寸编辑的时候,需要取消掉我们之前设置的约定,我们取消图片和Name、Company两个标签的约束,然后调整图片位置到上面,然后重新设定约束。再次运行:
完美运行!