iOS 中的那些小问题

作者: 牵线小丑 | 来源:发表于2016-03-18 10:45 被阅读1781次
    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. 将目标字体拖入目标项目;


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

      F002.png
    4. 将字体添加到 Copy Bundle Resources

      F003.png
    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<CGImage> 转化为 UIImage
    let unCGimage:Unmanaged<CGImage> = 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<SMsgAVIoctrlSetPlayVolumeReq>(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<Int8>(cmd), dataSize: sizeof(SMsgAVIoctrlSetPlayVolumeReq))
            free(cmd)
    
    1. 指针偏移
    let buf:UnsafePointer<UInt8> = 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 进行条件判断,那就是 ?? 。这个操作符可以判断输入并在当左侧的值是非 nilOptional 值时返回其 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("<p>Some Text</p>", 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 PathsFramework 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在范围内
    

    持续更新

    相关文章

      网友评论

        本文标题:iOS 中的那些小问题

        本文链接:https://www.haomeiwen.com/subject/hfoclttx.html