基于MVVM构建聊天App (二)登陆UI的实现

一、一个完整的开发流程

通常的,一个正常的流程包括:git

  • 产品定需求,给出原型图
  • 团队确认需求
  • 由设计师开始设计图,同步开发作开发前的准备工做,如技术调查,先后端如何配合等
  • 设计师完成设计后,团队对比设计图和原型图再次确认需求
  • 开发团队开始开发工做
  • 开发完成后由开发者自测 通常咱们在开发中编写的单元测试代码也属于自测
  • 开发者自测后由产品测试,事实上在开发过程当中,产品也应该实时的关注开发
  • 交由专业测试人员作测试
  • 发布内测版本,作大规模测试
  • 提交App Store审核

二、storyboard启动流程

通常的若是新建了一个Objective-C工程,默认先调用main函数,main函数内部会调用UIApplicationMain函数。github

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
复制代码

但在Swift项目中,并无main.m文件也没有main函数。Swift工程是在AppDelegate中用一个@UIApplicationMain标签。那么UIApplicationMain作了哪些操做呢:windows

  • 建立UIApplication单例对象
  • 建立UIApplication的委托对象,即AppDelegate
  • 开启事件循环监听系统事件,当坚挺到对应的系统事件时会通知AppDelegate
  • 建立最底层的UIWindows对象,
  • 读取Info.plist中设置的默认启动storyboard文件名称
  • 加载设置的is Initial View Controllerstoryboard文件,同时建立对应的View Controller对象
  • 设置建立的View ControllerUIWindows的根视图rootViewController
  • 显示Windows上的试图

运行工程能够看到以下图: 最底层是一个UIWindwos后端

windows

三、没有storyboard启动App

通常的一个工程,默认从main.storyboard中的设置的Initial view controller启动的,那么咱们该如何设置本身的初始启动View Controller呢?bash

在前面咱们工程的结构:RPChat_iOS是UI的显示以及交互相关的代码,因此在RPChat_iOS新建文件夹SignInapp

SignIn

新建登录界面Controller命名为SignInViewControlleride

AppDelegatedidFinishLaunchingWithOptions launchOptions回调方法中添加代码,设置SignInViewController为默认启动控制器:函数

if #available(iOS 13, *) {
    
} else {
     window = UIWindow.init()
     window?.frame = UIScreen.main.bounds
     window?.makeKeyAndVisible()
     let signInVC = SignInViewController()
     window?.rootViewController = signInVC
}
复制代码

SceneDelegateoptions connectionOptions方法中添加代码:单元测试

guard let windowScene = (scene as? UIWindowScene) else { return }

window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
window?.backgroundColor = .white
let tabBar = SignInViewController()
window?.rootViewController = tabBar
window?.makeKeyAndVisible()
复制代码

四、登陆UI的实现

先看一下这是最终效果图:测试

light Mode

  • 一、Auto Layout

至于UI,考虑到版本兼容和后期维护我采用了系统NSLayoutAnchor适配,

NSLayoutAnchor经常使用属性

  • leadingAnchor
  • trailingAnchor
  • leftAnchor
  • rightAnchor
  • topAnchor
  • bottomAnchor
  • widthAnchor
  • heightAnchor
  • centerXAnchor
  • centerYAnchor
  • firstBaselineAnchor
  • lastBaselineAnchor

关于Auto Layout其余更多使用细节请参考官方文档:

High Performance Auto Layout

Apple Develope NSLayoutConstraint

Apple Develope NSLayoutAnchor

WWDC 2018 What's New in Cocoa Touch

  • 二、UI实现

新建一个View命名SignInRootView做为登陆界面的主View,采用懒加载的方式初始化视图

最顶部的Logo图片实现代码:

lazy var logoImg: UIImageView = {
        self.addSubview($0)
        $0.translatesAutoresizingMaskIntoConstraints = false
        let top = $0.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor, constant: 44)
        let centerX = $0.centerXAnchor.constraint(equalTo: self.centerXAnchor, constant: 0)
        let width = $0.widthAnchor.constraint(equalToConstant: 120)
        let height = $0.heightAnchor.constraint(equalToConstant: 120)
        NSLayoutConstraint.activate([top, centerX, width, height])
        return $0
    }(UIImageView())
复制代码

用户名输入框实现代码:

lazy var accountNumberView: UIView = {
        self.addSubview($0)
        $0.translatesAutoresizingMaskIntoConstraints = false
        let top = $0.topAnchor.constraint(equalTo: logoImg.bottomAnchor, constant: 20)
        let left = $0.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 40)
        let right = $0.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -40)
        let height = $0.heightAnchor.constraint(equalToConstant: 50)
        NSLayoutConstraint.activate([top, left, right, height])
        $0.layer.cornerRadius = 25
        return $0
    }(UIView())
 
    
    lazy var accountNumberLab: UITextField = {
        accountNumberView.addSubview($0)
        $0.translatesAutoresizingMaskIntoConstraints = false
        $0.topAnchor.constraint(equalTo: accountNumberView.topAnchor, constant: 0).isActive = true
        $0.leftAnchor.constraint(equalTo: accountNumberView.leftAnchor, constant: 16).isActive = true
        $0.rightAnchor.constraint(equalTo: accountNumberView.rightAnchor, constant: -16).isActive = true
        $0.bottomAnchor.constraint(equalTo: accountNumberView.bottomAnchor, constant: 0).isActive = true
        $0.font = UIFont.init(name: "PingFangTC-Semibold", size: 19)
        return $0
    }(UITextField())
复制代码

密码输入框实现代码:

lazy var inputPasswordView: UIView = {
           self.addSubview($0)
           $0.translatesAutoresizingMaskIntoConstraints = false
           let top = $0.topAnchor.constraint(equalTo: accountNumberView.bottomAnchor, constant: 20)
           let left = $0.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 40)
           let right = $0.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -40)
           let height = $0.heightAnchor.constraint(equalToConstant: 50)
           NSLayoutConstraint.activate([top, left, right, height])
           $0.layer.cornerRadius = 25
           return $0
       }(UIView())
    
       
    lazy var inputPasswordTxt: UITextField = {
           inputPasswordView.addSubview($0)
           $0.translatesAutoresizingMaskIntoConstraints = false
           $0.topAnchor.constraint(equalTo: inputPasswordView.topAnchor, constant: 0).isActive = true
           $0.leftAnchor.constraint(equalTo: inputPasswordView.leftAnchor, constant: 16).isActive = true
           $0.rightAnchor.constraint(equalTo: inputPasswordView.rightAnchor, constant: -16).isActive = true
           $0.bottomAnchor.constraint(equalTo: inputPasswordView.bottomAnchor, constant: 0).isActive = true
           $0.font = UIFont.init(name: "PingFangTC-Semibold", size: 19)
           return $0
       }(UITextField())
复制代码

登陆按钮实现代码:

lazy var signInBtn: UIButton = {
        self.addSubview($0)
        $0.translatesAutoresizingMaskIntoConstraints = false
        let top = $0.topAnchor.constraint(equalTo: inputPasswordView.bottomAnchor, constant: 20)
        let left = $0.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 40)
        let right = $0.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -40)
        let height = $0.heightAnchor.constraint(equalToConstant: 50)
        NSLayoutConstraint.activate([top, left, right, height])
        $0.layer.cornerRadius = 25
        $0.titleLabel?.font = UIFont.init(name: "PingFangTC-Semibold", size: 20)
        $0.setTitle(NSLocalizedString("Sign In", comment: ""), for: .normal)
        return $0
   }(UIButton())
复制代码

此处代码较多,具体实现请看代码: gitub RPChat

  • 三、设置背景颜色

因为设计师给出的颜色通常为16进制,此处须要作一个转码处理:

open class func hexStringToColor(hexadecimal: String) -> UIColor {
        var cstr = hexadecimal.trimmingCharacters(in:  CharacterSet.whitespacesAndNewlines).uppercased() as NSString;
        if(cstr.length < 6){
            return UIColor.clear;
        }
        if(cstr.hasPrefix("0X")){
            cstr = cstr.substring(from: 2) as NSString
        }
        if(cstr.hasPrefix("#")){
            cstr = cstr.substring(from: 1) as NSString
        }
        if(cstr.length != 6){
            return UIColor.clear;
        }
        var range = NSRange.init()
        range.location = 0
        range.length = 2
        let rStr = cstr.substring(with: range);
        range.location = 2;
        let gStr = cstr.substring(with: range)
        range.location = 4;
        let bStr = cstr.substring(with: range)
        var r :UInt32 = 0x0;
        var g :UInt32 = 0x0;
        var b :UInt32 = 0x0;
        Scanner.init(string: rStr).scanHexInt32(&r);
        Scanner.init(string: gStr).scanHexInt32(&g);
        Scanner.init(string: bStr).scanHexInt32(&b);
        return UIColor.init(red: CGFloat(r)/255.0, green: CGFloat(g)/255.0, blue: CGFloat(b)/255.0, alpha: 1)
    }
复制代码

而后就能够用设计师给的16进制设置背景颜色:

signInBtn.backgroundColor = UIColor.hexStringToColor(hexadecimal: "0xF5BE62")
复制代码
  • 四、Drak Mode适配

因为iOS 13以后苹果处理Drak Mode,做为开发者也应该作相应的兼容处理。如今我在代码中并无此时调整模拟器为暗模式,运行工程能够看到在暗模式下,用户名和密码输入框背景色不见了。此时就应该作暗模式的兼容处理。

drak mode

此处个人作法也很简单,对UIColorextension处理,而后再扩展方法中分别对Drak ModeLight Mode两种模式作对应的处理,代码以下:

open class func configDarkModeViewColor() -> UIColor {
        let retColor: UIColor!
        if #available(iOS 13.0, *) {
            retColor = UIColor { (collection) -> UIColor in
                if (collection.userInterfaceStyle == .dark) {
                    return UIColor.init(red: 100/255, green: 100/255, blue: 100/255, alpha: 1)
                }
                return UIColor.white
            }
        } else {
            return UIColor.white
        }
        return retColor
    }
    
  open class func configDarkModeViewColorWithdDfaultColor(dfaultColor: UIColor) -> UIColor {
        let retColor: UIColor!
        if #available(iOS 13.0, *) {
            retColor = UIColor { (collection) -> UIColor in
                if (collection.userInterfaceStyle == .dark) {
                    return UIColor.init(red: 100/255, green: 100/255, blue: 100/255, alpha: 1)
                }
                return dfaultColor
            }
        } else {
            return dfaultColor
        }
        return retColor
    }
    
复制代码

只须要在设置颜色时调用便可:

view.backgroundColor = UIColor.configDarkModeViewColorWithdDfaultColor(dfaultColor: UIColor.groupTableViewBackground)
复制代码

再次运行项目,能够看到界面已经兼容了暗模式:

drak Mode

本文主要写了:

  • 不经过storyboard启动App
  • 登陆UI的实现
  • Drak Mode的适配

本文demo: Github RPChat

相关文章
相关标签/搜索