使用多个scrollToRowAtIndexPath时,在UITableView和UICollectionView之间同步滚动:滞后

时间:2013-06-10 07:51:44

标签: ios uitableview uiscrollview scroll uicollectionview

我有一个带有collectionView(单个水平线)和下面的tableview的视图。两个视图在滚动时同步,因为它们以不同的方式显示相同的数据。事实上,如果你有这个应用程序,就像“幻想”的应用程序。

我设法使用UIScrollView Delegate方法同步两个视图

#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGPoint currentOffset = scrollView.contentOffset;
    NSLog(@"scrollViewDidScroll %@ - %@", NSStringFromCGPoint(scrollView.contentOffset), NSStringFromCGPoint(self.previousScrollOffset));
    switch (self.scrollAnimation) {
        case ScrollAnimationFromCollection:
        {
            if (currentOffset.x > self.previousScrollOffset.x && (self.scrollDirection == ScrollDirectionLeft || self.scrollDirection == ScrollDirectionNone))
            {
                // NSLog(@"Change to Right!");
                self.scrollDirection = ScrollDirectionRight;
                [self.alreadySelectedIndexPaths removeAllObjects];
            }
            else if (currentOffset.x <= self.previousScrollOffset.x && (self.scrollDirection == ScrollDirectionRight || self.scrollDirection == ScrollDirectionNone))
            {
                // NSLog(@"Change to Left!");
                self.scrollDirection = ScrollDirectionLeft;
                [self.alreadySelectedIndexPaths removeAllObjects];
            }
            [self moveTableView];
            break;
        }

        case ScrollAnimationFromTableView:
        {
            if (currentOffset.y - self.previousScrollOffset.y > 0 && self.scrollDirection == ScrollDirectionBottom)
            {
                self.scrollDirection = ScrollDirectionTop;
                [self.alreadySelectedIndexPaths removeAllObjects];
            }
            else if (currentOffset.y - self.previousScrollOffset.y <= 0 && self.scrollDirection == ScrollDirectionTop)
            {
                self.scrollDirection = ScrollDirectionBottom;
                [self.alreadySelectedIndexPaths removeAllObjects];
            }
            [self moveCollectionView];
            break;
        }

        default:
            break;
    }
    self.previousScrollOffset = currentOffset;
}

#pragma mark - Move actions
- (void)moveCollectionView
{
    NSIndexPath* currentIp = [[self.tableView indexPathsForVisibleRows] objectAtIndex:1];
    NSLog(@"currentIp %@", currentIp);
    if (![self.alreadySelectedIndexPaths containsObject:currentIp])
    {
        NSLog(@"Scroll to IndexPath centered!");
        [self.alreadySelectedIndexPaths addObject:currentIp];
        [self.collectionViewController.collectionView scrollToItemAtIndexPath:currentIp atScrollPosition:PSTCollectionViewScrollPositionCenteredHorizontally animated:YES];
    }
}

- (void)moveTableView
{
    NSIndexPath* currentIp = [self.collectionViewController.collectionView indexPathForItemAtPoint:[self.horizontalContainer convertPoint:self.horizontalContainer.center toView:self.collectionViewController.collectionView]];
    NSLog(@"currentIp %@", currentIp);
    if (![self.alreadySelectedIndexPaths containsObject:currentIp])
    {
        NSLog(@"Scroll to IndexPath At Top!");
        [self.alreadySelectedIndexPaths addObject:currentIp];
        self.tableView.decelerationRate = UIScrollViewDecelerationRateFast;
        [self.tableView scrollToRowAtIndexPath:currentIp atScrollPosition:UITableViewScrollPositionTop animated:YES];
    }
}

除了一件事之外,它的效果非常好:当我滚动集合视图时,由于动画(YES)参数,tableview需要一些时间来滚动。如果我使用scrollToRowAtIndexPath:没有动画,它就像一个魅力。但是一旦我使用动画标志,似乎(非常逻辑地)iOS将scrollToRowAtIndexPath排队并一次运行一次,导致动画滞后。

FWI,alreadySelectedIndexPaths包含我已经滚动的indexPath列表(我不知道这句话是否很清楚:D:D)

我查看是否有办法取消之前的订单,但除了cancelPreviousPerformRequestsWithTarget:取消最后一次performSelector调用时,它没有找到任何有用的东西。

你看到我怎样才能在这里获得流畅的动画?

向你求助。

2 个答案:

答案 0 :(得分:4)

每当滚动tableView或collectionView时,覆盖scrollviewDidScroll方法。

在这里,您可以获取正在滚动的视图的偏移量,并将其转换为另一个视图的偏移量。

然后你可以在另一个视图上设置这个偏移量。

无论如何在动画循环中调用委托方法都不需要设置偏移或任何内容的动画,因此只需设置偏移量就会使两个视图看起来都是动画的。

希望这有帮助。

答案 1 :(得分:1)

我有类似的问题。我的工作方式如下:

  1. 创建UITableView和UICollectionView的自定义子类
  2. 这两个子类均应符合public class SetupActivity extends AppCompatActivity { private CircleImageView setupImage; private Uri mainImageURI = null; private EditText setupName; private Button setupBtn; private ProgressBar setupProgress; private StorageReference storageReference; private FirebaseAuth firebaseAuth; private FirebaseFirestore firebaseFirestore; private String user_id; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_setup); Toolbar setupToolbar = findViewById(R.id.setupToolbar); setSupportActionBar(setupToolbar); getSupportActionBar().setTitle("Konto Optionen"); firebaseAuth = FirebaseAuth.getInstance(); storageReference = FirebaseStorage.getInstance().getReference(); firebaseFirestore = FirebaseFirestore.getInstance(); user_id = firebaseAuth.getCurrentUser().getUid(); setupImage = findViewById(R.id.setup_image); setupName = findViewById(R.id.setup_name); setupBtn = findViewById(R.id. setup_btn); setupProgress = findViewById(R.id.setup_progress); setupProgress.setVisibility(View.VISIBLE); setupBtn.setEnabled(false); firebaseFirestore.collection("Users").document(user_id).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() { @Override public void onComplete(@NonNull Task<DocumentSnapshot> task) { if (task.isSuccessful()) { if (task.getResult().exists()) { String name = task.getResult().getString("name"); String image = task.getResult().getString("image"); mainImageURI = Uri.parse(image); setupName.setText(name); RequestOptions placeholderRequest = new RequestOptions(); placeholderRequest.placeholder(R.drawable.defaultpic); Glide.with(SetupActivity.this).setDefaultRequestOptions(placeholderRequest).load(image).into(setupImage); } } else { String error = task.getException().getMessage(); Toast.makeText(SetupActivity.this, "(FIRESTORE Retrieve Error) :" + error, Toast.LENGTH_LONG).show(); } setupProgress.setVisibility(View.INVISIBLE); setupBtn.setEnabled(true); } }); setupBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final String user_name = setupName.getText().toString(); if(!TextUtils.isEmpty(user_name) && mainImageURI != null){ user_id = firebaseAuth.getCurrentUser().getUid(); setupProgress.setVisibility(View.VISIBLE); StorageReference image_path = storageReference.child("profile_images").child(user_id + ".jpg"); image_path.putFile(mainImageURI).addOnCompleteListener(new OnCompleteListener<UploadTask.TaskSnapshot>() { @Override public void onComplete(@NonNull Task<UploadTask.TaskSnapshot> task) { if(task.isSuccessful()){ Task<Uri> download_uri = task.getResult().getStorage().getDownloadUrl(); Map<String, String> userMap = new HashMap<>(); userMap.put("name", user_name); userMap.put("image", mainImageURI.toString()); firebaseFirestore.collection("Users").document(user_id).set(userMap).addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { if(task.isSuccessful()){ Toast.makeText(SetupActivity.this, "Die Einstellungen wurden übernommen.",Toast.LENGTH_LONG).show(); Intent mainIntent = new Intent(SetupActivity.this,MainActivity.class); startActivity(mainIntent); }else{ String error = task.getException().getMessage(); Toast.makeText(SetupActivity.this, "(FIRESTORE Error) :" + error,Toast.LENGTH_LONG).show(); } setupProgress.setVisibility(View.INVISIBLE); } }); }else{ String error = task.getException().getMessage(); Toast.makeText(SetupActivity.this,"IMAGE Error : " + error, Toast.LENGTH_LONG).show(); setupProgress.setVisibility(View.INVISIBLE); } } }); }else{ Toast.makeText(SetupActivity.this,"Bitte setze ein Bild und einen Benutzernamen ein!" , Toast.LENGTH_LONG).show(); } } }); setupImage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if(ContextCompat.checkSelfPermission(SetupActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { Toast.makeText(SetupActivity.this, "Zugang verweigert",Toast.LENGTH_LONG).show(); ActivityCompat.requestPermissions(SetupActivity.this, new String []{Manifest.permission.READ_EXTERNAL_STORAGE},1); }else{ BringImagePicker(); } }else{ BringImagePicker(); } } }); } private void BringImagePicker() { CropImage.activity() .setGuidelines(CropImageView.Guidelines.ON) .setAspectRatio(1,1) .start(SetupActivity.this); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) { CropImage.ActivityResult result = CropImage.getActivityResult(data); if (resultCode == RESULT_OK) { mainImageURI = result.getUri(); setupImage.setImageURI(mainImageURI); } else if (resultCode == CropImage.CROP_IMAGE_ACTIVITY_RESULT_ERROR_CODE) { Exception error = result.getError(); } } } } 委托
  3. 在这两个类中,都覆盖UIGestureRecognizer方法
gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:)
  1. 您的表视图和集合视图必须具有相同的超级视图。将两个手势识别器添加到您的超级视图
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }

希望这会有所帮助!