Since Xcode 8 and iOS10, views are not sized properly on viewDidLayoutSubviews

前端 未结 13 2180
北海茫月
北海茫月 2020-11-29 16:48

It seems that with Xcode 8, on viewDidLoad, all viewcontroller subviews have the same size of 1000x1000. Strange thing, but okay, viewDidLoad has n

13条回答
  •  萌比男神i
    2020-11-29 17:35

    I already reported this issue to apple, this issue exist since a long time, when you are initializing UIViewController from Xib, but i found quite nice workaround. In addition to that i found that issue in some cases when layoutIfNeeded on UICollectionView and UITableView when datasource is not set in initial moment, and needed also swizzle it.

    extension UIViewController {
        open override class func initialize() {
            if self !== UIViewController.self {
                return
            }
            DispatchQueue.once(token: "io.inspace.uiviewcontroller.swizzle") {
                ins_applyFixToViewFrameWhenLoadingFromNib()
            }
        }
    
        @objc func ins_setView(view: UIView!) {
            // View is loaded from xib file
            if nibBundle != nil && storyboard == nil && !view.frame.equalTo(UIScreen.main.bounds) {
                view.frame = UIScreen.main.bounds
                view.layoutIfNeeded()
            }
            ins_setView(view: view)
        }
    
        private class func ins_applyFixToViewFrameWhenLoadingFromNib() {
            UIViewController.swizzle(originalSelector: #selector(setter: UIViewController.view),
                                     with: #selector(UIViewController.ins_setView(view:)))
            UICollectionView.swizzle(originalSelector: #selector(UICollectionView.layoutSubviews),
                                     with: #selector(UICollectionView.ins_layoutSubviews))
            UITableView.swizzle(originalSelector: #selector(UITableView.layoutSubviews),
                                     with: #selector(UITableView.ins_layoutSubviews))
         }
    }
    
    extension UITableView {
        @objc fileprivate func ins_layoutSubviews() {
            if dataSource == nil {
                super.layoutSubviews()
            } else {
                ins_layoutSubviews()
            }
        }
    }
    
    extension UICollectionView {
        @objc fileprivate func ins_layoutSubviews() {
            if dataSource == nil {
                super.layoutSubviews()
            } else {
                ins_layoutSubviews()
            }
        }
    }
    

    Dispatch once extension:

    extension DispatchQueue {
    
        private static var _onceTracker = [String]()
    
        /**
         Executes a block of code, associated with a unique token, only once.  The code is thread safe and will
         only execute the code once even in the presence of multithreaded calls.
    
         - parameter token: A unique reverse DNS style name such as com.vectorform. or a GUID
         - parameter block: Block to execute once
         */
        public class func once(token: String, block: (Void) -> Void) {
            objc_sync_enter(self); defer { objc_sync_exit(self) }
    
            if _onceTracker.contains(token) {
                return
            }
    
            _onceTracker.append(token)
            block()
        }
    }
    

    Swizzle extension:

    extension NSObject {
        @discardableResult
        class func swizzle(originalSelector: Selector, with selector: Selector) -> Bool {
    
            var originalMethod: Method?
            var swizzledMethod: Method?
    
            originalMethod = class_getInstanceMethod(self, originalSelector)
            swizzledMethod = class_getInstanceMethod(self, selector)
    
            if originalMethod != nil && swizzledMethod != nil {
                method_exchangeImplementations(originalMethod!, swizzledMethod!)
                return true
            }
            return false
        }
    }
    

提交回复
热议问题