iOS 中的那些小问题

  1. UITextView 中的文字默认并不是从最上面开始,在 UITextView 所在的 UIViewController中添加:
self.automaticallyAdjustsScrollViewInsets = false;
复制代码
  1. UITableView 自动隐藏键盘:
tableView.keyboardDismissMode = .onDrag
复制代码
  1. 设置 UITableView 中的 Cell 选中打钩的颜色:
tableView.tintColor = .red
复制代码
  1. UIButton 在边缘的时候没有高亮响应,重写 UIButton 的 pointInside函数:
/**
        解决按钮在边缘的时候被按下时没有显示高亮
     
     - parameter point: 按下的位置
     - parameter event: 目标事件
     
     - returns: 是否在内部
     */
    override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
        
        let inside = super.pointInside(point, withEvent: event)
        
        if inside != highlighted && event?.type == .Touches {
            highlighted = inside
        }
        
        return inside
    }
复制代码

PS:如果遇到需要这样的情况下,最好继承 UIButton 重写该函数,而不是extension UIButton,因为这样会使工程中用到的所有的 UIButton 都被重写。

  1. 静音模式下播放音频,最好在didFinishLaunchingWithOptions中进行调用
/**
        静音模式下仍可以播放音频
    */
    public class func playInQuietMode() {
        
        let audioSession: AVAudioSession = AVAudioSession.sharedInstance()
        
        do {
            try audioSession.setCategory(AVAudioSessionCategoryPlayback)
        } catch let error as NSError{
            print(error)
        }
        
        do {
            try audioSession.setActive(true)
        } catch let error as NSError{
            print(error)
        }
    }
复制代码
  1. 自定义 UITableViewCell 选中时的颜色
/**
        设置选中背景色
     
     - parameter color: 背景色
     */
    public func setSelectColor(color: UIColor) {
        
        let backgroundView = UIView();
        backgroundView.backgroundColor = color;
        self.selectedBackgroundView = backgroundView;
    }
复制代码

PS:self.selectionStyle的值不能为.None

  1. 判断 UIScrollView 是否滚动结束:
/// 记录手指放开滑动时 scrollView 的内容偏移
fileprivate var offset = CGPoint.zero

// 记录当前滑动状态
fileprivate var isScrollEnd = true

extension Controller: UIScrollViewDelegate {
    
    public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        // 开始滚动,记录状态
        isScrollEnd = false
    }
    
    public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        // 记录手指放开滑动时 scrollView 的内容偏移
        offset = scrollView.contentOffset
        // 使用 perform 延迟检测 scrollView 内容偏移,
        // 1. 如果手指放开滑动后 scrollView 继续滑动,由于 perform 运行在 NSDefaultRunLoopMode 模式,
        //    因此在 scrollView 继续滑动的情况下不会开始定时,因此不会在设置的延时后调用;
        // 2. 如果手指放开滑动后 scrollView 停止滑动,则会在设置的延时后调用 checkScrollEnd;
        perform(#selector(self.checkScrollEnd), with: nil, afterDelay: 0.020)
    }
    
    public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        // 当手指放开滑动后 scrollView 继续滑动,取消 checkScrollEnd 的调用;
        NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(self.checkScrollEnd), object: nil)
        // 结束滚动,记录状态
        isScrollEnd = true
    }
    
    internal func checkScrollEnd() {
        // 判断 scrollView 当前内容偏移与手指松开时的内容偏移是否相同,
        isScrollEnd = (offset == tableView.contentOffset)
    }
}
复制代码
  1. UITableView 添加 UITextField 时键盘处理
/// 添加监听
public override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    // 监听键盘的弹出
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
    // 监听键盘的隐藏
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
/// 移除监听
public override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    NotificationCenter.default.removeObserver(self)
}
/// 键盘弹出处理
public func keyboardDidShow(_ notification : Notification) {
    // 获取当前活跃的textField及其rect
    guard let textField = activeField, let rect = textField.superview?.convert(textField.frame, to: nil) else {
            return
    }
    // 获取键盘信息
    guard let keyboardInfo = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue else { return }
    // 获取键盘高度
    let keyboardHeight = keyboardInfo.cgRectValue.size.height;
    // 获取 textField 底部剩余空间
    let cellBottomHeight = UIScreen.main.bounds.height - rect.maxY
    // 获取 textField 需要向上平移多少才能满足键盘弹出所需的空间
    var offset = keyboardHeight - cellBottomHeight
    // 设置底部偏移,保证键盘弹出后仍能滑动到底部
    self.tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0)
    // 当 offset 小于 0 时,表示空间已满足
    guard offset > 0 else { return }
    // 加上 tableView 当前偏移
    offset += self.tableView.contentOffset.y
    self.tableView.setContentOffset(CGPoint(x: 0, y: offset), animated: true)
}
/// 键盘收回处理
public func keyboardWillHide(_ notification : Notification) {
    self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
}
复制代码
  1. 给 UIView 添加阴影
/**
        添加阴影
     
     - parameter color:  阴影颜色
     - parameter offset: 阴影偏移
     */
    public func addShadow(color: UIColor, offset: CGFloat) {
        
        self.layer.shadowColor = color.CGColor;
        self.layer.shadowOffset = CGSizeMake(-offset, offset);
        self.layer.shadowOpacity = 0.8;
        self.layer.shadowRadius = offset;
    }
复制代码
  1. 震动
/**
        震动,在真机上:设置/声音 中开启响铃/震动选项开启后才能震动
    */
    public class func shark() {
        AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate));
    }
复制代码
  1. 打开 iPod
/**
        打开 iPod
     */
    public class func openIpod() {
        
        UIApplication.sharedApplication().openURL(NSURL(string: "music://")!);
    }
复制代码
  1. App 国际化时,有时我们第一次安装APP时不想默认跟随系统,那么可以通过Xcode的scheme来指定特定语言

  2. 让 UITableView 在没有数据时不能滚动

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        self.tableView.scrollEnabled = (data.count > 0);
        
        return data.count;
}
复制代码
  1. 应用内打开其他 App 在Info.plist文件中设置URL types
    之后就可以使用下面的模式来发送一个URL:
myapp://
myapp://some/path/here
myapp://?foo=1&bar=2
myapp://some/path/here?foo=1&bar=2
复制代码

然后,App 的UIApplicationDelegate会收到一个消息。若你想自己处理该URL,可以重载下面这个方法:

func application(application: UIApplication, handleOpenURL url: NSURL) -> Bool {
}
复制代码
  1. 如果20秒内无法启动程序的话,iOS 检测计时器就会终止你的应用

  2. UIImage+imageNamed: 方法可避免延时加载,不像 +imageWithContentsOfFile:(和其他别的 UIImage 加载方法),这个方法会在加载图片之后立刻进行解压。

  3. App 播放视频时进入后台让视频继续播放,有两种方法可以实现:

第一种、 对目标视频对象的视频轨进行以下处理:

let playerItem = AVPlayerItem(URL: NSURL(string: "videoUrl")!);
let tracks = playerItem.tracks;
for track in tracks {
   if (track.assetTrack.hasMediaCharacteristic(AVMediaCharacteristicVisual)) {
       track.enabled = false;
   }
}
复制代码

第二种、 在进入后台前将目标视频渲染层设为 nil,在恢复到前台时重新设置:

func applicationDidEnterBackground(application: UIApplication) { 
   let playerView = "Get your player view"; 
   playerView.playerLayer.player = nil; 
}
func applicationDidBecomeActive(application: UIApplication) {
   let playerView = "Get your player view";
   playerView.playerLayer.player = player;
}
复制代码
  1. 获取视频的每秒帧数 fps 和 视频的长度 duration
let asset = AVAsset(URL: NSURL(fileURLWithPath: "videoPath"));
let fps = asset.tracksWithMediaType(AVMediaTypeVideo)[0].nominalFrameRate;
let duration = CMTimeGetSeconds(asset.duration);
复制代码
  1. App 进入后台仍然继续运行:
public func applicationDidEnterBackground(application: UIApplication) {
        
        application.beginReceivingRemoteControlEvents();

        var bgTask : UIBackgroundTaskIdentifier?
        bgTask = application.beginBackgroundTaskWithExpirationHandler {
            dispatch_async(dispatch_get_main_queue(), {
                if bgTask != UIBackgroundTaskInvalid
                {
                    bgTask = UIBackgroundTaskInvalid
                }
            })
        }
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
            dispatch_async(dispatch_get_main_queue(), {
                if bgTask != UIBackgroundTaskInvalid
                {
                    bgTask = UIBackgroundTaskInvalid
                }
            })
        }
    }
复制代码
  1. App 运行中不锁屏:
UIApplication.sharedApplication().idleTimerDisabled = true;
复制代码
  1. 禁止 oc 文件使用 ARC-fno-objc-arc

  2. Swift 获取对象的引用次数:CFGetRetainCount(obj)

  3. 禁止 UIButton 在按下时有动态阴影:

button.adjustsImageWhenHighlighted = false;
复制代码
  1. 隐藏导航栏的返回按钮:
self.navigationItem.hidesBackButton = true;
复制代码
  1. 隐藏导航栏下影线:
self.navigationController?.navigationBar.shadowImage = UIImage();
复制代码
  1. 设置导航栏中间为图片:
self.navigationItem.titleView = UIImageView(image: UIImage(named: "titleImage"));
复制代码
  1. 导航栏相关属性效果
/// rootView 的布局从(0,0)开始
edgesForExtendedLayout = .all
navigationController?.navigationBar.isTranslucent = false
/// rootView 的布局从(0,64)开始
edgesForExtendedLayout = .all
navigationController?.navigationBar.isTranslucent = true
复制代码
  1. 机型与屏幕尺寸的对应:
  • 4s_3.5inch
  • 5s_4inch
  • 6_4.7inch
  • 6+_5.5inch
  1. 模拟器截图时,必须设置分辨率为100%(Simulator > Window > Scale > 100%)

  2. UIView 中添加定时器时导致内存泄露处理:

override func willMoveToWindow(newWindow: UIWindow?) {
    super.willMoveToWindow(newWindow);
    if (newWindow == nil) {
        self.updateTimer.invalidate();
    }
}
复制代码
  1. UITextfiled 设置 placehold 字体颜色:
self.passwdTextIpt.attributedPlaceholder = NSAttributedString(string: NSLocalizedString("login_passwd", comment: ""), attributes: [NSForegroundColorAttributeName: UIColor(white: 1.0, alpha: 0.4)]);
复制代码
  1. 使用自定义字体

  2. 将目标字体拖入目标项目;

  3. Info.plist 文件中设置目标字体;

  4. 将字体添加到 Copy Bundle Resources

  5. 使用字体:UIFont(name: fontname, size: fontsize);

  6. 设置 UITextfiled 文字偏移:

self.userIdTextIpt.layer.sublayerTransform = CATransform3DMakeTranslation(10, 0, 0);
复制代码

或者重写 UITextfiled

override func textRectForBounds(bounds: CGRect) -> CGRect {
    return CGRectInset(bounds, 44, 0);
}
override func editingRectForBounds(bounds: CGRect) -> CGRect {
    return CGRectInset(bounds, 44, 0);
}
复制代码
  1. UIImage 显示与图片不同,设置其渲染模式即可:
UIImage(named: imageName)!.imageWithRenderingMode(.AlwaysOriginal);
复制代码
  1. 设置状态栏背景样式无效:

  2. Info.plist 文件中设置 View controller-based status bar appearanceNo

  3. Appdelegate.swift 文件中设置 UIApplication.sharedApplication().statusBarStyle = UIStatusBarStyle.Default;

  4. 隐藏导航栏返回按钮:

self.navigationItem.hidesBackButton = true;
复制代码
  1. 使用 Cookie:
let url = NSURL(string: url);
guard let cookies = NSHTTPCookieStorage.sharedHTTPCookieStorage().cookiesForURL(url!) else {
    return
}
let header = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies);
let request = NSMutableURLRequest(URL: url!);
request.addValue(header["Cookie"]!, forHTTPHeaderField: "Cookie");
复制代码
  1. 添加阴影
view.layer.shadowColor = UIColor.blackColor().CGColor;
view.layer.shadowOffset = CGSizeMake(0, 0);
view.layer.shadowOpacity = 0.25;
view.layer.shadowRadius = 6;
复制代码
  1. 检查 API 可用性
if #available(iOS 9.0, *) { 
      let store = CNContactStore()
} else { 
      // 旧版本的情况
}
复制代码
  1. swift 中将 Unmanaged 转化为 UIImage
let unCGimage:Unmanaged = defaultRepresentation.fullResolutionImage();
let image = unCGimage.takeUnretainedValue())
复制代码
  1. CADisplayLink 使用完成后内存无法被释放:
self.displaylink = CADisplayLink(target: self, selector: #selector(SCGifView.changeKeyFrame))
self.displaylink?.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
复制代码

以上在我一个工程中使用到的,在测试的时候,发现其内存无法被释放,检查看发现需要手动调用以下代码来完成释放:

self.displaylink?.removeFromRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes);
self.displaylink?.invalidate();
self.displaylink = nil;
复制代码
  1. 设置 App 沙盒内的 Document 文件夹可以被其他软件共享(各类手机助手): 在 Info.plist 文件中 添加 Application supports iTunes file sharing,并设置其值为 YES

  2. 播放系统拍照时的 咔嚓声(本质就是播放系统声音):

let soundID: SystemSoundID = 1108;    //! 1108 就是拍照的系统声音编号,要播放其他的声音可以到网上搜索对应的编号
AudioServicesPlayAlertSound(soundID);
复制代码
  1. AudioServicesPlaySystemSoundAudioServicesPlayAlertSound 的区别:

    1. AudioServicesPlaySystemSound 仅仅是播放系统声音,在静音模式下不播放;
    2. AudioServicesPlayAlertSound 当设置了 通用/声音/响铃模式振动,播放系统声音并振动,否则只播放系统声音;当设置了 通用/声音/静音模式振动,振动,否则不播放系统声音。
  2. UIButton 即有文字又有图像时,只让图像旋转:

let animation = CABasicAnimation(keyPath: "transform.rotation.z");
animation.fromValue = NSNumber(float: 0);
animation.toValue = NSNumber(double: 2 * M_PI);
animation.duration = 3;
animation.repeatCount = HUGE;
button.imageView?.layer.addAnimation(animation, forKey: "scRotationAnimation");
复制代码
  1. 调用 AVPlayerseekToTime 接口时间不对,将
self.player!.seekToTime(CMTimeMakeWithSeconds(time, self.player!.currentItem!.currentTime().timescale));
复制代码

更换为:

self.player!.seekToTime(CMTimeMakeWithSeconds(time, self.player!.currentItem!.currentTime().timescale), toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero);
复制代码
  1. 使用 NSDateFormatter.dateFromString 总是返回 nil,原因可能是你的时间格式是12小时制而你的值是24小时制,将 hh:mm:ss 改成 HH:mm:ss即可。

  2. swift 中结构体与结构体指针的使用,SMsgAVIoctrlSetPlayVolumeReq 为结构体, IOTYPE_USER_IPCAM_AUDIO_PLAY_VOLUME_SETTING_REQ 为枚举变量:

let cmd = UnsafeMutablePointer(malloc(sizeof(SMsgAVIoctrlSetPlayVolumeReq)))
cmd.memory.command_types_ = 1;
cmd.memory.value_ = 1;  
self.camera.sendIOCtrlToChannel(0, type: Int(IOTYPE_USER_IPCAM_AUDIO_PLAY_VOLUME_SETTING_REQ.rawValue), data: UnsafeMutablePointer(cmd), dataSize: sizeof(SMsgAVIoctrlSetPlayVolumeReq))
        free(cmd)
复制代码
  1. 指针偏移
let buf:UnsafePointer = CFDataGetBytePtr(rawData)
var r:UInt8 = 0
r = buf.advancedBy(i+0).memory
复制代码
  1. 字符串保留特定字符(以下例子保留字母):
let notAllowedCharactersSet = NSCharacterSet(charactersInString: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdedghijklmnopqrsquvwxyz").invertedSet
let filterStr = (str.componentsSeparatedByCharactersInSet(notAllowedCharactersSet) as NSArray).componentsJoinedByString("")
复制代码
  1. swift 的黑魔法
extension UIButton {
    
    class func oyc_swizzleSendAction() {
        
        struct oyc_swizzleToken {
            static var onceToken : dispatch_once_t = 0
        }
        
        dispatch_once(&oyc_swizzleToken.onceToken) {
            
            let cls: AnyClass! = UIButton.self
            
            let originalSelector = #selector(sendAction(_:to:forEvent:))
            let swizzledSelector = #selector(oyc_sendAction(_:to:forEvent:))
            
            let originalMethod =
                class_getInstanceMethod(cls, originalSelector)
            let swizzledMethod =
                class_getInstanceMethod(cls, swizzledSelector)
            
            method_exchangeImplementations(originalMethod, swizzledMethod)
        }
    }
    
    public func oyc_sendAction(action: Selector, to: AnyObject!, forEvent: UIEvent!) {
        
        struct oyc_buttonTapCounter {
            static var count: Int = 0
        }
        
        oyc_buttonTapCounter.count += 1
        print(oyc_buttonTapCounter.count)
        oyc_sendAction(action, to: to, forEvent: forEvent)
    }
    
    override public class func initialize() {
        if self != UIButton.self {
            return
        }
        UIButton.oyc_swizzleSendAction()
    }
}
复制代码
  1. ?? 操作符 在 Swift 中,有一个非常有用的操作符,可以用来快速地对 nil 进行条件判断,那就是 ?? 。这个操作符可以判断输入并在当左侧的值是非 nil 的 Optional 值时返回其 value,当左侧是 nil 时返回右侧的值,比如:
var level : Int?
var startLevel = 1
var currentLevel = level ?? startLevel
复制代码
  1. App 在启动界面停留特定时间:
NSThread.sleepForTimeInterval(3.0);      // 3.0 表示 3 秒
复制代码
  1. swift 中根据字符串创建对象:
        //动态获取命名空间:
        let nameSpace = NSBundle.mainBundle().infoDictionary!["CFBundleExecutable"] as! String
        //根据命名空间和传过来的控制器名字获取控制器的类
        let controllerClass:AnyClass = NSClassFromString(nameSpace + "." + controllerName)!
        
        //告诉编译器真实的控制器类型,比如这个控制器本质是UITableViewController则:
        let realClass = controllerClass as! UITableViewController.Type
        
        //实例化这个控制器出来
        let childController = realClass.init()
复制代码
  1. UIButton 中的 文本图片 默认是水平对齐

  2. 使用 单元测试 时显示 Module 'CTest' was not compiled for testing: 在 Build SettingsEnable Testability 设置为 YES

  3. Xcode 8 控制台输出信息太多解决方法:Edit Scheme -> Run -> Arguments, 在 Environment Variables 里边添加 OS_ACTIVITY_MODE,值设置为 disable

  4. 设置 WKWebView 的字体大小:

        //创建网页配置对象
        let config = WKWebViewConfiguration();
        // 创建设置对象
        let preference = WKPreferences();
        // 设置字体大小(最小的字体大小)
        preference.minimumFontSize = 60;
        // 设置偏好设置对象
        config.preferences = preference;
        // 创建WKWebView
        let webView = WKWebView(frame: self.view.bounds, configuration: config);
        webView.loadHTMLString("

Some Text

"
, baseURL: nil)         return webView; 复制代码
  1. 设置 WKWebView 根据屏幕宽度调整内容大小:
    /// 网络浏览视图
    fileprivate let webView: WKWebView = {
        let source = "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);"
        let script = WKUserScript(source: source, injectionTime: WKUserScriptInjectionTime.atDocumentEnd, forMainFrameOnly: true)
        let userContentController = WKUserContentController()
        userContentController.addUserScript(script)
        //创建网页配置对象
        let config = WKWebViewConfiguration()
        config.userContentController = userContentController
        // 创建设置对象
        let preference = WKPreferences()
        // 设置字体大小(最小的字体大小)
        preference.minimumFontSize = 18
        // 设置偏好设置对象
        config.preferences = preference
        return WKWebView(frame: .zero, configuration: config)
    }()
复制代码
  1. lazy 属性本质上是一个闭包,闭包中的表达式只会调用一次。需要强调的是,虽然这个闭包中捕获了self,但是这样做并不会导致循环引用,猜测是 swift 自动把 self 标记为 unowned 了。

  2. 给视图及之上的子控件添加阴影:

	let container = UIView();
    container.clipsToBounds = true;
    container.layer.cornerRadius = 5;
    container.layer.borderWidth = 1;
    container.layer.borderColor = UIColor.gray.cgColor;
    container.layer.shadowColor = UIColor.black.cgColor;
    container.layer.shadowOffset = CGSize(width: 4, height: 4);
    container.layer.shadowOpacity = 0.5;
    container.layer.shadowRadius = 3;
    return container;
复制代码
  1. 从项目中删除了某个目录、文件以后,编译出现警告信息: ld: warning: directory not found for option“XXXXXX” 解决办法:

  2. 选择工程,选中 TARGETS 中的目标工程

  3. 选择 Build Settings 菜单

  4. 查找 Library Search Paths 和 Framework Search Paths,删掉编译报 warning 的路径即可。

  5. 统一收起键盘:

UIApplication.shared.keyWindow?.endEditing(true);
复制代码
  1. 动态创建控制器
let vcs = [RunLoopViewController.self, CDrawViewController.self] as [UIViewController.Type];
let vc = self.vcs[indexPath.row].init()    // 创建控制器
let vcString = NSStringFromClass(self.vcs[indexPath.row])    // 
复制代码
  1. 设置 UITableView 中的 cell分隔线铺满整个 cell
let stView = UITableView();
stView.separatorInset = UIEdgeInsetsMake(0, 0, 0, 0);
复制代码
  1. 动态修改 tableFooterView 的高度
self.footview.frame = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: heightOfFooterView);
self.tableView.tableFooterView = self.footview;    // 需要重新给 tableFooterView 赋值
复制代码
  1. 获取 UILabel 设置 text 后的大小:
label.intrinsicContentSize
复制代码
  1. UIButton 添加子视图后无法响应事件的处理:
let btn = UIButton();
let subView = UIView(frame: btn.bounds);
subView.isUserInteractionEnabled = false;     // 将子视图的用户交互禁止,这样 UIButton 就能继续响应事件
btn.addSubview(subView);
复制代码
  1. 使用 UISearchController 导致的黑屏问题,在使用 UISearchControllerUIViewControllerviewDidLoad 添加:
self.definesPresentationContext = true
复制代码
  1. 自定义 UISearchBar 中的 UITextField 无效:必须确保 UISearchBarsearchBarStyleprominent ,当其值为 minimal 时,自定义 UITextField 无效。

  2. 手动取消 UISearchController

searchController.isActive = false
复制代码
  1. 子控件(比如按钮)超出父视图部分无法响应事件,在父视图添加以下代码(其思路是遍历父视图的所有子视图,并判断触发事件的点是否在子视图的bounds内如果在就返回这个子视图。):
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    var view = super.hitTest(point, with: event);
    guard view == nil else { return view }
    for subView in self.subviews {
        let p = subView.convert(point, from: self);
        if subView.bounds.contains(p) {
            view = subView;
        }
    }
    return view;
}
复制代码
  1. Xcode 无法在高于某系统的真机上运行:This iPhone 6 is running iOS 10.1 (14B55或者14B72), which may not be supported by this version of Xcode: 把升级包放在路径:应用程序(xcode)-右键-显示包内容  /Developer/Platforms/iPhoneOS.platform/DeviceSupport

  2. 在控制器 A 中,push 展示 控制器 B,这时要隐藏控制器 B 的返回按钮的文本,需要在控制器 A 中添加以下代码,而不是在控制器 B 中,因为 控制器 B 中的返回按钮实际是控制器 A 的:

self.navigationItem.backBarButtonItem = UIBarButtonItem(title:"", style:.plain, target:nil, action:nil)
复制代码
  1. 设置导航栏标题:

方式一:

self.title = "标题"          // 当控制器处于 UITabbarViewController 中时,对应的 Tabbar 项也会显示该标题
复制代码

方式二:

self.navigationItem.title = "标题"          // 这样设置的话,即使当控制器处于 UITabbarViewController 中时,对应的 Tabbar 项也不会显示该标题
复制代码
  1. 设置导航栏返回按钮图片:在创建导航栏的时候添加以下代码
// 绘制图片,主要是原始图片问题,可以跳过此步
UIGraphicsBeginImageContextWithOptions(image.size, false, 0)
image.draw(at: CGPoint(x: -10, y: 10))
var backImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
// 设置渲染模式,不设置的话,会使用系统默认颜色
backImage = backImage.withRenderingMode(.alwaysOriginal);
// 设置返回按钮图片
self.navigationController?.navigationBar.backIndicatorImage = backImage;
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = backImage;
复制代码
  1. UITableViewCell 被点击时,subviews 消失,这是因为当 UITableViewCell 被点击时会改变它内部的 subviews 的背景色,因此重写 UITableViewCell 以下两个方法即可:
override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)
    guard selected else { return }
    subview.backgroundColor = subviewColor
}
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
    super.setHighlighted(highlighted, animated: animated)
    guard highlighted else { return }
    subview.backgroundColor = subviewColor
}
复制代码
  1. swift 3 之后,函数有返回值没有被使用的时候会报警告,在该函数前加 @discardableResult 可以让编译器不报警告

  2. 多线程

  3. 串行

    • 同步,在当前线程中按顺序执行,同时阻塞当前线程
    • 异步,新开线程,但仅新开一个线程,所有执行的任务都在同一个新开的线程中顺序执行,不会阻塞当前线程
  4. 并发

    • 同步,在当前线程中顺序执行,同时阻塞当前线程。(这个运行结果和串行队列,同步执行是一模一样的。 因为同步任务的概念就是按顺序执行,后面都要等。言外之意就是不允许多开线程。 同步和异步则是决定开一条还是开多条。)
    • 异步,每一个 async 新开一个线程(也可能多个 async 共有一个线程,具体看系统分配)并发执行,不会阻塞当前线程
  5. 总结

    • 同步/异步决定开不开新线程;
    • 只有并发异步内的任务是并发执行的;
    • 其他的都是顺序执行;
    • 主队列是一个串行队列,全局队列是一个并行队列;
    • 异步串行队列添加的任务按添加的顺序执行,在一些情况可以用来当锁,比如多线程中对数组的操作;;
  6. 使用 for in 操作序列:

// 只对非 nil 值进行循环
for case let item? in seq { 
	// i 将是 Int 值,而不是 Int? 
	print(item)
}
或
for case let .some(item) in seq { 
	// i 将是 Int 值,而不是 Int? 
	print(item)
}
// 只对 nil 值进行循环 
for case nil in seq { 
	// 将对每个 nil 执行一次
	print("No value") 
}
let j=5 
if case0..<10=j{
	print("\(j) 在范围内") 
}//5在范围内
复制代码

持续更新

你可能感兴趣的:(swift,xcode,测试)