ios 知识点iOS开发杂货铺swift

iOS - 30个Swift项目

2017-11-15  本文已影响437人  licaLicaGao_

概述

自学 iOS 和 Swift 也有一段时间了,最早尝试写Demo时都是向着相对较完整的App方向进行的,至此这样“相对完整的App”也只完成了三个,但是到头来学到的最大干货只有 CoreData 和 JSON解析,大部分时间用在不断重复的布局设置等方面,甚至有时为了画一些图标都要对着PS扣一晚上。所以对自己的学习安排做了一些改变,半个月前开始尽量保证每天写一个简单的Demo,当然这也是受到一些大佬们的启发,在网上有不少类似于《自学 iOS - 三十天三十个 Swift 项目 》这样的文章。
这次练习计划的目的完全是为了掌握一些新的知识和技巧,省去了”为了让Demo看上去更完整“而浪费的时间。
所有的Demo我都已经开源——GitHub链接 ,我没有把这些练习放在一个工程里面所以在下面的每一个Demo的记录中都会单独附上链接。
因为是自学所以代码规范方面一直有一些问题,但是做练习的同时也需要不断参考其他大佬们的代码,所以整个学习计划其实也是见证自己的代码逐渐规范化的一个过程。Demo我会坚持写,写这篇文章的目的一方面是希望给有缘看到这篇文章的初学者们一些帮助,另一方面坚持更新也是对自己学习过程的监督。

Demo

1.自定义导航栏透明渐变

1-1.gif

使用的是自定义导航栏,设置系统导航栏为透明的方法网上有很多但是在iOS 11中透明的导航栏默认为白色还不知道怎么解决,所以先用自定义的代替了。透明渐变的功能在scrollViewDidScroll 这个方法中实现,根据滚动偏移量计算百分比数值并将其作为导航栏透明度。

2.MapKit简单使用

2-1.gif

MapKit的简单使用,import前需要进行下面的设置:


2-2.jpg

添加地点时我是使用了CoreData,如果只是单纯的练习MapKit是没必要这样做的所以也算是对之前的知识做了复习。左划删除则是iOS 11里的新方法,代替以前的editActionsForRowAtIndexPath 方法:

    override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        let actionDel = UIContextualAction(style: .destructive, title: "删除") { (action, view, finished) in
            let appDelegate = UIApplication.shared.delegate as! AppDelegate
            let context = appDelegate.persistentContainer.viewContext
            context.delete(self.fc.object(at: indexPath))
            appDelegate.saveContext()
            finished(true)
        }
        actionDel.backgroundColor = UIColor.red
        return UISwipeActionsConfiguration(actions: [actionDel])
    }

3.图片无限轮播

3.gif

使用scrollView实现图片无限轮播的功能,在scrollView中添加左、中、右三个imageView。使用下面的方法设置三个imageView的image:

    func setImg() {
        if index == 0 {
            leftImg?.image = UIImage(named: imgs.last!)
            midImg?.image = UIImage(named: imgs.first!)
            rightImg?.image = UIImage(named: imgs[1])
        } else if index == imgs.count - 1 {
            leftImg?.image = UIImage(named: imgs[index - 1])
            midImg?.image = UIImage(named: imgs.last!)
            rightImg?.image = UIImage(named: imgs.first!)
        } else {
            leftImg?.image = UIImage(named: imgs[index - 1])
            midImg?.image = UIImage(named: imgs[index])
            rightImg?.image = UIImage(named: imgs[index + 1])
        }
    }

自动滚动则是通过设置定时器实现。

4.两个tableView联动

4-1.gif

这次的练习是参考了一位大佬的文章【Swift联动】:两个TableView之间的联动,TableView与CollectionView之间的联动
在众多电商类App中都有用到联动tableView的形式。

5.新闻点击展开预览

5-1.gif

tableView自动行高的使用,设置行高为 UITableViewAutomaticDimension ,默认设置新闻内容label的行高为2,点击后变为0(即全部显示)
需要注意的是使用storyBoard进行约束时,必须将 tableViewCell 的 ContentView 从上到下填满,否则无法计算出当前行高。

6.AVKit简单使用

6-1.gif

系统自带播放器的简单使用,点击cell中的播放按钮调用下面的方法:

    @objc func playAction(_ sender: UIButton) {
        let path = Bundle.main.path(forResource: "清醒梦", ofType: "mp3")
        videoPlayer = AVPlayer(url: NSURL(fileURLWithPath: path!) as URL)
        playVideoController.player = videoPlayer
        self.present(playVideoController, animated: true) {
            self.playVideoController.player?.play()
        }
    }

这里因为我电脑里没有现成的视频文件所以偷懒用音频文件代替了...

7.自定义转场动画

7-1.gif

这个练习感觉是至今为止写过最实用的一个🤔,这次练习是分成两次做的。

    var secondViewController = SecondViewController()
    @objc func panAction(panGesture: UIPanGestureRecognizer) {
        let panPercent = panGesture.translation(in: self.view).x / self.view.frame.width
        switch panGesture.state {
        case .began:
            self.percentDrivenTransition = UIPercentDrivenInteractiveTransition()
            self.dismiss(animated: true, completion: nil)
        case .changed:
            self.percentDrivenTransition?.update(panPercent)
        case .ended, .cancelled:
            if panPercent > 0.5 {
                self.percentDrivenTransition?.finish()
            } else {
                self.percentDrivenTransition?.cancel()
            }
            self.percentDrivenTransition = nil
        default:
            break
        }
    }

8.tabBar简单使用

8-1.gif

tabBar的简单使用,主要是storyBoard上面的用法,前两个view都是之前的练习复制过来的,第三个view则是简单copy了一下微信的个人信息界面......
(大红色的导航栏可以说是很蠢了)

9.WKWebView简单使用

9-1.gif

使用WKWebView进行网页访问:

        let webView = WKWebView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
        self.view.addSubview(webView)
        if let exUrl = URL(string: "https://www.baidu.com") {
            let request = URLRequest(url: exUrl)
            webView.load(request)
        }

注意,苹果现在要求App内访问的网络必须使用HTTPS协议,但是很多网页是使用HTTP协议的,所以需要在 Info.plist 文件内做添加图中的内容:


9-2.png

同时复习了使用storyboard中的segue进行传值。
(tableView和网页访问的内容,可以无视...)

10.拖动手势&图片放大

10-1.gif

这个练习是在 自定义动画第二部分-交互手势 之前进行的,重点有两个:

有两个小知识点需要注意:

11.仿聊天界面

11-1.gif

这个练习耗费的时间比较多。

12.仿QQ个人资料页下拉背景图放大效果

12-1.gif

实现了类似于QQ个人资料页下拉背景图放大的效果,依然是在scrollViewDidScroll这个方法里做文章,当y方向偏移量<= 0 时,让图片的高度等于偏移量也就是将图片的高度固定不动,计算偏移量和屏幕宽度的比值,增加imageView的宽和高。同时为了整体效果我把第一个练习里的透明渐变导航栏也加进来了。

13.tableView中嵌入collectionView

13-1.gif

实现tableViewCell中嵌入collectionView,点击collectionView中的图片,将值传给tableView后单独刷新第一行的数据实现改变图片效果。同样是在电商类App中较常见。
单独刷新指定的
某个cell或某个section:

let index = IndexPath.init(row: 0, section: 0)
tableView.reloadRows(at: [index], with: .fade)

14.仿登录界面但实质是关键帧动画练习

14-1.gif 14-2.gif 14-3.gif

15.本地推送

15.gif

使用UserNotifications实现本地推送通知,代码均写在AppDelegate中。参考自大神故胤道长Swift30Projects

16.实现用户更改头像操作

16-1.gif

调用系统的相机和相册,模拟应用中用户更改头像的操作。使用 UIImagePickerControllerDelegate 和 UINavigationControllerDelegate 这两个代理,并调用完成图片选择的代理方法实现头像更改:

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        imgView.image = info[UIImagePickerControllerOriginalImage] as? UIImage
        dismiss(animated: true, completion: nil)
    }

17.tableView设置索引

17-1.gif

给tableView添加索引功能,使用其代理方法 sectionForSectionIndexTitle:

    func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
        var indexSection : Int = 0
        for i in sections {
            if i == title {
                return indexSection
            }
            indexSection += 1
        }
        return 0
    }

18.搜索栏使用

18-1.gif

搜索栏的使用,定义一个UISearchController,使用UISearchResultsUpdating并实现其代理方法:

    func updateSearchResults(for searchController: UISearchController) {
        if var text = searchController.searchBar.text {
            text = text.trimmingCharacters(in: .whitespaces)
            searchFilter(text: text)
            marvelTableView.reloadData()
        }
    }

使用数组的filter函数对原始数据进行筛选以实现搜索功能:

    func searchFilter(text: String) {
        self.searchResults = movies.filter({ (movie) -> Bool in
            return movie.name.localizedCaseInsensitiveContains(text)
        })
    }

19.仿微博界面 实现字数限制功能

19-1.gif
NotificationCenter.default.addObserver(self, selector: #selector(textViewNotificationAction(notification:)), name: NSNotification.Name.UITextViewTextDidChange, object: nil)

该通知调用方法 textViewNotificationAction :

    @objc func textViewNotificationAction(notification: Notification) {
        let limit: Int = 140
        let text = self.textView.text as NSString
        if text.length >= limit {
            let str = text.substring(to: limit)
            self.textView.text = str
            self.limitLabel.text = "\(limit)"
            self.limitLabel.textColor = UIColor.orange
        } else {
            self.limitLabel.textColor = UIColor.darkGray
            self.limitLabel.text = "\(text.length)"
        }
        self.weiboDetail = String(text)
    }

20.几个自己不常用的控件练习合集

20-1.gif

几个以前没用怎么用过的控件的练习合集,包括选择器UISegmentedControl、滑块UISlider、数字UIStepper、开关UISwitch。

21.tableView编辑、cell移动、左右侧滑

21-1.gif
    func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        let actionTop = UIContextualAction(style: .normal, title: "置顶") { (action, view, finished) in
            let first = IndexPath(row: 0, section: 0)
            tableView.moveRow(at: indexPath, to: first)
            finished(true)
        }
        actionTop.backgroundColor = UIColor.orange
        return UISwipeActionsConfiguration(actions: [actionTop])
    }
    func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        let actionDel = UIContextualAction(style: .destructive, title: "删除") { (action, view, finished) in
            self.ints.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .fade)
            finished(true)
        }
        
        actionDel.backgroundColor = UIColor.red
        return UISwipeActionsConfiguration(actions: [actionDel])
    }
    func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
        let fromRow = (sourceIndexPath as NSIndexPath).row
        let toRow = (destinationIndexPath as NSIndexPath).row
        let int = ints[fromRow]
        
        ints.remove(at: fromRow)
        ints.insert(int, at: toRow)
    }

22.使用截图完成动画

22-1.gif

使用截屏实现一些动画效果:

let snapshotView = selectedCell?.snapshotView(afterScreenUpdates: false)!

比如在demo中,CollectionView中的每一个Cell都有一个ImageView,动画开始前使用上面的代码给点击的cell截图,将其frame设置与原cell一致,并将原cell隐藏。接下来的位移及大小动画则均是以该截图为操作对象。
这种方法在自定义转场动画时更实用。

23.UIPageViewController的使用

23-1.gif
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        var index = (viewController as! WebViewController).index
        index -= 1
        return setViewController(index: index)
    }
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        var index = (viewController as! WebViewController).index
        index += 1
        return setViewController(index: index)
    }

在 setViewController 方法中为 WebViewController 设置相应的url和索引并返回

    func setViewController(index: Int) -> WebViewController? {
        if case 0..<urls.count = index {
            let main = UIStoryboard.init(name: "Main", bundle: Bundle.main)
            if let webVC = main.instantiateViewController(withIdentifier: "webView") as? WebViewController {
                webVC.url = urls[index]
                webVC.index = index
                
                return webVC
            }
        }
        return nil
    }

24.使用UIPickerView模拟老虎机

24-1.gif
    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
        let pickerLabel = UILabel()
        pickerLabel.font = UIFont(name: "Apple Color Emoji", size: 70)
        pickerLabel.textAlignment = .center
        switch component {
        case 0:
            pickerLabel.text = emojiArray[Int(arrayOne[row])]
        case 1:
            pickerLabel.text = emojiArray[Int(arrayTwo[row])]
        case 2:
            pickerLabel.text = emojiArray[Int(arrayThree[row])]
        default:
            break
        }
        return pickerLabel
    }

25.简单的画板功能

25-1.gif
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        
        let context = UIGraphicsGetCurrentContext()
        context?.setLineCap(.round)
        context?.setLineJoin(.round)
        
        if allLines.count > 0 {
            for i in 0..<allLines.count {
                let linePoints = allLines[i]
                if linePoints.count > 0 {
                    context?.beginPath()
                    context?.move(to: linePoints[0])
                    for j in 0..<linePoints.count {
                        context?.addLine(to: linePoints[j])
                    }
                    context?.setLineWidth(self.lineWidth)
                    context?.setStrokeColor(strokeColors[i])
                    context?.strokePath()
                }
            }
        }
        if currentPoints.count > 0 {
            context?.beginPath()
            context?.setLineWidth(self.lineWidth)
            context?.setStrokeColor(self.strokeColor.cgColor)
            context?.move(to: currentPoints[0])
            for i in 0..<currentPoints.count {
                context?.addLine(to: currentPoints[i])
            }
            context?.strokePath()
        }
    }

26.下拉刷新UIRefreshControl基本使用

26-1.gif
    var emojiArray = ["","","","","","","","🦄","",""]
    let refreshEmojiArray = ["","","","",""]

其中第一个数组是tableView默认显示的数据,第二个则是提供给刷新方法使用:

    @objc func refreshAction() {
        let arcCount = UInt32(self.refreshEmojiArray.count)
        let arc = Int(arc4random() % arcCount)
        self.emojiArray.insert(refreshEmojiArray[arc], at: 0)
        self.tableView.reloadData()
        self.refreshControl.endRefreshing()
    }

使用arc4random()方法生成随机数作为数组2的下标,并将对应的字符串添加到数组1的最开始位置(方便观察刷新效果)。

27.通过简单的单词翻译功能Demo练习JSON解析

27-1.gif
    func gradientColors() -> CAGradientLayer {
        
        let topColor = UIColor(named: "topColor")!
        let bottomColor = UIColor(named: "bottomColor")!
        let gradientColors = [topColor.cgColor, bottomColor.cgColor]
        let gradientLocations: [NSNumber] = [0.0, 1.0]
        let gradientLayer = CAGradientLayer()

        gradientLayer.colors = gradientColors
        gradientLayer.locations = gradientLocations
        return gradientLayer
    }

28.计算最大公约数和最小公倍数

28-1.gif

老实说,这不算是个练习。至于为啥写了这么个东西,是因为前两天考研的室友,专业课考C语言,他做的题里有计算这俩东西的题,让我拿Swift写一下。然后昨天晚上等球赛的时候,把在playground上写给他演示的代码写成了这个工程。。。

29.tableView分组折叠/展开效果

29-1.gif
    func tableViewHeaderClick(_ headerView: TableViewHeaderView, section: Int) {

        let show = itemData[section].isShow
        itemData[section].isShow = !show!
        
        let index = IndexSet(integer: section)
        self.tableView.reloadSections(index, with: .fade)
    }

30.Swift 自定义UICollectionViewLayout

30-1.gif

写在最后

由于偶尔偷懒,最后用了40天左右的时间写了这30个小练习,虽然难度都不大但是我通过这30个练习确实学到了不少东西。下一步打算开通开发者账号,试着以上架App Store为目的写一个完整的App。

上一篇 下一篇

猜你喜欢

热点阅读