2016-09-08 11:10:37 philar2016 阅读数 2409

怎样用Swift的UIKit制作一个简单的画板APP???                                                  ---翻译自https://www.raywenderlich.com/87899/make-simple-drawing-app-uikit-swift


在我们成长的某些阶段,很多人都喜欢画画,卡通等。对于我来说,我是伴随着笔和纸长大的一代人,但是计算机和触屏设备的快速发展已经快要取代笔和纸的用途!画画可以在触屏设备上,就像你看到Apple Store里有很多画画的App.

想要学习怎样制作一个画板app?? 好消息就是相对比较简单,感谢在ios里有很多的可用的画板API。

在整个学习过程中,你需要学习到:

1.使用Quartz2D来勾画线条。

2.使用多种颜色。

3.设置画笔的宽度和透明度。

4.创建一个橡皮。

5.设置RGB的颜色属性。

6.保存用户在画板中画的画。

7.共享你的画的朋友圈或微博。

开始制作

首先,下载这个项目的基本框架starter project

打开xcode, 打开你的项目浏览一下里面的文件,你可以看到我基本上没有为你做太多的工作。我只是添加了所有你需要的图片,然后创建了一个app的主视图和该有的约束。这个项目是基于single View Application的模版。

现在打开Main.storyboard然后看一下界面。这个视图控制器在顶部有3个按钮。就像文字所示,这三个按钮分别用来“重置”,“设置画板”和“保存”。 在界面的底部,你可以看到有很多的按钮(有“笔”的背景图片),和一个橡皮。它们都被用来选择颜色。

另外,还有两个视图叫“mainImageView"和“tempImageView"--你之后就会知道为什么你需要这两个视图,这些可以让用户在画画的时候设置不同的画笔透明级别。


视图控制器有actions和outlets两种设置,每一个顶部的按钮都有一个action, 笔的颜色都会连接到相同的action, 另外有两个Image View的outlets.

接下来,我们需要添加一些代码到你的项目里面。

你的App需要以一个简单的画画功能作为开始,用你的手指在屏幕上滑动,来画出一条简单的黑色的线条。

打开ViewController.swift,然后添加下面的属性到类之中:

var lastPoint = CGPoint.zeroPoint
var red: CGFloat = 0.0
var green: CGFloat = 0.0
var blue: CGFloat = 0.0
var brushWidth: CGFloat = 10.0
var opacity: CGFloat = 1.0
var swiped = false

接下来,我来解释一下上面所声明的变量:

1.last point存储的是在画板上最后的一个绘制点,这是使用在一个连续的触笔画在画板上。

2.red,green,blue存储的是当前RGB的值,用来选择颜色。

3.brushWidth和opacity存储的是画笔的宽度和透明度。

4.swiped定义了是否触笔是连续的。

5.RGB的默认值是0,也就是说画画的颜色以黑色为默认值。默认的透明度设置为1.0, 线条的宽度设置为10.0.

现在,终于到了画画的阶段了! 所有的touch-notifying方法都是来自于父类UIResponder, 它会响应触碰开始,移动,触碰结束的事件。你会用到这3种方法来实现你的画画逻辑。

现在开始添加以下的方法:

override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
  swiped = false
  if let touch = touches.first as? UITouch {
    lastPoint = touch.locationInView(self.view)
  }
}
touchesBegan是当用户把手指按在屏幕上的时候调用的方法,这也就是画画事件开始的第一件事,所以你首先要初始化swiped为false在触碰还没有移动的时候。你还需要保存触碰的移动轨迹在最后一个点位的时候,所以,当用户开始用手指画画的时候,你可以追踪用户手指的轨迹(何时开始)。

接下来,添加以下两个方法到你的代码里:

func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) {
 
  // 1
  UIGraphicsBeginImageContext(view.frame.size)
  let context = UIGraphicsGetCurrentContext()
  tempImageView.image?.drawInRect(CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height))
 
  // 2
  CGContextMoveToPoint(context, fromPoint.x, fromPoint.y)
  CGContextAddLineToPoint(context, toPoint.x, toPoint.y)
 
  // 3
  CGContextSetLineCap(context, kCGLineCapRound)
  CGContextSetLineWidth(context, brushWidth)
  CGContextSetRGBStrokeColor(context, red, green, blue, 1.0)
  CGContextSetBlendMode(context, kCGBlendModeNormal)
 
  // 4
  CGContextStrokePath(context)
 
  // 5
  tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
  tempImageView.alpha = opacity
  UIGraphicsEndImageContext()
 
}
 
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
  // 6
  swiped = true
  if let touch = touches.first as? UITouch {
    let currentPoint = touch.locationInView(view)
    drawLineFrom(lastPoint, toPoint: currentPoint)
 
    // 7
    lastPoint = currentPoint
  }
}
我在来解释一下:

1. 在第一个方法中,负责在两点之间画一条线。记住这个app会有两个image View--mainImageView(目前为止所画的)和tempImageView(线条你现在所画的)。现在你希望画进tempImageView, 所以你需要定义一个常量,来存储画画的内容。

2. 接下来,你拿到了用户当前触碰的点位,然后画一条线用CGContextAddLineToPoint从lastPoint到currentPoint. 这个将会生产出一些直线,所谓两点一线。

3. 这些就是所有画板的参数,笔刷的尺寸,透明度,颜色。

4. 这就是神奇的部分,这就是你画的路径。

5. 接下来,你需要完成绘画内容,然后渲染整条线到temporary image view.

6. 在touchesMoved里,你设置了swiped为true, 所以你可以追踪到当前的触屏滑动。接着你可以调用hepler方法也就是你刚刚写的drawLine.

7.最后,因为你更新了lastPoint, 所以接下来会继续你的触碰事件。


下一步,添加最后的触屏处理程序touchesEnded.

override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
 
  if !swiped {
    // draw a single point
    drawLineFrom(lastPoint, toPoint: lastPoint)
  }
 
  // Merge tempImageView into mainImageView
  UIGraphicsBeginImageContext(mainImageView.frame.size)
  mainImageView.image?.drawInRect(CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: kCGBlendModeNormal, alpha: 1.0)
  tempImageView.image?.drawInRect(CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: kCGBlendModeNormal, alpha: opacity)
  mainImageView.image = UIGraphicsGetImageFromCurrentImageContext()
  UIGraphicsEndImageContext()
 
  tempImageView.image = nil
}
首先,你检测是否用户在屏幕滑动当中,如果不在,那么就意味着用户点击了屏幕画了一个点。

如果用户是在手指滑动之中,那就是说你可以跃过画一个点--在touchesMoved方法调用之前,当调用touchesEnded方法的时候你就不需要画更多的东西。

最后一步就是合并tempImageView和mainImageView. 你画的触笔tempImageView而不是mainImageView. 

当你在tempImageView里画图的时候,透明度设置为1.0(完全不透明),但当你合并tempImageView和mainImageView时,你可以设置tempImageView的透明度来配置值,可以给你的笔刷定义你想要的透明度。如果你直接画在mainImageView上,那么以不同透明度的画笔画画将是一件非常困难的事。

好啦,现在准备画图吧!运行你的app,你会看见你现在可以画一些黑色的线条在你的画板上了!!!


这是一个非常好的开始哦! 用这些触摸的处理方法你可以有很多的功能。现在到时候填写更多的选项,那我们先从颜色开始。

设置颜色

是时候添加颜色到你的场景上了!

目前总共有10款颜色按钮在界面上,但是如果你想点击其他颜色的笔,现在是没效果的。所以首先,你需要定义这些颜色。添加下面数组属性到类里。

let colors: [(CGFloat, CGFloat, CGFloat)] = [
  (0, 0, 0),
  (105.0 / 255.0, 105.0 / 255.0, 105.0 / 255.0),
  (1.0, 0, 0),
  (0, 0, 1.0),
  (51.0 / 255.0, 204.0 / 255.0, 1.0),
  (102.0 / 255.0, 204.0 / 255.0, 0),
  (102.0 / 255.0, 1.0, 0),
  (160.0 / 255.0, 82.0 / 255.0, 45.0 / 255.0),
  (1.0, 102.0 / 255.0, 0),
  (1.0, 1.0, 0),
  (1.0, 1.0, 1.0),
]
这些建立了一个关于RGB值的数组,每一个数组的元素都是3个CGFloats的元组。这里的每一个颜色都匹配每一个界面按钮的标签。

接下来,找到pencilPressed这个方法,然后添加下面的代码:

// 1
var index = sender.tag ?? 0
if index < 0 || index >= colors.count {
  index = 0
}
 
// 2
(red, green, blue) = colors[index]
 
// 3
if index == colors.count - 1 {
  opacity = 1.0
}
这是一个很短的方法,但是让我们来一起一步一步的看一下。

1. 首先,你需要知道哪一个颜色的索引是用户选择的。这里会有一些地方会导致错误-错误的标签,标签没有设置,数组里没有足够的颜色--所以你需要检查这些地方。默认值是黑色,也就是第一个颜色。

2. 接下来,你设置red, green, blue的属性。

3. 最后一个按钮是橡皮,所以有一点特殊。橡皮按钮设置了颜色是白色,透明度为1.0。就像你看到的背景是白色一样,这会给你一个非常方便的橡皮效果。

现在准备好画彩色的画了么?? 运行你的app,你可以绘画出彩色的线条啦!现在你可以点击颜色按钮,改变笔刷的颜色。


白板

所有伟大的艺术家都有退后一步然后摇着头低声说“不,不,这永远不会做!”这种情况。那么你要提供一种情况来清理你的画板,并让用户可以一遍又一遍的开始画图。你已经有一个reset(重置)按钮设置在您的应用程序里了。

找到reset()这个方法,并添加这句代码:

mainImageView.image = nil
就是这样,信不信由你。上面的代码就是设置mainImageView的图为空。瞧!你的画板已经清理啦。记住,你画的线是在imageView的内容里面,所以把这清除为0,将会重置所有的事情。构建和运行你的app, 随便画一些东西,然后点击重置按钮来清除你的画。就是这样,你可没必要因为撕毁你的画而沮丧。


完成触摸--设置

好啦!你现在拥有了一个功能性的画板app,但是我们还有第二个设置界面要做。

首先,打开SettingsViewController.swift然后添加下面两行属性到类里:

var brush: CGFloat = 10.0
var opacity: CGFloat = 1.0
这个会让你一直追踪用户选择的笔刷的尺寸和透明度。

现在添加以下代码到sliderChanged():

if sender == sliderBrush {
  brush = CGFloat(sender.value)
  labelBrush.text = NSString(format: "%.2f", brush.native) as String
} else {
  opacity = CGFloat(sender.value)
  labelOpacity.text = NSString(format: "%.2f", opacity.native) as String
}
 
drawPreview()
这上面的代码,随着滑动条控制的变化,滑块值将适当的匹配。那么你就需要更新那些在drawPreview的预览图像,那么你将添加下一段到drawPreview里:

func drawPreview() {
  UIGraphicsBeginImageContext(imageViewBrush.frame.size)
  var context = UIGraphicsGetCurrentContext()
 
  CGContextSetLineCap(context, kCGLineCapRound)
  CGContextSetLineWidth(context, brush)
 
  CGContextSetRGBStrokeColor(context, 0.0, 0.0, 0.0, 1.0)
  CGContextMoveToPoint(context, 45.0, 45.0)
  CGContextAddLineToPoint(context, 45.0, 45.0)
  CGContextStrokePath(context)
  imageViewBrush.image = UIGraphicsGetImageFromCurrentImageContext()
  UIGraphicsEndImageContext()
 
  UIGraphicsBeginImageContext(imageViewBrush.frame.size)
  context = UIGraphicsGetCurrentContext()
 
  CGContextSetLineCap(context, kCGLineCapRound)
  CGContextSetLineWidth(context, 20)
  CGContextMoveToPoint(context, 45.0, 45.0)
  CGContextAddLineToPoint(context, 45.0, 45.0)
 
  CGContextSetRGBStrokeColor(context, 0.0, 0.0, 0.0, opacity)
  CGContextStrokePath(context)
  imageViewOpacity.image = UIGraphicsGetImageFromCurrentImageContext()
 
  UIGraphicsEndImageContext()
}
这个方法用了同样的技术来画一个预览设置,就像视图控制器用触摸的处理方法。在这两种情况下,该方法绘制一个点,而不是一条线(有着适当宽度和不透明度的滑块的值)。

运行你的代码,打开设置界面,调动滑动框。你会看见预览图和值会跟着你的滑动框移动而变化。


集成设置

这里有一个重要的地方丢失了,你有没有注意到??

更新过的透明度和宽度值还没有被应用于ViewController绘图板!这是因为你还没有把设置界面特定的值放到ViewController里。这就需要用到代理和协议了!

打开SettingViewController.swift文件,然后添加以下代码:

protocol SettingsViewControllerDelegate: class {
  func settingsViewControllerFinished(settingsViewController: SettingsViewController)
}
这段代码的意思就是定义一个协议,里面有一个叫settingsViewControllerFinished的方法。

然后添加一个属性到SettingsViewController的类里:

weak var delegate: SettingsViewControllerDelegate?

我们将引用这个代理。如果有一个代理,那么你需要通知它当用户点击了关闭按钮。找到close()然后添加以下代码到这个方法的最后:

self.delegate?.settingsViewControllerFinished(self)
这个就是调用代理的方法,然后可以自己更新新的值。

现在,打开ViewController.swift文件,然后添加一个扩展的类在文件底部给协议。

extension ViewController: SettingsViewControllerDelegate {
  func settingsViewControllerFinished(settingsViewController: SettingsViewController) {
    self.brushWidth = settingsViewController.brush
    self.opacity = settingsViewController.opacity
  }
}
这就表明了类符合SettingViewControllerDelegate和实现它的一个方法。在实现中,需要做的就是设置视图的滑动控件来调节当前画笔的宽度和透明度的值。当用户从画图转向设置界面,这就意味着你需要让用户打开你的设置界面。

添加下面的方法复写这个类:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
  let settingsViewController = segue.destinationViewController as! SettingsViewController
  settingsViewController.delegate = self
  settingsViewController.brush = brushWidth
  settingsViewController.opacity = opacity
}
当用户点击设置按钮触发segue,这个方法将配置新的SettingsViewController, 通过设置代理。

运行你的app,在这个阶段,你会看到笔刷和透明度值现在更新后可以在设置界面改变。现在,你可以画许多颜色的画,也可以有不同大小的笔和透明级别啦!!



收尾工作--自定义颜色选择器

现在,你有10个颜色按钮在你的画板界面中,但是在颜色选择器里,有一系列的RGB颜色调动栏在你的设置界面中。

既然你已经提供了一个预览画笔大小和透明度,你不妨再提供一个预览新画笔的颜色!。 预览效果会显示在预览图像里,就像透明度和画笔预览会显示在RGB颜色里。不需要额外的图片,重用你已经有的!

打开SettingsViewController.swift 然后添加下面的属性:

var red: CGFloat = 0.0
var green: CGFloat = 0.0
var blue: CGFloat = 0.0
你要用到这些来保存你当前的RGB值。

现在添加以下代码到colorChanged:

red = CGFloat(sliderRed.value / 255.0)
labelRed.text = NSString(format: "%d", Int(sliderRed.value)) as String
green = CGFloat(sliderGreen.value / 255.0)
labelGreen.text = NSString(format: "%d", Int(sliderGreen.value)) as String
blue = CGFloat(sliderBlue.value / 255.0)
labelBlue.text = NSString(format: "%d", Int(sliderBlue.value)) as String
 
drawPreview()
这是将要被你调用的方法,当你的RGB滑块移动的时候。在上面的代码中,请注意你所做的事情就是更新属性值,更新标签。

如果你现在运行你的项目,你会发现颜色变化将不会显示在预览里。为了显示,你需要在drawPreview()里做一点小小的改变。搜索行调用CGContextSetRGBStrokeColor,替换所有的0.0值和红色,绿色 蓝色变量。

在第一个方法中,替换调用CGContextSetRGBStrokeColor,用下面的代码:

CGContextSetRGBStrokeColor(context, red, green, blue, 1.0)
接下来,在下半部分,替换调用CGContextSetRGBStrokeColor,用下面的代码:

CGContextSetRGBStrokeColor(context, red, green, blue, opacity)
现在,你已经有了所有的正确的设置,你想要正确显示这个设置界面,添加以下代码到viewWillAppear:

override func viewWillAppear(animated: Bool) {
  super.viewWillAppear(animated)
 
  sliderBrush.value = Float(brush)
  labelBrush.text = NSString(format: "%.1f", brush.native) as String
  sliderOpacity.value = Float(opacity)
  labelOpacity.text = NSString(format: "%.1f", opacity.native) as String
  sliderRed.value = Float(red * 255.0)
  labelRed.text = NSString(format: "%d", Int(sliderRed.value)) as String
  sliderGreen.value = Float(green * 255.0)
  labelGreen.text = NSString(format: "%d", Int(sliderGreen.value)) as String
  sliderBlue.value = Float(blue * 255.0)
  labelBlue.text = NSString(format: "%d", Int(sliderBlue.value)) as String
 
  drawPreview()
}
这样你就可以看到,这个方法预设了正确的值给所有的标签和滑动栏。 drawPreview调用预览视图会正确的显示出来。

最后,打开ViewController.swift. 就像之前一样,你需要确定当前的颜色到设置界面,所以添加下面3行代码到prepareForSegue的最后:

settingsViewController.red = red
settingsViewController.green = green
settingsViewController.blue = blue
这样RGB滑动框就被正确设置了。

最后,找到settingsViewControllerFinished在扩展类里,添加下面3行代码到方法里:

self.red = settingsViewController.red
self.green = settingsViewController.green
self.blue = settingsViewController.blue
大功告成! 是时候运行你的app啦。 滑动你的颜色选择器,选中RGB颜色,显示在RGBPreview里,现在默认的笔的颜色就在画板上啦。

你得到了一款属于你自己的画板。。现在可以尽情的画图享受了。。

2015-04-18 20:12:32 Dreamer_undo 阅读数 390

swift入门,找了个简单app教程

教程地址:http://testerhome.com/topics/1450


IDE:Xcode6.2 


其中遇到了以下几个个问题:

问题一:'URLWithString' is unavailable: use object construction 'NSURL(string:)‘

根据提示直接是用NSURL(string:)来创建url,传值给NSURLRequest的参数加 !拆包


问题二:show the object library 显示 No matches

准备添加往view 加入空间是出现的,在stackoverflow.com找到的答案http://stackoverflow.com/questions/26354956/no-items-in-object-library-xcode-6

第一次创建swift工程,点击Main.storyborder的时候,可能我好几次都是双击打开的,都是弹出浮动窗口。

单击storyborder在主面板中展示就没这问题了。


问题三:simulator窗口过大

使用command+1,command+2,command+3,进行100%,75%,50%大小的切换

2014-09-28 10:22:59 chaoyuan899 阅读数 11544

以下所有代码都是使用Xcode Version 6.0.1 (6A317)编写的。

由于团队开发的时候使用stroyboard在合并的时候有诸多不便,所有还是使用.xib文件编写这个ToDo App.

想要实现的功能是:TableView 上可以增加待做选项,并按照时间先后排序,可以实现删除,到点通知功能。

想要实现的效果如下:

      

步骤:

1、新建一个基于Singal View Application 的工程,然后删掉storyboard,在新建两个新文件 Main.xib 和 Main.swift 作为主要的ViewController,打开 Main.xib 将 File's Owner的l类属性改为 Main(这样才可以将关联变量拖动到 Mian.swift )。

Main.xib 页面UI,一个用于展示todo list 的 tableView,然后关联一个 tableView 变量到 Main.swift文件


2、接下来设置 Mian 为rootViewController,在AppDelegate.swift中做写如下代码:

 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
       
        var viewController = Main(nibName: "Main", bundle: nil)
        navigationController = UINavigationController(rootViewController: viewController)
        
        self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
        self.window?.rootViewController = navigationController
        self.window?.makeKeyAndVisible()
        
        return true
    }

注意: var viewController = Main(nibName:"Main", bundle: nil) ,用来将 Mian.xib 与 Mian.swift 进行绑定。run 一下你就可以看到界面了。

3、然后在Main.swift 中编写一下TableView 的数据源和代理的方法。这里我们用的是 自定义的 Cell。所有新建一个 Cell.xib 和 Cell.swift 并将它们关联起来,做法和上面的相同,Cell.xib UI 如下。


func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 20
    }
    
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? Cell
        var str: String
        if (cell == nil) {
            let nibs:NSArray = NSBundle.mainBundle().loadNibNamed("Cell", owner: self, options: nil)
            cell = nibs.lastObject as? Cell
        }
        
        cell?.todoTitle.text = "toDoTitle"
        cell?.time.text = "\(NSDate())"
        cell?.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
        return cell!
    }
    
    
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

    }
    
    
    func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        if editingStyle == UITableViewCellEditingStyle.Delete {
        
        }
    }

run 一下就可以看到如下效果:


注意:考虑到UITableView的滚动性能,Cell 的重用非常重要,通过上面的 println(cell),滚动Cell,观察打印出来的 Cell 地址,可以看到 Cell 并没有进行重用。在 

override func viewDidLoad() { } 中添加下面的代码使 Cell 重用。

var bundle: NSBundle = NSBundle.mainBundle()
        var nib: UINib = UINib(nibName: "Cell", bundle: bundle)
        tableView.registerNib(nib, forCellReuseIdentifier: cellIdentifier)

4、以上讲到的都是些静态的数据,接下来我们做一些动态数据。

  4.1、在NavigationBar 增加一个 ‘+’ 按钮,用来给用户增加待做选项

self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Add, target: self, action: "addItem")
响应函数:

func addItem() {
        let addVC: Add = Add(nibName: "Add", bundle: nil)
        addVC.delegate = self;
        self.presentViewController(addVC, animated: true, completion: nil)
    }
 

 4.2、新增一个 Add.xib 和 Add.swift 让用户输入待做选项,记得绑定(同步骤1),Add.xib UI如下:


为了在Main.swift 中接收到 Add.xib 中用户输入的信息,我们在 Add.swift 定义一个协议,然后Main.swift 遵循这个协议,在Add.xib 界面消失前获取用户输入信息。

protocol AddProtocal {
    func didCompleted(addObject: Add)
}
Add.swift 代码如下:

//
//  Add.swift
//  ToDoApp
//
//  Created by aaron on 14-9-17.
//  Copyright (c) 2014年 The Technology Studio. All rights reserved.
//

import UIKit

protocol AddProtocal {
    func didCompleted(addObject: Add)
}


class Add: UIViewController {
    
    @IBOutlet var todo: UITextField!
    @IBOutlet var desc: KCTextView!
    @IBOutlet var time: UIDatePicker!
    @IBOutlet var completeBtn: UIButton!
    var delegate: AddProtocal?
    
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }
    
    
    override func viewWillAppear(animated: Bool) {
        setup()
    }
    
    func setup() {
        completeBtn.layer.cornerRadius = 5.0
        todo.placeholder = "请输入待做项"
//        desc.placeholder = "请输入详细描述。"
        todo.text = self.todo.text
        desc.text = self.desc.text
        time.date = self.time.date
        time.minimumDate = NSDate.date()
        
        if delegate? == nil {
            todo.textColor = UIColor.lightGrayColor()
            todo.userInteractionEnabled = false
            desc.textColor = UIColor.lightGrayColor()
            desc.userInteractionEnabled = false
            time.userInteractionEnabled = false
            completeBtn.setTitle("好", forState: UIControlState.Normal)
        }else {
            todo.textColor = UIColor.blackColor()
            todo.userInteractionEnabled = true
            desc.textColor = UIColor.blackColor()
            desc.userInteractionEnabled = true
            time.userInteractionEnabled = true
            completeBtn.setTitle("完成", forState: UIControlState.Normal)
        }
        
        let swipeGesture = UISwipeGestureRecognizer(target: self, action:"hideKeyboard")
        swipeGesture.direction = UISwipeGestureRecognizerDirection.Down
        swipeGesture.numberOfTouchesRequired = 1
        self.view.addGestureRecognizer(swipeGesture)
        
       
    }
    
    func hideKeyboard() {
        println("swipeGesture....")
        todo.resignFirstResponder()
        desc.resignFirstResponder()
    }
    
    func shakeAnimation(sender: AnyObject) {
        let animation = CAKeyframeAnimation()
        animation.keyPath = "position.x"
        animation.values = [0, 10, -10, 10, 0]
        animation.keyTimes = [0, 1/6.0, 3/6.0, 5/6.0, 1]
        animation.duration = 0.4
        animation.additive = true
        sender.layer.addAnimation(animation, forKey: "shake")
    }
    
    
    
    @IBAction func completeTouch(sender: AnyObject) {
        if (countElements(todo.text) > 0){
            delegate?.didCompleted(self)
            self.dismissViewControllerAnimated(true, completion: nil)
        }else{
            shakeAnimation(todo)
        }
    }
    @IBAction func editingDidEnd(sender: UITextField) {
        if (countElements(sender.text) == 0) {
           shakeAnimation(todo)
        }
        
    }
    
}

ToDo项为空时会有一个小小的提示动画:


Add.swift 中的关联变量 desc 是UITextView 类型的,UITextView 不像 UITextField 有 placeHolder ,所以这里我们引入一个 OC 写的 KCTextView ,由 KCTextView 代替 UITextView,swift 中引用 OC 写的 API 容易,新建一个 .h ,把你需要用到的头文件统统写在里面,然后 Build Settings 中的 Object-C Bridging Header 写入 .h 文件的路径即可,接着就可以正常使用 OC 写的接口了。



Main.swift 实现 AddProtocal,并实现协议规定的函数:

func didCompleted(addObject: Add) {
  
        toDoData.append(addObject)
        tableView.reloadData()
}
toDoData的是一个 Add类型的可变数组。

Main.swift 代码如下:

//
//  Main.swift
//  ToDoApp
//
//  Created by aaron on 14-9-16.
//  Copyright (c) 2014年 The Technology Studio. All rights reserved.
//

import UIKit

class Main: UIViewController, UITableViewDataSource, UITableViewDelegate, AddProtocal {

    @IBOutlet var tableView: UITableView!
    let cellIdentifier = "Cell"

    var toDoData = [Add]()
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setup()
        registerCell()
    }
    
    func setup() {
        self.title = "To Do List"
        self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Add, target: self, action: "addItem")

    }
    
    func registerCell() {
        var bundle: NSBundle = NSBundle.mainBundle()
        var nib: UINib = UINib(nibName: "Cell", bundle: bundle)
        tableView.registerNib(nib, forCellReuseIdentifier: cellIdentifier)
    }
    
    
    func addItem() {
        let addVC: Add = Add(nibName: "Add", bundle: nil)
        addVC.delegate = self;
        self.presentViewController(addVC, animated: true, completion: nil)
    }
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return toDoData.count
    }
    
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? Cell
        var str: String
        if (cell == nil) {
            let nibs:NSArray = NSBundle.mainBundle().loadNibNamed("Cell", owner: self, options: nil)
            cell = nibs.lastObject as? Cell
        }
        
        let addObject = toDoData[indexPath.row] as Add
        cell?.todoTitle.text = addObject.todo.text
        cell?.time.text = dateFormatter(addObject.time.date)
        cell?.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
        return cell!
    }
    
    
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        let addVC = toDoData[indexPath.row] as Add
        addVC.delegate = nil
        self.presentViewController(addVC, animated: true, completion: nil)
    }
    
    
    func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        if editingStyle == UITableViewCellEditingStyle.Delete {
            toDoData.removeAtIndex(indexPath.row)
            tableView.reloadData()
        }
    }
    
 
    func didCompleted(addObject: Add) {
  
        toDoData.append(addObject)
        toDoData.sort({ self.dateFormatter($0.time.date) < self.dateFormatter($1.time.date)})//按时间排序
        tableView.reloadData()
        

    }
    
    
    func dateFormatter(date: NSDate) -> String {
        let formatter = NSDateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        formatter.locale = NSLocale(localeIdentifier: NSGregorianCalendar)
        let dateStr = formatter.stringFromDate(date)
        return dateStr
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

最后你大概可以看到这样的效果:


5、最后一步,为待做项目添加通知功能,这一功能在之前的文章(ios8 notifacation in swift)中就讲过了,这里就不重复写了。完整的项目代码我发在github上来,需要的到这里拿。







2017-05-12 17:51:00 Frankltf 阅读数 25

用swift开发一个完整的度假地app,设计到布局、数据绑定、数据编辑、页面导航等;适合初学者;
github:(git@github.com:Frankltf/ios-swift-app.git)

2016-11-13 18:53:11 Solar_Terry 阅读数 541

用swift写一个悬浮的searchbar

这次用代码布局实现下面这个悬浮着的搜索框。
img1
先交代一下背景:这是一个tabviewcontroller里的一个tabview,然后它还是一个navigationcontroller的rootcontroller。


首先,因为它是navigationcontroller里的,所以要隐藏掉自带的navigationbar,不然会影响美感

// MARK: - 隐藏navigationbar
self.navigationController?.isNavigationBarHidden = true

然后,新建一个用于承载searchbar的view,我称之为barView。整个搜索框的颜值就体现在这个view上,因为自带的searchbar对我来说太难修改它的外观了。

//设置搜索框的位置和尺寸
var barView = UIView()
barView = UIView(frame: CGRect(x:10, y:30, width:self.view.frame.width-20, height:40))
//设置搜索框的圆角、背景颜色、透明度等外貌属性
barView.layer.masksToBounds = true
barView.layer.cornerRadius = 10
barView.layer.backgroundColor = UIColor(red: 49/255, green: 151/255, blue: 230/255,alpha:0.9).cgColor

这时候,放搜索框的位置应该是长这样的:
img2
隐隐约约能看到背后的地图,开始有种悬浮的感觉了。

之后,就要把searchbar放到这个barView里了

var searchBar = UISearchBar()
searchBar = UISearchBar(frame: barView.bounds)
searchBar.placeholder = "请输入校园内地址"
searchBar.delegate = self
barView.addSubview(searchBar)

这时候就会看见长成这样的搜索框了:
img3
有两个问题,1:仔细看的话会发现barView上有两条杠;2:barView本身的蓝色变成了灰色。
其实问题产生的原因是searchbar本身的默认样式里是灰色的背景色,这个灰色背景色覆盖了barView的背景色,而且这个默认样式还使searchbar的输入文字部分的背景色成白色的。如果用storyboard布局的话就可以在storyboard里将default改成minimal,但是现在是代码布局。
img5

//问题一的解决方法(跟去除nc的bar底部的黑线很相似诶)
searchBar.setBackgroundImage(UIImage(), for: .any, barMetrics: .default)
//问题二的解决方法
searchBar.searchBarStyle = UISearchBarStyle.minimal

这时候,搜索框变成了这样:
img4
大概雏形已经出来了,可是placeholder里的字看不太清楚,所以需要改变一下字体的颜色,因为placeholder本身不能修改字体颜色,所以通过取出searchbar里的textfield来改变searchbar的placeholder的字体颜色:

let textFieldInsideSearchBar = searchBar.value(forKey: "searchField") as? UITextField

textFieldInsideSearchBar?.textColor = UIColor.white

let textFieldInsideSearchBarLabel = textFieldInsideSearchBar?.value(forKey: "placeholderLabel") as?UILabel

textFieldInsideSearchBarLabel?.textColor = UIColor.white

将以上代码整合起来,就可以实现图一的样式了!在实现这个样式的过程中,我遇到了:1、searchbar有黑线;2、改变不了searchbar的背景色;3、修改不了placeholder的样式,这几个困扰我比较久的问题,希望这篇文章能帮助到像我一样的swift3小白~

没有更多推荐了,返回首页