exclusiveTouch ですclipsToBounds が好きです。isUserInteractionEnabledと友達で、よく喧嘩します!/// viewControllerから再帰的に画面遷移でtoViewControllerまで戻る fileprivate func recursivelyDismissViewController( _ viewController: UIViewController, toViewController: UIViewController, completion: @escaping (_ dismissedViewController: UIViewController) -> Void) { guard viewController != toViewController else { completion(viewController) return } guard toViewController != self.rootViewController else { completion(viewController) return } if let presentingViewController = viewController.presentingViewController { viewController.dismiss(animated: false, completion: { [weak self] in self?.recursivelyDismissViewController( presentingViewController, toViewController: toViewController, completion: completion) }) } else if viewController.navigationController?.viewControllers.contains(toViewController) ?? false { viewController.navigationController? ._transition_router_popToViewController( toViewController, animated: false) { completion(toViewController) } } else if let nvc = viewController as? UINavigationController, nvc.viewControllers.contains(toViewController) { nvc._transition_router_popToViewController(toViewController, animated: false) { completion(toViewController) } } else { completion(viewController) } } (edited)enum ViewControllerInteractiveEvent { case popGestureCompleted case pushedOrPoped case cancelled } extension UIViewController { func hoge_receiveInteractiveAction(action: ((ViewControllerInteractiveEvent) -> Void)?) { if let coordinator = self.transitionCoordinator, coordinator.initiallyInteractive { coordinator.notifyWhenInteractionEnds({ context in if context.isCancelled { action?(.cancelled) } else { action?(.popGestureCompleted) } }) } else { action?(.pushedOrPoped) } } } (edited) @IBAction func onButton() { var message = "" for _ in 0..<1000 { message += "長い文言" } let alertController = UIAlertController(title: "aaa", message: message, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "button1", style: .default) { print($0) }) alertController.addAction(UIAlertAction(title: "button2", style: .default) { print($0) }) alertController.addAction(UIAlertAction(title: "cancel", style: .cancel) { print($0) }) present(alertController, animated: true, completion: nil) }iOS 10.3 to iOS 11.0 API Differences で検索してみたらそれっぽいのが。↑tabBarController.tabBar.isHidden = true tabBarController.additionalSafeAreaInsets.bottom += 1.0 tabBarController.additionalSafeAreaInsets.bottom -= 1.0 tabBarController.view.layoutIfNeeded() これやると、SafeAreaが再計算されます (edited)self.additionalSafeAreaInsets.bottom += 1.0 self.additionalSafeAreaInsets.bottom -= 1.0 wwautoresizingMask = [.flexibleHeight, .flexibleWidth]にして自己解決しました。やったぜUINavigationControllerのviewControllers(もしくはtopViewController or visibleViewController)の変化をKVO(UINavigationControllerDelegate以外)で検出したいと思っているのですが、可能かどうかご存じの方いらっしゃいますでしょうか? 以下のコードで試したのですが、push / popしても何も反応ありませんでした。 class CustomNavigationController: UINavigationController { var token: NSKeyValueObservation? override func viewDidLoad() { super.viewDidLoad() token = self.observe(\.viewControllers) { observed, change in print(observed) print(change) } } } 根源的にやりたいことは次のようなものです: status barのappearanceの変化のために、childViewControllerForStatusBarHiddenをoverrideし、topViewControllerを返すようにしています。 class CustomNavigationController: UINavigationController { override var childViewControllerForStatusBarHidden: UIViewController? { return self.topViewController } } このメソッドのドキュメントを見ると、 If you change the return value from this method, call the setNeedsStatusBarAppearanceUpdate() method. とあり、topViewControllerが変わった段階で setNeedsStatusBarAppearanceUpdateをcallする必要があるので、変化を検出したいです。 (edited)"UINavigationControllerDidShowViewControllerNotification" という通知を監視すると要求は満たせると思います。問題はUndocumentedなことですけど。Plain Style unsupported in a Navigation Item warningについて質問です IB上でUIBarButtonItemのstyleをPlainにすると警告が表示されてしまいますが、これはどう対処すべきでしょうか。 ドキュメントを参照すると、 https://developer.apple.com/documentation/uikit/uibarbuttonitemstyle
|[A]-(>=0@1000)-[B]| |[A]-(>=0@1000)-[C]| |[A]-(0@750)-| を A.right = min(B.left, C.left) とかで表現したいっていう話ですが、 [A]-(>=0@1000,0@750)-[B] [A]-(>=0@1000,0@750)-[C] でも同じ事実現できるので、それなら min 実装できそうだと思った次第。isTranslucent = false だったかな と思ったけどこれiOS 3からある。じゃあ違うかも。 (edited)isTransclucent をfalseにするので合ってそうです。barTintColor を設定するだけで、その色のアルファ値をかなり下げない限りはすりガラス効果がなくなるみたいですね。 気にしたことなかった。 https://qiita.com/yimajo/items/4781d5d2712e34677db2 (edited)// Cell, ざっくりとですが下のような構造をしてます ContentView |- label |- StackView |- categoryLabel |- View |- StackView class Cell: UITableViewCell { // cellForRowで呼ばれる func configure(_ item: Item) { categoryLabel.isHidden = item.category.isEmpty } }func configure() の最下部で print(Thread.isMainThread) して trueを吐いてるので全てメインスレッドで呼ばれてると思います。 (edited)class CollectionContainerView: UIView { @IBOutlet weak var collectionView: UICollectionView! override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { guard self.bounds.contains(point) else { return super.hitTest(point, with: event) } let updatedPoint = CGPoint(x: point.x, y: collectionView.frame.origin.y + collectionView.frame.height / 2) return super.hitTest(updatedPoint, with: event) } }UIPageViewControllerについて質問です。 UIPageViewControllerに生えているviewControllersプロパティは常に先頭が現在表示しているVCという認識であっていますか? 公式サンプルにはそれを前提としたようなコードがあるのですが、ドキュメントを見た限りではそのような記述はありません。。。 https://developer.apple.com/documentation/uikit/uipageviewcontroller/1614106-viewcontrollers
https://developer.apple.com/library/content/samplecode/ZoomingPDFViewer/Introduction/Intro.html サンプルにあったコード↓ let currentViewController = pageViewController.viewControllers![0] as UIViewController (edited)setViewControllersメソッドの説明 // For transition style 'UIPageViewControllerTransitionStylePageCurl', if 'doubleSided' is 'YES' and the spine location is not 'UIPageViewControllerSpineLocationMid', two view controllers must be included, as the latter view controller is used as the back.UIImageView の座標系上の座標を、表示されている画像上の座標(画像のFit/Fillなどの変形前の1ピクセルが1の座標系での座標)に変換する簡単な方法( CGAffineTransform を取得するなど)って何かありますか?自力実装すればもちろんできますが、 UIKit が提供する API で。↓が出てきたのでないのかなと思っています。 https://stackoverflow.com/questions/26348736/uiimageview-get-the-position-of-the-showing-imageextension UIImageView { public var viewToImage: CGAffineTransform? { guard let image = self.image else { return nil } switch contentMode { case .scaleAspectFit: let viewSize = frame.size let imageSize = image.size if viewSize.width / viewSize.height < imageSize.width / imageSize.height { return UIImageView.viewToImageFittingHorizontally(viewSize: viewSize, imageSize: imageSize) } else { return UIImageView.viewToImageFittingVertically(viewSize: viewSize, imageSize: imageSize) } case .scaleAspectFill: let viewSize = frame.size let imageSize = image.size if viewSize.width / viewSize.height < imageSize.width / imageSize.height { return UIImageView.viewToImageFittingVertically(viewSize: viewSize, imageSize: imageSize) } else { return UIImageView.viewToImageFittingHorizontally(viewSize: viewSize, imageSize: imageSize) } default: fatalError("Unsupported `contentMode`: \(contentMode)") } } private static func viewToImageFittingHorizontally(viewSize: CGSize, imageSize: CGSize) -> CGAffineTransform { let scale = imageSize.width / viewSize.width let offsetY = (imageSize.height - viewSize.height * scale) / 2 return CGAffineTransform(a: scale, b: 0, c: 0, d: scale, tx: 0, ty: offsetY) } private static func viewToImageFittingVertically(viewSize: CGSize, imageSize: CGSize) -> CGAffineTransform { let scale = imageSize.height / viewSize.height let offsetX = (imageSize.width - viewSize.width * scale) / 2 return CGAffineTransform(a: scale, b: 0, c: 0, d: scale, tx: offsetX, ty: 0) } }CGAffineTransform で得ます。 .scallAspectFit と .scallAspectFill にしか対応してませんが。CGAffineTransform がだと独自クラスにしなくても単に重ねるとかやりやすいかなと。viewToImage じゃなくて imageToView がほしいか( viewToImage から逆行列計算すればいいけど)。258(USキーボードから切り開ける場合。日本語のフリック入力キーボードの場合は216)のとき、同様に通知されないけどこれは見た目の高さは変わらないので実質的に問題にならない。@IBAction func toggleInputView(_ sender: Any) { let wasFirstResponder = textField.isFirstResponder if wasFirstResponder { textField.resignFirstResponder() } if let _ = textField.inputView { textField.inputView = nil } else { let myInputView = UIView(frame: view.bounds) myInputView.frame.size.height = 275 myInputView.backgroundColor = .blue textField.inputView = myInputView } textField.reloadInputViews() if wasFirstResponder { textField.becomeFirstResponder() } } こんな感じで、resignFirstResponder()を挟む。それだけだとキーボードが閉じてしまうのでbecomeFirstResponder()も呼ぶ。同じメソッド内で呼ぶぶんにはRunLoopが回らないので最終的に何ごともなかったようにキーボードはそのまま(変換中の日本語入力などは確定されてしまう)。scrollView.refreshControl = refreshControlのプロパティを使ってUIRefreshControlを設定すると、5秒目からの挙動のようにスクロールがガクガクする。 ^ は単調な上下の移動を繰り返してるだけなのです。ガクガクしてるのはUIRefreshControlが悪さをしている。 ワークアラウンドとしてiOS 9以前のようにaddSubview(refreshControl)すると発生しない。UIView.animate(withDuration: 1) { [weak self] in self?.visualEffectView.effect = UIBlurEffect(style: .light) } visualEffectView.layer.speed = 0 ^ 時間を止めてvisualEffectView.layer.timeOffset = TimeInterval(slider.value) (edited)visualEffectView.layer.timeOffset = TimeInterval(slider.value) timeOffsetを使って任意の瞬間にできる。visualEffectView.effect は nilとそれ以外の値でアニメーションするということ、visualEffectView.layer.speed = 0とtimeOffsetのテクニックですね。view.alpha = alpha; alpha == view.alpha が偽になる可能性があるということかな?UIImage(named:) 使うと思うんですけど、このコードはSwiftGenがデフォルトで生成するコードと本質的に同じなので、デバイスでも起こるんだったらそれなりに大変な問題じゃないかと思います。class ImageAssetTests: XCTestCase { func testLoadImages() { let allImages = Asset.allImages for imageAsset in allImages { XCTAssertNotNil(UIImage(asset: imageAsset), "\(imageAsset.name) should not be nil") } } } ^ こういうテストを書いてチェックする。label.text = "hoge" label.alpha = 0.5 この場合描画は2回呼ばれるのでしょうか…?var text: String { didSet { setNeedsDisplay() } } var subText: String { didSet { setNeedsDisplay() } } みたいな実装にすれば大丈夫でしょうか?(差分検知は別で行うとしてlet fullPath = UIBezierPath(rect: view.bounds) let tempPath = UIBezierPath(roundedRect: CGRect(x: 100, y: 100, width: 50, height: 50), cornerRadius: 16) fullPath.append(tempPath.reversing()) shapeLayer.path = fullPath.cgPathdebugWindow.makeKeyAndVisible() debugWindow.resignKey() ってやってるんだけど一瞬メインのwindowがkeyじゃなくなることに何か問題ないかなというのが不安func configureExternalDisplayAndShowWithContent(rootVC : UIViewController) { let screens = UIScreen.screens() // Configure the content only if a second screen is available. if screens.count > 1 { let externalScreen = screens[1] let screenBounds = externalScreen.bounds // Create and configure the window. self.externalWindow = UIWindow.init(frame: screenBounds) self.externalWindow!.windowLevel = UIWindowLevelNormal self.externalWindow!.screen = externalScreen // Install the root view controller self.externalWindow!.rootViewController = rootVC // Show the window, but do not make it key self.externalWindow!.hidden = false } else { // No external display available for configuration. } } UIWindowのドキュメントにこんな感じで書いてあったから、同じようにしたらいいのではないかしら。appendingPathComponent(_:) とかはダメですよね。 @available(*, unavailable, message: "Use lastPathComponent on URL instead.") public var lastPathComponent: String { fatalError("unavailable function can't be called") }-dump-ast で見ると、暗黙の変換が出てくるんだったような$ echo 'import UIKit; "test".size()'|swift -frontend -dump-ast -sdk `xcrun --show-sdk-path --sdk iphonesimulator` -target x86_64-apple-ios12.0 - (edited)-dump-astの方が判りやすいのか。 (source_file (import_decl trailing_semi 'UIKit') (top_level_code_decl (brace_stmt (call_expr type='CGSize' location=<stdin>:1:22 range=[<stdin>:1:15 - line:1:27] nothrow arg_labels= (dot_syntax_call_expr type='([NSAttributedString.Key : Any]?) -> CGSize' location=<stdin>:1:22 range=[<stdin>:1:15 - line:1:22] nothrow (declref_expr type='(NSString) -> ([NSAttributedString.Key : Any]?) -> CGSize' location=<stdin>:1:22 range=[<stdin>:1:22 - line:1:22] decl=UIKit.(file).NSString.size(withAttributes:) function_ref=single) (bridge_to_objc_expr implicit type='NSString' location=<stdin>:1:15 range=[<stdin>:1:15 - line:1:15] (string_literal_expr type='String' location=<stdin>:1:15 range=[<stdin>:1:15 - line:1:15] encoding=utf8 value="test" builtin_initializer=Swift.(file).String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) initializer=**NULL**))) (tuple_shuffle_expr implicit type='(withAttributes: [NSAttributedString.Key : Any]?)' location=<stdin>:1:26 range=[<stdin>:1:26 - line:1:27] tuple_to_tuple elements=[-3] variadic_sources=[] default_args_owner=UIKit.(file).NSString.size(withAttributes:) (tuple_expr type='()' location=<stdin>:1:26 range=[<stdin>:1:26 - line:1:27]))))))String/NSString 間でしかこの暗黙変換は起きないっぽい?let maskLayer1 = CALayer() let maskLayer2 = CALayer() let layer = CALayer() maskLayer2.mask = maskLayer1 layer.mask = maskLayer2 これなんか動かない気がする 2つのMaskの共通部分でマスクしたいが、片方のマスクだけ適用される。 ちなみにNimble_snapshotだとちゃんとマスクされた状態のスクショが生成されてた (edited)isPagingEnabled = true の時の1ページぶんの大きさは自身のboundsになります。isHiddenを切り替えるのは静的であるという扱い という感じで、動的な要素を排除し静的な定義のみで作られたビューはメンテナンス性が高いということを目指そうというものです。>= 11.4This NSLayoutConstraint is being configured with a constant that exceeds internal limits. A smaller value will be substituted, but this problem should be fixed. Break on BOOL _NSLayoutConstraintNumberExceedsLimit(void) to debug. This will be logged only once. This may break in the future. ^ AutoLayoutで遊んでたらこういうログを見た。 OS バージョンで閾値は異なるようです。collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath)
collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath) の実装が必要そうですが、それにしてもcolorの設定とか必要になるわけですね なんかこう、そういう大層なやつじゃなくて、一発でお手軽にcellをグレーアウトさせる方法はないのでしょうか? そういう場合はcellの選択に任せず、内部でUIButtonを使う…というのが定石になるのでしょうかselectedBackgroundView ですね。これはUITableViewCellの同名のプロパティと一緒で、ハイライトに使うものです。selectedBackgroundView はnilであり、 UIView() をセットする必要があるbringSubview(toFront:) するべきではないので、contentViewに背景色が必要な場合は selectedBackgroundView の利用は諦めるisHighlighted のdidSetをトリガーにして見た目を更新するcell.backgroundView でやれますねdidHighlightItemやcellのisHighlightをoverrideしてdidSetで色変える手法だとタップ時は見た目変わらなくてホールドしてやっと見た目変わるので、微妙だと悩んでいたらタイムリーな! 参考にさせてもらいます (edited)class ImageAssetTests: XCTestCase { func testLoadImages() { let allImages = Asset.allImages for imageAsset in allImages { XCTAssertNotNil(UIImage(asset: imageAsset), "\(imageAsset.name) should not be nil") } } } 理由は xcodebuild build-for-testing -workspace $BITRISE_WORKSPACE -scheme $BITRISE_SCHEME -destination 'generic/platform=iOS Simulator' ENABLE_TESTABILITY=YES ^ のようにビルドして ... xcodebuild test-without-building -workspace $BITRISE_WORKSPACE -scheme $BITRISE_SCHEME -destination 'name=iPhone 7 Plus,OS=10.3.1' -only-testing:FolioTests/ImageAssetTests ... のようにテストしてたけどこれだと再現しなくて、xcodebuild build-for-testing -workspace $BITRISE_WORKSPACE -scheme $BITRISE_SCHEME -destination 'name=iPhone 7,OS=10.3.1' ENABLE_TESTABILITY=YES ^ のようにビルド時のDestinationからiPhone 7/OS 10を指定しないとダメだった。 謎の現象。Optional(<__NSSingleObjectArrayI 0x2801f0a10>({})) AVCaptureSession Optional(<__NSSingleObjectArrayI 0x282f00170>({}))Optional({ CVImageBufferColorPrimaries = "ITU_R_709_2"; CVImageBufferTransferFunction = "ITU_R_709_2"; CVImageBufferYCbCrMatrix = "ITU_R_601_4"; MetadataDictionary = { ExposureTime = "0.033328"; NormalizedSNR = "22.21105655007992"; SNR = "28.23165646335955"; SensorID = 852; }; })import UIKit import PlaygroundSupport class ViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) view.backgroundColor = .white print(view) let box = UIView() view.addSubview(box) box.bounds = CGRect(x: 0, y: 0, width: view.bounds.width - 40, height: view.bounds.width - 40) box.center = CGPoint(x: view.bounds.width / 2, y: view.bounds.width / 2 + 100) box.backgroundColor = .red box.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) DispatchQueue.main.asyncAfter(deadline: .now() + 3) { UIView.animate( withDuration: 3, animations: { box.transform = CGAffineTransform.identity .translatedBy(x: 0, y: 200) }) } } } let vc = ViewController() vc.view.bounds = CGRect(x: 0, y: 0, width: 375, height: 667) PlaygroundPage.current.liveView = vc (edited)#0 0x00000001cc026b14 in -[UIView(Rendering) setBackgroundColor:] () #1 0x000000019f62629c in -[NSObject(NSKeyValueCoding) setValue:forKey:] () #2 0x00000001cc038fcc in -[UIView(CALayerDelegate) setValue:forKey:] () #3 0x00000001cb83c71c in -[NSObject(UIIBPrivate) _uikit_applyValueFromTraitStorage:forKeyPath:] () #4 0x00000001cb83dd08 in -[_UIColorAttributeTraitStorage applyRecordsMatchingTraitCollection:] () #5 0x00000001cb83cf0c in -[NSObject(_UITraitStorageAccessors) _applyTraitStorageRecordsForTraitCollection:] () #6 0x00000001cc01095c in -[UIView _traitCollectionDidChangeInternal:] () #7 0x00000001cc010c7c in -[UIView _wrappedProcessTraitCollectionDidChange:forceNotification:] () #8 0x00000001cc010f70 in -[UIView _processDidChangeRecursivelyFromOldTraits:toCurrentTraits:forceNotification:] () #9 0x00000001cc03970c in -[UIView(CALayerDelegate) layoutSublayersOfLayer:] () #10 0x00000001a3275b7c in -[CALayer layoutSublayers] () #11 0x00000001a327ab34 in CA::Layer::layout_if_needed(CA::Transaction*) () #12 0x00000001a31d9598 in CA::Context::commit_transaction(CA::Transaction*) () #13 0x00000001a3207ec8 in CA::Transaction::commit() () #14 0x00000001cbbaaf78 in __34-[UIApplication _firstCommitBlock]_block_invoke_2 () #15 0x000000019ec09f30 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ () #16 0x000000019ec09830 in __CFRunLoopDoBlocks () #17 0x000000019ec04824 in __CFRunLoopRun () #18 0x000000019ec040e0 in CFRunLoopRunSpecific () #19 0x00000001a0e7d584 in GSEventRunModal () #20 0x00000001cbb90c00 in UIApplicationMain () #21 0x0000000104169b30 in main at /Users/omochi/github/omochi/NamedColorBug/NamedColorBug/AppDelegate.swift:12 <view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="AnA-HA-I0a"> <rect key="frame" x="16" y="20" width="240" height="128"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <color key="backgroundColor" name="redColor"/> </view> <resources> <namedColor name="redColor"> <color red="1" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> </namedColor> </resources>draw(_ rect:) をオーバーライドして CGContext 経由で描画しようとしてるんですが、ビューの高さを増やすと描画されなくなる現象にハマってます。何かわかる方いますか?↓の height を増やすと描画されなくなります。 import UIKit // `drawRect` fails when `height` is increased private let height: CGFloat = 2000 class ViewController: UIViewController { @IBOutlet private var scrollView: UIScrollView! private var heightConstraint: NSLayoutConstraint! override func viewDidLoad() { super.viewDidLoad() let drawRectView: DrawRectView = DrawRectView() drawRectView.translatesAutoresizingMaskIntoConstraints = false scrollView.addSubview(drawRectView) heightConstraint = drawRectView.heightAnchor.constraint(equalToConstant: height) NSLayoutConstraint.activate([ drawRectView.topAnchor.constraint(equalTo: scrollView.topAnchor), drawRectView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor), drawRectView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor), drawRectView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor), drawRectView.widthAnchor.constraint(equalTo: scrollView.widthAnchor), heightConstraint, ]) } @IBAction func pressButton(_ sender: UIButton) { // this does not make `drawRect` fail heightConstraint.constant += 1000 } } class DrawRectView: UIView { override func draw(_ rect: CGRect) { let context: CGContext = UIGraphicsGetCurrentContext()! print("\(#file) \(#line): frame=\(frame)") context.setLineWidth(6.0) context.setStrokeColor(UIColor.red.cgColor) context.move(to: .zero) context.addLine(to: CGPoint(x: frame.size.width, y: frame.size.height)) context.strokePath() } }let drawRectView: DrawRectView = DrawRectView() drawRectView.contentMode = .redraw (edited)isActive = true は忘れてハマりがち・・・。height 増やしたら動かなくない?hieght を増やして実行すると起こるdraw(_ rect:) の結果が描画されなくなる height は環境によって異なって、シミュレーターだと機種によって 5000 - 9000 くらいの範囲で起こったけど、hieght = 8192 だと描画されるけど height = 8193 だと描画されなくなる・・・。 (edited)height の値を変えてビルドして実行heightConstraint.constant += 100001000 ずつ 10 回足せば OK だけど 10000 足すと描画されなくなった。let height = 40000 でビルドして実行しても赤線が描画されてるってこと??400_000 とか 4_000_000 はどうでしょう?2019-03-05 17:11:09.118307+0900 DrawRectFailureSample[10843:3204174] This NSLayoutConstraint is being configured with a constant that exceeds internal limits. A smaller value will be substituted, but this problem should be fixed. Break on BOOL _NSLayoutConstraintNumberExceedsLimit(void) to debug. This will be logged only once. This may break in the future. 2019-03-05 17:11:09.136346+0900 DrawRectFailureSample[10843:3204174] -[<CALayer: 0x282d91240> display]: Ignoring bogus layer size (1024.000000, 2777777.000000), contentsScale 2.000000, backing store size (2048.000000, 5555554.000000)height を増やすと環境ごとに異なるある値を超えると描画されなくなって真っ白になる。let height = 8193 で起こるpressButton に scrollView.subviews[0].setNeedsDisplay() を追加して、ボタンを押すと、こちらでも 10000 前後で真っ白になる事象が発生しました。 iPhone XR 12.1 シミュレータpressButton で発生しないことがあったのは setNeedsDisplay してないからな可能性がありますね。if #available() を書かないといけないです。そうすると結局2つのコードベースをメンテするのであまり嬉しくない。(そうは言ってもクラスが違うだけの同じコードになるから格段に楽ではある) 今考えているのはクラスにPrefixを付けて、それで書くようにしてもらって、iOS 13なら標準のAPIを、iOS 12以下ならライブラリのAPIにフォールバックする、という風にしようかと思いますがあまり気に入ってないのでlet itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) let item = NSCollectionLayoutItem(layoutSize: itemSize) let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(44)) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) let section = NSCollectionLayoutSection(group: group) let layout = UICollectionViewCompositionalLayout(section: section) let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) ... ^ これでiOS 13でも12以前でもうまく動く(iOS 13では標準のAPIを使う)。Xcode 10ならうまく動きそう。Xcode 11でビルドしようとするとダメ。 現在は妥協して以下のように書くようにしようかと考えています。 (edited)let itemSize = IBPNSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) let item = IBPNSCollectionLayoutItem(layoutSize: itemSize) let groupSize = IBPNSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(44)) let group = IBPNSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) let section = IBPNSCollectionLayoutSection(group: group) let layout = IBPUICollectionViewCompositionalLayout(section: section) let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) ...
IBPプレフィックスをつける。美しくないし、あまり理解してもらえない気がする。#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_OS_13_0 typedef IBPUICollectionViewCompositionalLayout UICollectionViewCompositionalLayout #endif とやっておけば、ビルドが12以下を含む場合だけ、UICollectionViewCompositionalLayoutが使えて、12以下を切ったら、UICollectionViewCompositionalLayoutがUIKitを参照する、 というふうにできる気がしますがNS_SWIFT_NAMEを使うとか?let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) ... 困っているのは違う名前にするのであればいいんですけど、同じ名前だと^ のようなコードに絶対 #if available の分岐を入れないといけないんですよね。 理想はXcode 11でビルドしつつ、下のOSには互換ライブラリが動く、なんですけどなんか手段ないですかねえ?if #availableを使うしかないのでは。IBPプレフィクスが付いたObjective-Cの実装が、内部でOSバージョンをチェックして、本来の型のインスタンスを返すとか? (edited)typealias NSCollectionLayoutSize = IBP.IBPNSCollectionLayoutSize としてから使ってね、で、衝突は回避できそうですけど (edited)NS_SWIFT_NAMEを使って、Objective-C側をiOS 13の型に見せる方法を試してみたけど、NS_SWIFT_NAMEが@protocolに対応してなくてダメだった。Swift側でtypealias使った方が良さそう。 (edited)NS_SWIFT_NAMEで型名をiOS 13の型名に変えた場合、メソッドの実装が2つ(iOS 13とIBP)ある様にSwiftからは見えてしまい、Ambiguous use of 'fractionalWidth'みたいなエラーが沢山出てしまった。Objective-Cで型名の重複が起きた時と同じ状態なのかも。 (edited)fractionalWidth と書いていても fractionalWidthDimension も見つけてしまってエラーになる、ということか。/Volumes/Macintosh HD/Users/norio/github/IBPCollectionViewCompositionalLayout/Example/Example/Samples from Apple/Basics View Controllers/SectionDecorationViewController.swift:30:64: error: ambiguous use of 'fractionalWidth' let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), ^ UIKit.NSCollectionLayoutDimension:4:21: note: found this candidate open class func fractionalWidth(_ fractionalWidth: CGFloat) -> Self ^ IBPCollectionViewCompositionalLayout.NSCollectionLayoutDimension:3:21: note: found this candidate open class func fractionalWidth(_ fractionalWidth: CGFloat) -> Self ^
NS_SWIFT_NAMEが漏れてたのでちょっと修正。 (edited)NS_SWIFT_NAMEを試してダメだった記録としてcommitを残しておきます。 https://github.com/norio-nomura/IBPCollectionViewCompositionalLayout/commit/6a905437b681fba42c06d707b85a655033cfe0d9 (edited)6a905437b681fba42c06d707b85a655033cfe0d9 の環境で起こるってことですか?それとも現在のmasterで起こります?私の環境で起こってないのでちょっと詳しく教えてください @norio_nomura public typealias NSCollectionLayoutSize = IBPNSCollectionLayoutSize public typealias NSCollectionLayoutItem = IBPNSCollectionLayoutItem ... ^ このようなtypealiasで回避できたので、クロージャとEnumに問題があるんですけどなんとかなりそうです。 あとはランタイムでちゃんとスワップ できるのかどうかをトライしてみます。Cannot convert value of type '(Int, NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection?' (aka '(Int, IBPNSCollectionLayoutEnvironment) -> Optional<IBPNSCollectionLayoutSection>') to expected argument type 'IBPUICollectionViewCompositionalLayoutSectionProvider' (aka '(Int, NSCollectionLayoutEnvironment) -> Optional<NSCollectionLayoutSection>')masterは問題ないです。紛らわしくてごめんなさい IBPCollectionViewCompositionalLayoutInteroperability.swift を自分でプロジェクトにコピーしてもらう、ということにしました。@implementation IBPUICollectionViewCompositionalLayout - (instancetype)initWithSection:(IBPNSCollectionLayoutSection *)section { if (@available(iOS 13, *)) { #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 return [[UICollectionViewCompositionalLayout alloc] initWithSection:section]; #else return nil; #endif } else { IBPUICollectionViewCompositionalLayoutConfiguration *configuration = [[IBPUICollectionViewCompositionalLayoutConfiguration alloc] init]; return [self initWithSection:section configuration:configuration]; } } イニシャライザで分岐してiOS 13以上と以下で違うインスタンスを返すというテクニックとも言えない力技で、特にRuntime APIを使う必要もなかった。if (@available(iOS 13, *))と#if __IPHONE_OS_VERSION_MAX_ALLOWEDの順序だと、Xcode 10でビルドしてあるとiOS 13で動かないのでは? (edited)__IPHONE_OS_VERSION_MAX_ALLOWED がコンパイル時の分岐で、 @available が実行時の分岐だから、包み方を入れ替えるだけで良い? (edited)+ (instancetype)layoutAnchorWithEdges:(NSDirectionalRectEdge)edges { if (@available(iOS 13, *)) { return [NSClassFromString(@"NSCollectionLayoutAnchor") layoutAnchorWithEdges:edges]; } else { return [[self alloc] initWithEdges:edges]; } } まあこれで良いんじゃないかな? #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 はXcodeがundefined symbolと言ってくるので仕方なくつけたもので、こっちの方がシンプルだし。NSDiffableDataSourceSnapshot が最新のBetaでStructに変わった。 (edited)let item = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(widthDimension: NSCollectionLayoutDimension.fractionalWidth(1), heightDimension: NSCollectionLayoutDimension.estimated(44))) item.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10) let group = NSCollectionLayoutGroup.horizontal(layoutSize: NSCollectionLayoutSize(widthDimension: NSCollectionLayoutDimension.fractionalWidth(1), heightDimension: NSCollectionLayoutDimension.estimated(44)), subitems: [item]) let section = NSCollectionLayoutSection(group: group) let layout = UICollectionViewCompositionalLayout(section: section) Estimated heightとcontentInsetsを一緒に設定すると起こる。let item = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(44))) let group = NSCollectionLayoutGroup.vertical(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(44)), subitems: [item]) let section = NSCollectionLayoutSection(group: group) let layout = UICollectionViewCompositionalLayout(section: section)
https://github.com/kishikawakatsumi/CollectionViewCompositionalLayouts-InfiniteLoop (edited)systemBrown がiOSだけ無くなったのなんでなんだろう?Attempting to add contentInsets to an item's dimension along an estimated axis. This layout axis insets will be ignored (however, any non-estimated axis insets will be applied). Please consider using the item's edgeSpacing or the containing group's interItemSpacing instead.textView(_:shouldInteractWith:in:interaction:)でtrue返しとけば概ね期待通りになって、 シングルタップでsafari開く、長押しで選択肢出る、とかじゃなかったでしたっけ カスタムする場合はinteractionの種類に応じていろいろやるで良いのでは、副作用もOK(_:will〜) -> Bool みたいなのを見かけるイメージはあったけど、副作用を許す(_:should〜)も他にもけっこうあるのかな シグネチャだけじゃわからないdidInteractwith... にしておいて、自分で invokeDefaultTextInteractHandler みたいに明示的にデフォルト挙動を実行するようなAPIのほうが好きだな。UIKitだとあんまり見かけないけど。textView(_:shouldInteractWith:in:interaction:) で副作用許すなら許すで別にいいんですが、 そうすると textView(_:shouldInteractWith:in:) で明記されていたその記述が https://developer.apple.com/documentation/uikit/uitextviewdelegate/1649337-textview から削除された動機がわからないんですよね.multiplier が let なのでグギギってなったんすよねisAccessibilityElement = false とか無視される (edited)SCNMatrix, GLKMatrix, simd_double が全部違ったような気がする。 (edited).transform とかも、どっちからどっちへの変換なのかを名前で表してくれないと、いつも混乱する。 https://developer.apple.com/documentation/scenekit/scnnode/1407964-transform* で書きたくなる・・・。* 演算子に限ってはA * B って書いたら Aが左でBが右になるやつしか見たこと無いCould not load the "image" image referenced from a nib in the bundle with identifier "Bundle ID". のエラーが出てしまいます。 書くFrameworkにAssetをコピーすると解決できますが、ビルド成果物にアセットが重複してしまい、サイズが大きくなってしまうという状況ですUITabBarController を使うならタイトルを消すのは推奨されないと思っているんですがいかがでしょうか?UITabBarController 的に良くないんじゃないかと。var touchedLocation: CGPoint override var location: CGPoint{ get{ return touchedLocation } set(p){ touchedLocation = p } } たくさん質問に答えていただきありがとうございます (edited)override func で関数としてオーバライドしましょうoverride func で関数としてオーバライドしましょう override func で関数としてオーバライドしましょう var touch: MyTouch override func touches(for: UIView) -> Set<MyTouch>{ return [touch] }Set<UITouch>? にします。UIApplication.shared.sendEvent を呼べば良いです (edited)UICollectionViewCompositionalLayoutについて質問があります。NSCollectionLayoutSection の orthogonalScrollingBehavior を .none 以外にすると、NSCollectionLayoutSupplementaryItem の表示順序が狂ってしまうのですが、どなたかこの現象に出くわしたことはありませんか?常に最前面に表示したいのですが、もしご存じでしたら実装方法について教えていただけると幸いです。.none はデフォルト?.none "以外" かlayer.zPositionで回避できませんか? ↑のサンプル(iOS14)では最前面になりました layer.zPosition で無理矢理前に持ってくるのは盲点でした、、(zIndex とは...)UITextInputDelegate のプライベートメソッドに setInputMode: というのがあるので、UITextInputDelegate のオブジェクトを捕まえて値をセットしたら変わったりするんだったかな。。。?のような非公式の方法しかないんじゃないかなあ。。。