2017-09-15 52 views
5

Tôi đang cố gắng xây dựng bộ điều khiển vùng chứa phân chia phức tạp để tạo điều kiện cho hai vùng chứa chiều cao thay đổi, mỗi bộ chứa có bộ điều khiển chế độ xem lồng nhau của riêng chúng. Có một cử chỉ pan toàn cầu trên bộ điều khiển chính cho phép người dùng kéo bất kỳ vị trí nào trong vùng chứa chế độ xem và trượt "dải phân cách" giữa các chế độ xem lên và xuống. Nó cũng có một số logic thông minh vị trí ngưỡng phát hiện rằng sẽ mở rộng hoặc view (hoặc thiết lập lại vị trí chia):Tắt cử chỉ di chuyển vùng chứa của phụ huynh để lồng vào UICollectionView

         

này hoạt động tốt. Ngoài ra còn có rất nhiều mã để xây dựng cái này, mà tôi rất vui được chia sẻ, nhưng tôi không nghĩ nó có liên quan, vì vậy tôi sẽ bỏ qua nó trong lúc này.

Tôi bây giờ cố gắng để làm phức tạp mọi thứ bằng cách thêm một cái nhìn bộ sưu tập để quan điểm đáy:

tôi đã có thể làm việc nó ra để tôi có thể di chuyển các phân chia xem với một cử chỉ pan quyết định và cuộn chế độ xem bộ sưu tập bằng cách vuốt nhanh ngón tay (cử chỉ vuốt, tôi cho rằng đó là?), nhưng đây thực sự là trải nghiệm phụ: bạn không thể xoay chế độ xem và cuộn bộ sưu tập xem cùng một lúc và hy vọng người dùng thường xuyên lặp lại các cử chỉ tương tự, nhưng khác nhau để kiểm soát chế độ xem là quá khó đối với một tương tác. Để cố gắng giải quyết vấn đề này, tôi đã thử một số giải pháp đại biểu/giao thức trong đó tôi phát hiện vị trí của dải phân cách trong chế độ xem chia tách và bật/tắt canCancelTouchesInView và/hoặc isUserInteractionEnable trên chế độ xem bộ sưu tập chế độ xem được mở rộng hoàn toàn. Điều này làm việc đến một điểm, nhưng không phải trong hai trường hợp sau:

  1. Khi chia chia xem ở vị trí mặc định, nếu người dùng mở rộng đến nơi xem dưới cùng được mở rộng hoàn toàn, hãy tiếp tục mở rộng , chế độ xem bộ sưu tập sẽ bắt đầu cuộn cho đến khi cử chỉ kết thúc.
  2. Khi bộ chia chế độ xem phân chia ở trên cùng (chế độ xem vùng chứa dưới cùng được mở rộng hoàn toàn) và chế độ xem bộ sưu tập là không phải là ở trên cùng, nếu người dùng kéo xuống, chế độ xem bộ sưu tập sẽ cuộn thay vì bộ chia chia xem , cho đến khi chế độ xem bộ sưu tập đạt đến vị trí trên cùng, tại thời điểm đó, chế độ xem chia tách sẽ trở về vị trí mặc định của nó.

Dưới đây là một phim hoạt hình minh họa hành vi này:

enter image description here

Vì điều này, tôi bắt đầu nghĩ cách duy nhất để giải quyết vấn đề là bằng cách tạo ra một phương pháp đại biểu về quan điểm phân chia cho biết chế độ xem bộ sưu tập khi chế độ xem dưới cùng ở độ cao tối đa, sau đó có thể chặn cử chỉ di chuyển của cha hoặc chuyển tiếp màn hình chạm vào chế độ xem bộ sưu tập thay thế? Nhưng, tôi không chắc chắn làm thế nào để làm điều đó. Nếu tôi đi đúng hướng với một giải pháp, thì câu hỏi của tôi đơn giản là: Làm cách nào để chuyển tiếp hoặc chuyển một cử chỉ pan sang chế độ xem bộ sưu tập và có chế độ xem bộ sưu tập tương tác giống như cách chạm vào bởi nó ở nơi đầu tiên? Tôi có thể làm điều gì đó với các phương pháp pointInside hoặc touches____ không?

Nếu tôi không thể thực hiện theo cách này, tôi có thể giải quyết vấn đề này bằng cách nào khác?


Cập nhật cho thợ săn tiền thưởng: Tôi đã có một số phân mảnh may mắn tạo ra một phương pháp đại biểu về quan điểm bộ sưu tập, và gọi đó là trên thùng phân chia nhằm thiết lập một tài sản shouldScroll, do đó tôi có sử dụng một số phương hướng và chảo thông tin định vị để xác định xem cuộn có cuộn hay không. sau đó tôi quay trở lại giá trị này trong phương pháp gestureRecognizer:shouldReceive touch: đại biểu UIGestureRecognizerDelegate 's:

// protocol delegate 
protocol GalleryCollectionViewDelegate { 
    var shouldScroll: Bool? { get } 
} 

// shouldScroll property 
private var _shouldScroll: Bool? = nil 
var shouldScroll: Bool { 
    get { 
     // Will attempt to retrieve delegate value, or self set value, or return false 
     return self.galleryDelegate?.shouldScroll ?? self._shouldScroll ?? false 
    } 
    set { 
     self._shouldScroll = newValue 
    } 
} 

// UIGestureRecognizerDelegate method 
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { 
    return shouldScroll 
} 

// ---------------- 
// Delegate property/getter called on the split view controller and the logic: 
var shouldScroll: Bool? { 
    get { 
     return panTarget != self 
    } 
} 

var panTarget: UIViewController! { 
    get { 
     // Use intelligent position detection to determine whether the pan should be 
     // captured by the containing splitview or the gallery's collectionview 
     switch (viewState.currentPosition, 
       viewState.pan?.directionTravelled, 
       galleryScene.galleryCollectionView.isScrolled) { 
     case (.top, .up?, _), (.top, .down?, true): return galleryScene 
     default: return self 
     } 
    } 
} 

này hoạt động OK khi bạn bắt đầu di chuyển, nhưng không hoạt động tốt khi di chuyển được kích hoạt trên quan điểm bộ sưu tập, bởi vì cuộn cử chỉ hầu như luôn luôn ghi đè cử chỉ pan. Tôi tự hỏi nếu tôi có thể dây một cái gì đó lên với gestureRecognizer:shouldRecognizeSimultaneouslyWith:, nhưng tôi không có được nêu ra.

+0

Bạn có thể chia sẻ mã của mình để kiểm tra hành vi này không? Trong một trong hai trường hợp bạn đã đề cập, có vẻ như collectionView sẽ tiêu thụ các sự kiện chạm cho đến khi bù ngang của nó đạt đến ranh giới của nó có vẻ khá đơn giản. – Lukas

+0

@Lukas Tôi có thể, nhưng tôi không chắc nó sẽ thực sự giúp giải quyết vấn đề - bởi vì rất nhiều được xây dựng bằng IB với các ràng buộc IB, và bởi vì bạn đã biết rằng tôi có phương pháp đại biểu phát hiện cháy chính xác cách bạn đã mô tả: "khi bù ngang đạt đến ranh giới trên của nó, collectionView sẽ tiêu thụ các sự kiện chạm." Tuy nhiên, nếu bạn muốn một số mã, tôi có thể thêm một số '¯ \ _ (ツ) _/¯' – brandonscript

Trả lời

2

Điều gì khiến cho chế độ xem con cho chế độ xem dưới thực sự chiếm toàn bộ màn hình và đặt nội dung của chế độ xem bộ sưu tậpInset.top thành chế độ xem trên cùng. Và sau đó thêm bộ điều khiển chế độ xem con khác ở phía trên chế độ xem dưới cùng. Sau đó, điều duy nhất bạn cần làm là đặt bộ điều khiển chế độ xem của cha mẹ là đại biểu để nghe bù cuộn cuộn của chế độ xem của chế độ xem dưới cùng và thay đổi vị trí của chế độ xem trên cùng. Không có công cụ nhận dạng cử chỉ phức tạp nào. Chỉ có một cái nhìn cuộn (xem bộ sưu tập)

enter image description here

Cập nhật: Hãy thử điều này !!

import Foundation 
import UIKit 

let topViewHeight: CGFloat = 250 

class SplitViewController: UIViewController, BottomViewControllerScrollDelegate { 

    let topViewController: TopViewController = TopViewController() 
    let bottomViewController: BottomViewController = BottomViewController() 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     automaticallyAdjustsScrollViewInsets = false 

     bottomViewController.delegate = self 
     addViewController(bottomViewController, frame: view.bounds, completion: nil) 
     addViewController(topViewController, frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: topViewHeight), completion: nil) 
    } 

    func bottomViewScrollViewDidScroll(_ scrollView: UIScrollView) { 
     print("\(scrollView.contentOffset.y)") 

     let offset = (scrollView.contentOffset.y + topViewHeight) 
     if offset < 0 { 
      topViewController.view.frame.origin.y = 0 
      topViewController.view.frame.size.height = topViewHeight - offset 
     } else { 
      topViewController.view.frame.origin.y = -(scrollView.contentOffset.y + topViewHeight) 
      topViewController.view.frame.size.height = topViewHeight 
     } 
    } 
} 

class TopViewController: UIViewController { 

    let label = UILabel() 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     automaticallyAdjustsScrollViewInsets = false 
     view.backgroundColor = UIColor.red 

     label.text = "Top View" 
     view.addSubview(label) 
    } 

    override func viewWillLayoutSubviews() { 
     super.viewWillLayoutSubviews() 
     label.sizeToFit() 
     label.center = view.center 
    } 
} 

protocol BottomViewControllerScrollDelegate: class { 
    func bottomViewScrollViewDidScroll(_ scrollView: UIScrollView) 
} 

class BottomViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout { 

    var collectionView: UICollectionView! 

    weak var delegate: BottomViewControllerScrollDelegate? 

    let cellPadding: CGFloat = 5 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     view.backgroundColor = UIColor.yellow 
     automaticallyAdjustsScrollViewInsets = false 

     let layout = UICollectionViewFlowLayout() 
     layout.minimumInteritemSpacing = cellPadding 
     layout.minimumLineSpacing = cellPadding 
     layout.scrollDirection = .vertical 
     layout.sectionInset = UIEdgeInsets(top: cellPadding, left: 0, bottom: cellPadding, right: 0) 

     collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout) 
     collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight] 
     collectionView.contentInset.top = topViewHeight 
     collectionView.scrollIndicatorInsets.top = topViewHeight 
     collectionView.alwaysBounceVertical = true 
     collectionView.backgroundColor = .clear 
     collectionView.dataSource = self 
     collectionView.delegate = self 
     collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: String(describing: UICollectionViewCell.self)) 
     view.addSubview(collectionView) 
    } 

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 
     return 30 
    } 

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 
     let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: UICollectionViewCell.self), for: indexPath) 
     cell.backgroundColor = UIColor.darkGray 
     return cell 
    } 

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 
     let width = floor((collectionView.frame.size.width - 2 * cellPadding)/3) 
     return CGSize(width: width, height: width) 
    } 

    func scrollViewDidScroll(_ scrollView: UIScrollView) { 
     delegate?.bottomViewScrollViewDidScroll(scrollView) 
    } 
} 

extension UIViewController { 

    func addViewController(_ viewController: UIViewController, frame: CGRect, completion: (()-> Void)?) { 
     viewController.willMove(toParentViewController: self) 
     viewController.beginAppearanceTransition(true, animated: false) 
     addChildViewController(viewController) 
     viewController.view.frame = frame 
     viewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight] 
     view.addSubview(viewController.view) 
     viewController.didMove(toParentViewController: self) 
     viewController.endAppearanceTransition() 
     completion?() 
    } 
} 
+0

Âm thanh như một ý tưởng đầy hứa hẹn ... nếu bạn có một đâm vào nó trong mã nó có thể có giá trị một tiền thưởng – brandonscript

3

Bạn không thể "tắt" cử chỉ, vì trình nhận dạng cử chỉ vẫn giữ nguyên đối tượng và view không thay đổi - đó là chế độ xem trình nhận dạng cử chỉ được đính kèm.

Tuy nhiên, không có gì ngăn bạn không cho người khác biết phải làm gì để phản ứng với cử chỉ. Chế độ xem bộ sưu tập là chế độ xem cuộn, vì vậy bạn biết cách nó được cuộn vào mọi lúc và có thể làm điều gì khác song song.

+0

Đó là một trong những nỗ lực trước đây của tôi - phát hiện cử chỉ và sử dụng nội dung của bộ sưu tập (xem cuộn) để xử lý . Tuy nhiên khi làm điều này, tôi hy sinh khả năng của chế độ xem bộ sưu tập để 'trả lại' và tương tác cảm thấy rất vô hồn. – brandonscript

1

Bạn sẽ có thể đạt được những gì bạn đang tìm kiếm bằng một chế độ xem bộ sưu tập bằng cách sử dụng UICollectionViewDelegateFlowLayout. Nếu bạn cần bất kỳ hành vi cuộn đặc biệt nào cho chế độ xem trên cùng của bạn như thị sai, bạn vẫn có thể đạt được điều đó trong một chế độ xem bộ sưu tập bằng cách triển khai đối tượng bố cục tùy chỉnh kế thừa từ UICollectionViewLayout.

Sử dụng cách tiếp cận UICollectionViewDelegateFlowLayout là nhiều hơn một chút đơn giản hơn so với việc thực hiện một cách bố trí tùy chỉnh, vì vậy nếu bạn muốn cho rằng một shot, hãy thử như sau:

  • Tạo điểm hàng đầu của bạn như một lớp con của UICollectionViewCell và đăng ký nó với chế độ xem bộ sưu tập của bạn.

  • Tạo "chia" xem bạn như là một lớp con của UICollectionViewCell và đăng ký nó với cái nhìn bộ sưu tập của bạn như một cái nhìn bổ sung sử dụng func register(_ viewClass: AnyClass?, forSupplementaryViewOfKind elementKind: String, withReuseIdentifier identifier: String)

  • Có điều khiển xem bộ sưu tập của bạn phù hợp với UICollectionViewDelegateFlowLayout, tạo ra một đối tượng bố trí như một ví dụ của UICollectionViewFlowLayout chỉ định bộ điều khiển xem bộ sưu tập của bạn làm đại biểu của cá thể bố cục luồng và bắt đầu chế độ xem bộ sưu tập của bạn với bố cục luồng.

  • Triển khai optional func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize trả lại kích thước mong muốn của từng chế độ xem khác nhau trong bộ điều khiển chế độ xem bộ sưu tập của bạn.

+0

Trong thực tế điều này nghe có vẻ như nó sẽ làm việc khá tốt, nhưng trong tình hình của tôi, tôi đang sử dụng một bộ điều khiển xem container cho bộ điều khiển phân chia - trên và dưới là cả hai bộ điều khiển xem độc lập. Tôi không hoàn toàn tự tin rằng tôi có thể tạo ra một flowlayout như thế này và sử dụng container ... – brandonscript

+0

Ok, trong trường hợp đó, tôi sẽ thêm một bộ nhận dạng cử chỉ pan vào khung nhìn của bộ điều khiển container. Sau đó, trong trình xử lý của nó thực hiện một trong hai điều sau: 1) nếu trạng thái của cử chỉ pan là '.changed' cập nhật mỗi khung nhìn của người xem dựa trên bản dịch của nó trong chế độ xem vùng chứa của bạn. 2) Nếu trạng thái của cử chỉ pan là '.ended', hiển thị hoặc ẩn chế độ xem bộ sưu tập của bạn dựa trên hướng xoay và bất kỳ ngưỡng dịch nào bạn có thể quyết định đặt. – nicksweet

Các vấn đề liên quan